Волшебство javascript итераций

от автора

Почему это нужно знать?

Не знаю почему захотелось назвать так заголовок, но когда я делал свой проект и написал такой-то кусок кода, я встал в ступор. По началу я думал, что это баг javascript, потом думал, что это фича, шутка, нет конечно, я стал размышлять. После этого я стал больше понимать javascript, чисто логически, как-то осознал некоторые моменты, логику, да!

Задача

Допустим у нас появилась задача: клонировать все div-ы с классом newClass в объект с идентификатором inner
Алгоритм вроде бы простой: Пройтись циклом от начала и до конца, клонировать вставить… клонирование в javascript осуществляется, не мне вас учить, посредством метода-свойства cloneNode, но, а если мы не знаем, что существует cloneNode, или вообще просто решили идти другим путем.

Приступаем к оформлению примера

Если хотите читать дальше, то сейчас мы с вами создадим бесконечный цикл, итак:

1. Создайте страницу index.htm (не важно как назовете, можно не только — index) и вставьте туда такой макет

<!DOCTYPE html> <html> <head> 	<title>For</title> </head> <body> 	<div id='inner'></div> 	<div class='newClass'>1</div> 	<div class='newClass'>2</div> 	<div class='newClass'>3</div> 	<div class='newClass'>4</div> 	<div class='newClass'>5</div> 	<div class='newClass'>6</div>                   <script>               // здесь будем писать javascript          </script> </body> </html> 

Рассмотрим такой алгоритм:

1. Делаем цикла от 1 до (Число всех элементов div.newClass), ну правда в программировании индексация смещена к нулю… то есть 1 элемент — это нулевой… то есть

for(j = 0; j < (число всех элементов); j++) {}

2. В цилке нам нужно создавать div, в созданный div поместить внутренность div.newClass… конечно каждой итерации, свой div со своими внутренностями от div.newClass

3. Поместить созданный div в div с определенным идентификатором

<!DOCTYPE html> <html> <head> 	<title>HTML5 Drag-and-Drop</title>  </head> <body> 	<div id='inner'></div> 	<div class='newClass'>1</div> 	<div class='newClass'>2</div> 	<div class='newClass'>3</div> 	<div class='newClass'>4</div> 	<div class='newClass'>5</div> 	<div class='newClass'>6</div> 	 	 	<script>  		var $taskElement = document.getElementsByClassName('newClass');  		for (var $m = 0; $m < $taskElement.length; $m++){ 			pan = document.createElement('div'); 			pan.innerHTML = $taskElement[$m].innerHTML; 			document.getElementById('inner').appendChild(pan); 		} 		 	</script> </body> </html> 

Казалось бы прототип алгоритма работает, остается только тем div-ам прописать тот же класс, который у оригинальных объектов div, я имею ввиду — newClass

Кстати, если у кого не работает getElementsByClassName, то просто, где-нибудь, между тегами добавьте:

if (!document.getElementsByClassName) { 		document.getElementsByClassName = function(search) { 			var d = document, elements, pattern, i, results = []; 			if (d.querySelectorAll) { // IE8 			  return d.querySelectorAll("." + search); 			} 			if (d.evaluate) { // IE6, IE7 			  pattern = ".//*[contains(concat(' ', @class, ' '), ' " + search + " ')]"; 			  elements = d.evaluate(pattern, d, null, 0, null); 			  while ((i = elements.iterateNext())) { 				results.push(i); 			  } 			} else { 			  elements = d.getElementsByTagName("*"); 			  pattern = new RegExp("(^|\\s)" + search + "(\\s|$)"); 			  for (i = 0; i < elements.length; i++) { 				if ( pattern.test(elements[i].className) ) { 				  results.push(elements[i]); 				} 			  } 			} 			return results; 		  } 		} 

Продолжаем работать с прототипом

Если добавим такую строчку pan.setAttribute(«class», $taskElement[$m].className); — то есть как бы задаем нашему, созданному div, класс клонируемого элемента, вот тут-то и начинает бесконечная итерация… Цикл никогда не завершится

<!DOCTYPE html> <html> <head> 	<title>HTML5 Drag-and-Drop</title>  </head> <body> 	<div id='inner'></div> 	<div class='newClass'>1</div> 	<div class='newClass'>2</div> 	<div class='newClass'>3</div> 	<div class='newClass'>4</div> 	<div class='newClass'>5</div> 	<div class='newClass'>6</div> 	 	 	<script>  		var $taskElement = document.getElementsByClassName('newClass'); 		 		for (var $m = 0; $m < $taskElement.length; $m++){ 			pan = document.createElement('div'); 			pan.setAttribute("class", $taskElement[$m].className); // вот тут новая строчка 			pan.innerHTML = $taskElement[$m].innerHTML; 			document.getElementById('inner').appendChild(pan); 		} 		 	</script> </body> </html> 

Дело в том, что до момента цикла $taskElement = document.getElementsByClassName(‘newClass’); $taskElement.length = 6 {6 элементов div};
А значит и в for($m=0; $m < 6; $m++) {}
Но так как в теле цикла for мы создаем с вами div, да еще и пишем туда класс newClass, то коллекция $taskElement — автоматически увеличивается, так как при итерации создался новый идентичный элемент. То есть теперь for($m=0; $m < 7; $m++) {при первой итерации}. Откуда 7? А вот, с потолка взял, нет. В область определения for(..) тоже автоматически подставляется новое значение $taskElement.length, которое равно 7 и при каждом проходе цифра будет расти и расти, и расти, вплоть до бесконечности. Казалось бы есть решение, но не тут то было…

Отменяем бесконечную итерацию

А делается это легко и просто. Нам нужно в область определения поставить не динамическую коллекцию, а статическую константу подставить, вот смотрите…

{ // 1-случай
var k = $taskElement.length;
for ($m = 0; $m < k; $m++) {}
}

{ // 2-случай
for ($m = 0; $m < $taskElement.length; $m++) {}
}

1 и 2 случай — совершенно разные, но при это совершенно одинаковые, при определенных условиях!

<!DOCTYPE html> <html> <head> 	<title>HTML5 Drag-and-Drop</title>  </head> <body> 	<div id='inner'></div> 	<div class='newClass'>1</div> 	<div class='newClass'>2</div> 	<div class='newClass'>3</div> 	<div class='newClass'>4</div> 	<div class='newClass'>5</div> 	<div class='newClass'>6</div> 	 	 	<script>  		var $taskElement = document.getElementsByClassName('newClass'); 		 		var k = $taskElement.length; 		for (var $m = 0; $m < k; $m++){ 			pan = document.createElement('div'); 			pan.setAttribute("class", $taskElement[$m].className); 			pan.innerHTML = $taskElement[$m].innerHTML; 			document.getElementById('inner').appendChild(pan); 		} 		 	</script> </body> </html> 

Бесконечная итерация исчезла, но результат непредсказуемый!

Мы знаем, что в цикле создается div, но ему присваивается класс клонируемого объекта, коллекция newClass вновь обновляется и, только, что созданный элемент, встает впереди клонируемых, надеюсь я тут правильно рассуждаю. В общем и целом, эту задачу посредством такого алгоритма мне не удалось решить! Может это получится у вас!?

ссылка на оригинал статьи http://habrahabr.ru/post/212585/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *