При поиске элементов в DOM надо помнить одну простую вещь: все списки узлов - не массивы Array, а специальные динамичные сущности DOMNodeList.
Какое это имеет значение? Разберем на примере.
Для этого сделаем функцию, чтобы удаляла все дочерние элементы узла. Она будет получать список узлов и по очереди удалять каждый, вот так:
function removeChildren(node) {
var children = node.childNodes
for(var i=0;i<children.length; i++) {
var child = children[i]
node.removeChild(child)
}
}
Проверим работу функции на тестовом списке.
Саша
Маша
Паша
Даша
С одного нажатия все узлы удалить не получится. Проверьте. Почему?
Причина в том, что childNodes всегда содержит упорядоченный список текущих детей.
При работе функции removeChildren сначала удаляется первый элемент списка, children[0] (Саша). Если посмотреть на children после удаления, то обнаружится интересная картина:
Было:
[0] Саша
[1] Маша
[2] Паша
[3] Даша
Сашу удалили. Стало:
[0] Маша
[1] Паша
[2] Даша
Список DOM-узлов по-прежнему начинается с 0, а функция продолжает удалять с children[1], так что Маша остается на месте:
[0] Маша
[1] Даша
Свойство length, как и сам список, отражает текущую картину вещей, поэтму цикл не идет дальше i=2 и заканчивает свою работу, удалив ровно половину элементов.
Как сделать правильно работающую функцию?
Вот один из вариантов:
function removeChildren(node) {
var children = node.childNodes
while(children.length) {
node.removeChild(children[0])
}
}
Общий принцип такой: если вам нужно производить изменения с набором DOM-узлов - имейте в виду, что все ваши изменения отражаются в DOM не после окончания работы скрипта, а сразу же.
У меня почему-то после первого нажатия никто не удаляется, после второго - удаляются Саша и Паша одновременно, потом Маша, потом Даша. Хоть в опере хоть в IE.
Извините, сам разобрался.
А мне не понятно, почему сначало ничего не происходит, а лишь потом удаляются Саша и Паша. Может быть потому, что между ними пустые узлы? Мне кажется это нужно уточнить.
Учитывая особенность поведения DOMNodeList, целесообразно удаление проводить в обратном порядке, например, так:
function removeChildren(node) {
var children = node.childNodes;
// так как последний элемент имеет индек, на 1 меньше, чем length, начинаем с i-1
// i>=0 - нулевой элемент списка существует, и нужно его удалить
for(var i=children.length-1; i>=0; i--) {
node.removeChild(children[i]);
}
}
Такой способ не приводит к пропуску элементов при итерации цикла, это позволяет проверять свойства каждого элемента, и удалять не все подряд:
function removeChildren(node) {
var children = node.childNodes
var Count = children.length
for(var i=Count;i>0; i--) {
var child = children[i]
node.removeChild(child)
}
}
function removeChildren(node) {
var children = node.childNodes
var length = children.length
for(var i=0; i < length; i++) {
node.removeChild(children[0])
}
}
я новичёк и не знаю тонкостей работы интерпретатора, но по опыту других языков это должен быть самый быстрый вариант, поскольку нет вызовов функций в условии цикла. правда этот вариант работоспособен при условии, что во время работы цикла не изменится размер DOMNodeList.
я только узнаю азы JS (сам я С++-сник), в данном случае почему нельзя использовать:
while(elem.firstChild != null) elem.removeChild(elem.firstChild);
?
Ты всё верно написал.
Так и надо делать.
Я сам С++-ник.
Это они не программеры, раз выдумывают рашпильные, полурабочие способа дял стандартных действий.
querySelectorAll и getElement(s)By(TagName, ClassName ...) возвращают разные списки.
В первом случае работать со списком можно как с массивом (т.е список не живой).
<!DOCTYPE html>
<div></div>
<div></div>
var par = document.body;
var list = document.querySelectorAll( 'body *' );
typeof list; // object
list.toString(); // [object NodeList]
console.dirxml( list ); // [<div></div>, <div></div>]
par.removeChild( list[0] );
console.dirxml( list ); // [<div></div>, <div></div>]
Это является ещё одним плюсом в пользу использования querySelector(All)
1
function removeChildren(node) {
2
var children = node.childNodes
3
4
for(var i=0;i<children.length; i++) {
5
var child = children[i]
6
7
node.removeChild(child)
8
}
9
}
удаляет сразу два элемента на второе выполнение цылка?
вернее не так,как он вообще удаляет когда остаётся два элемента.Насколько я понимаю он должен удалять 2 элемент массива а на третьем прохождении цикла элементов всего 0 и 1 ...как он удаляет эти элементы?
При первом нажатии(запуске цикла) удаляются пробелы. При втором нажатии удаляются 0 и 3 элементы, цикл завершается, поскольку i=2, а children.length=1 (маша-0, даша-1). При 3 выход из цикла уже при i=1, ну и дальше остается одна даша, для которой опять придется запускать цикл.
DOM представляет каждый элемент на странице (такие как заголовки, параграфы, изображения и т.д.) в виде объектов connections, которые можно манипулировать с помощью JavaScript.
а вообще, я предпочитаю насильно приводить списки узлов к типу Array, что позволяет использовать соответствующие методы для работы с массивами.
.ня
innertHTML = '' сделает Вам каку если понадобится где либо использовать удаленные потомки.
Сначала удаляются пустые текстовые элементы, если браузер их создал (в соответствии с написанным в ДОМ-1). Google Chrome, кстати.
У меня почему-то после первого нажатия никто не удаляется, после второго - удаляются Саша и Паша одновременно, потом Маша, потом Даша. Хоть в опере хоть в IE.
Извините, сам разобрался.
А мне не понятно, почему сначало ничего не происходит, а лишь потом удаляются Саша и Паша. Может быть потому, что между ними пустые узлы? Мне кажется это нужно уточнить.
Полностью согласен.
Если удалить пробелы и табы между еллементами
(ol и li) все работает правильно , т. е. удаляются без задержки .
А почему нельзя было просто вписать:
??
Работает же.
Не очень помню детали, но это вроде не всегда работает, бывало в узле оставалось не пусто, а nbsp; (в IE?)
Может подскажете точнее, если кто недавно на граблю наступал с таким способом ?
В IE можно было бы использовать, ан нет, глючит
Я думаю, что произойдет утечка памяти...
А из-за чего ? Т.е. Это предположение или утечка памяти в данном случае обоснована ?
пробывал в IE, Opera, Chrome и Firefox. Везде все сработало. Ну может он не такой надежный и все таки не всегда срабатывает. Сложно сказать.
Учитывая особенность поведения DOMNodeList, целесообразно удаление проводить в обратном порядке, например, так:
Такой способ не приводит к пропуску элементов при итерации цикла, это позволяет проверять свойства каждого элемента, и удалять не все подряд:
Да, это хороший способ, когда-то даже очень сильно выручил.
...хотя, кстати, и он не стопроцентный. Обработчик
DOMNodeRemoved
сможет поменять содержимое списка нод между итерациями цикла.function removeChildren(node) {
var children = node.childNodes
var Count = children.length
for(var i=Count;i>0; i--) {
var child = children[i]
node.removeChild(child)
}
}
А чем такое не тру?
Будет ошибка
var child = children[i]
ведь при первой итерации у тя i=children.length
я новичёк и не знаю тонкостей работы интерпретатора, но по опыту других языков это должен быть самый быстрый вариант, поскольку нет вызовов функций в условии цикла. правда этот вариант работоспособен при условии, что во время работы цикла не изменится размер DOMNodeList.
Спасибо, весь цикл статей был хорош, но тут я бы сделал вот так:
я только узнаю азы JS (сам я С++-сник), в данном случае почему нельзя использовать:
while(elem.firstChild != null) elem.removeChild(elem.firstChild);
?
Ты всё верно написал.
Так и надо делать.
Я сам С++-ник.
Это они не программеры, раз выдумывают рашпильные, полурабочие способа дял стандартных действий.
Посмотрите на него, какой умный.
А ничего, что способ из статьи более читаемый? И откуда Вы взяли, что это "полурабочий способ"?
Самый точный алгоритм. Именно так профессиональные программисты и пишут.
Можно использовать вот такую функцию для удаления всех дочерних элементов
function removeChildren(node) {
var ch = node.childNodes;
for(var i=0;i
Можно использовать вот такую функцию для удаления всех дочерних элементов
Короткая:
querySelectorAll и getElement(s)By(TagName, ClassName ...) возвращают разные списки.
В первом случае работать со списком можно как с массивом (т.е список не живой).
Это является ещё одним плюсом в пользу использования querySelector(All)
Немного промахнулась разделом =(
Мне иногда бывает удобно делать так:
На мой взгляд:
while(elem.firstChild != null) elem.removeChild(elem.firstChild);
наглядно и лаконично
Ещё лаконичнее, и ничуть не менее наглядно для человека, знающего JS.
Чуть быстрее и чуть меньше. Выигрыш за счёт минимального количества обращений к свойствам DOM (lastChild, removeChild по одному разу).
А можно написать так:
Работает быстрее из за однократного обращения к length
Скажите пожалуста почему данный код
удаляет сразу два элемента на второе выполнение цылка?
вернее не так,как он вообще удаляет когда остаётся два элемента.Насколько я понимаю он должен удалять 2 элемент массива а на третьем прохождении цикла элементов всего 0 и 1 ...как он удаляет эти элементы?
При первом нажатии(запуске цикла) удаляются пробелы. При втором нажатии удаляются 0 и 3 элементы, цикл завершается, поскольку i=2, а children.length=1 (маша-0, даша-1). При 3 выход из цикла уже при i=1, ну и дальше остается одна даша, для которой опять придется запускать цикл.
Очепятался не 0й и 3й, а 0й и 2й элементы. Извините
DOM представляет каждый элемент на странице (такие как заголовки, параграфы, изображения и т.д.) в виде объектов connections, которые можно манипулировать с помощью JavaScript.