...

пятница, 14 февраля 2014 г.

[Из песочницы] Волшебство 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), ну правда в программировании индексация смещена к нулю. Я имею ввиду, что первый элемент — это нулевой. Понятное дело, мы часто задаем такой цикл, во всяком случае, именно я, начинаю с j=0.



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 pan, taskElement = document.getElementsByClassName('newClass');
// почему taskElement? это не важно, взял за название общей коллекции, можете назвать и иначе, дело вкуса

for (var j = 0; j < taskElement.length; j++){
pan = document.createElement('div');
pan.innerHTML = taskElement[j].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[j].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 pan, taskElement = document.getElementsByClassName('newClass');

for (var j = 0; j < taskElement.length; j++){
pan = document.createElement('div');
pan.setAttribute("class", taskElement[j].className); // вот тут новая строчка
pan.innerHTML = taskElement[j].innerHTML;
document.getElementById('inner').appendChild(pan);
}

</script>
</body>
</html>


Дело в том, что до момента цикла taskElement = document.getElementsByClassName('newClass'); taskElement.length = 6 {6 элементов div};

А значит и в for(j=0; j < 6; j++) {}

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


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




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

{ // 1-случай

var k = taskElement.length;

for (j = 0; j < k; j++) {}

}


{ // 2-случай

for (j = 0; j < taskElement.length; j++) {}

}


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 pan, taskElement = document.getElementsByClassName('newClass');

var k = taskElement.length;
for (var j = 0; j < k; j++){
pan = document.createElement('div');
pan.setAttribute("class", taskElement[j].className);
pan.innerHTML = taskElement[j].innerHTML;
document.getElementById('inner').appendChild(pan);
}

</script>
</body>
</html>


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


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий