Javascript.RU

Списки узлов DOM. Динамика в примере.

Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/searching-elements-internals.

При поиске элементов в 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)
    }
}

Проверим работу функции на тестовом списке.

  1. Саша
  2. Маша
  3. Паша
  4. Даша

С одного нажатия все узлы удалить не получится. Проверьте. Почему?

Причина в том, что childNodes всегда содержит упорядоченный список текущих детей.

При работе функции removeChildren сначала удаляется первый элемент списка, children[0] (Саша). Если посмотреть на children после удаления, то обнаружится интересная картина:

Было:

  1. [0] Саша
  2. [1] Маша
  3. [2] Паша
  4. [3] Даша

Сашу удалили. Стало:

  1. [0] Маша
  2. [1] Паша
  3. [2] Даша

Список DOM-узлов по-прежнему начинается с 0, а функция продолжает удалять с children[1], так что Маша остается на месте:

  1. [0] Маша
  2. [1] Даша

Свойство length, как и сам список, отражает текущую картину вещей, поэтму цикл не идет дальше i=2 и заканчивает свою работу, удалив ровно половину элементов.

Как сделать правильно работающую функцию?

Вот один из вариантов:

function removeChildren(node) {
    var children = node.childNodes

    while(children.length) {
        node.removeChild(children[0])
    }
}

Общий принцип такой: если вам нужно производить изменения с набором DOM-узлов - имейте в виду, что все ваши изменения отражаются в DOM не после окончания работы скрипта, а сразу же.


Автор: tenshi, дата: 9 апреля, 2008 - 12:34
#permalink
while( child= element.firstChild ) element.removeChild( child )
element.innerHTML= ''

а вообще, я предпочитаю насильно приводить списки узлов к типу Array, что позволяет использовать соответствующие методы для работы с массивами.

.ня


Автор: all87 (не зарегистрирован), дата: 14 июля, 2010 - 03:22
#permalink

innertHTML = '' сделает Вам каку если понадобится где либо использовать удаленные потомки.


Автор: orgasm (не зарегистрирован), дата: 21 ноября, 2008 - 00:23
#permalink

Сначала удаляются пустые текстовые элементы, если браузер их создал (в соответствии с написанным в ДОМ-1). Google Chrome, кстати.


Автор: valenok2003, дата: 10 апреля, 2009 - 13:08
#permalink

У меня почему-то после первого нажатия никто не удаляется, после второго - удаляются Саша и Паша одновременно, потом Маша, потом Даша. Хоть в опере хоть в IE.
Извините, сам разобрался.


Автор: Ax (не зарегистрирован), дата: 2 июня, 2009 - 12:05
#permalink

А мне не понятно, почему сначало ничего не происходит, а лишь потом удаляются Саша и Паша. Может быть потому, что между ними пустые узлы? Мне кажется это нужно уточнить.


Автор: aldan8, дата: 24 августа, 2009 - 18:52
#permalink

Полностью согласен.
Если удалить пробелы и табы между еллементами
(ol и li) все работает правильно , т. е. удаляются без задержки .


Автор: Satis-PSP, дата: 14 октября, 2009 - 10:59
#permalink

А почему нельзя было просто вписать:

node.innerHTML = ''

??

Работает же.


Автор: Илья Кантор, дата: 14 октября, 2009 - 16:01
#permalink

Не очень помню детали, но это вроде не всегда работает, бывало в узле оставалось не пусто, а nbsp; (в IE?)

Может подскажете точнее, если кто недавно на граблю наступал с таким способом ?


Автор: Богдан (не зарегистрирован), дата: 3 февраля, 2010 - 13:17
#permalink

В IE можно было бы использовать, ан нет, глючит

<select name="flat" id="flat" size="5">
           <option value="1" selected="selected">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
          </select>
		 <script>
			document.getElementById('flat').innerHTML='<option value="2">111</option>';
		 </script>

Автор: Илговский Евгений, дата: 11 ноября, 2009 - 12:09
#permalink

Я думаю, что произойдет утечка памяти...


Автор: Гость (не зарегистрирован), дата: 24 ноября, 2009 - 19:51
#permalink

А из-за чего ? Т.е. Это предположение или утечка памяти в данном случае обоснована ?


Автор: Satis-PSP, дата: 15 октября, 2009 - 12:45
#permalink

пробывал в IE, Opera, Chrome и Firefox. Везде все сработало. Ну может он не такой надежный и все таки не всегда срабатывает. Сложно сказать.


Автор: EgorOFF (не зарегистрирован), дата: 25 октября, 2009 - 15:52
#permalink

Учитывая особенность поведения 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]);
    }
}

Такой способ не приводит к пропуску элементов при итерации цикла, это позволяет проверять свойства каждого элемента, и удалять не все подряд:

for(var i=children.length-1; i>=0; i--) {
    if(children[i].getAttribute("class")=="delete"){
        node.removeChild(children[i]);
    }
}

Автор: subzey, дата: 6 декабря, 2009 - 16:12
#permalink

Да, это хороший способ, когда-то даже очень сильно выручил.

...хотя, кстати, и он не стопроцентный. Обработчик DOMNodeRemoved сможет поменять содержимое списка нод между итерациями цикла.


Автор: WeslomPo (не зарегистрирован), дата: 12 апреля, 2010 - 09:50
#permalink

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)
}
}

А чем такое не тру?


Автор: lum (не зарегистрирован), дата: 21 апреля, 2010 - 21:26
#permalink

Будет ошибка
var child = children[i]
ведь при первой итерации у тя i=children.length


Автор: THERE (не зарегистрирован), дата: 2 июня, 2010 - 22:18
#permalink
function removeChildren(node) {
    var children = node.childNodes
    var length = children.length

    for(var i=0; i < length; i++) {
        node.removeChild(children[0])
    }
}

я новичёк и не знаю тонкостей работы интерпретатора, но по опыту других языков это должен быть самый быстрый вариант, поскольку нет вызовов функций в условии цикла. правда этот вариант работоспособен при условии, что во время работы цикла не изменится размер DOMNodeList.


Автор: Hobbit (не зарегистрирован), дата: 2 июля, 2010 - 14:10
#permalink

Спасибо, весь цикл статей был хорош, но тут я бы сделал вот так:

function removing(elem){
            var count = elem.childNodes.length;
            if (elem.childNodes[count-1].nodeType == 1 && elem.childNodes[count-1].tagName != "BR"){
                var remove = elem.childNodes[count-1];
                elem.removeChild(remove);
            }
            else{
                var remove = elem.childNodes[count-1];
                elem.removeChild(remove);
                removing(elem);
            }
        }

Автор: Гость (не зарегистрирован), дата: 14 октября, 2010 - 13:50
#permalink

я только узнаю азы JS (сам я С++-сник), в данном случае почему нельзя использовать:
while(elem.firstChild != null) elem.removeChild(elem.firstChild);
?


Автор: StNekroman (не зарегистрирован), дата: 5 марта, 2011 - 19:47
#permalink

Ты всё верно написал.
Так и надо делать.
Я сам С++-ник.
Это они не программеры, раз выдумывают рашпильные, полурабочие способа дял стандартных действий.


Автор: Гость (не зарегистрирован), дата: 11 апреля, 2011 - 14:15
#permalink

Посмотрите на него, какой умный.
А ничего, что способ из статьи более читаемый? И откуда Вы взяли, что это "полурабочий способ"?


Автор: wlong, дата: 1 декабря, 2013 - 09:41
#permalink

Самый точный алгоритм. Именно так профессиональные программисты и пишут.


Автор: Гость (не зарегистрирован), дата: 4 июня, 2011 - 14:14
#permalink

Можно использовать вот такую функцию для удаления всех дочерних элементов

function removeChildren(node) {
var ch = node.childNodes;

for(var i=0;i


Автор: Гость (не зарегистрирован), дата: 4 июня, 2011 - 14:15
#permalink

Можно использовать вот такую функцию для удаления всех дочерних элементов

function removeChildren(node) {
var ch = node.childNodes;

for(var i=0;i<ch.length; i++) {
	        var child = children[i--];
	        node.removeChild(child);
	    }
}

Автор: trikadin, дата: 2 июля, 2011 - 13:54
#permalink

Короткая:

function removeChilds(node)
{
 while(node.childNodes)
  node.removeChild(node.firstChild);
};

Автор: lenok_sm, дата: 21 июля, 2011 - 18:13
#permalink

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)


Автор: lenok_sm, дата: 21 июля, 2011 - 18:15
#permalink

Немного промахнулась разделом =(


Автор: mov (не зарегистрирован), дата: 21 октября, 2011 - 02:57
#permalink

Мне иногда бывает удобно делать так:

function removeNext(tagID){
    var elem = document.getElementById(tagID)
    elem.removeChild(elem.getElementsByTagName('li')[0])
}

Автор: Бо (не зарегистрирован), дата: 21 октября, 2011 - 04:44
#permalink

На мой взгляд:
while(elem.firstChild != null) elem.removeChild(elem.firstChild);
наглядно и лаконично


Автор: Zenitchik (не зарегистрирован), дата: 23 апреля, 2012 - 13:43
#permalink
while(elem.firstChild) elem.removeChild(elem.firstChild);

Ещё лаконичнее, и ничуть не менее наглядно для человека, знающего JS.


Автор: pashak, дата: 11 марта, 2013 - 15:34
#permalink
var t; while(t=elem.lastChild) elem.removeChild(t);

Чуть быстрее и чуть меньше. Выигрыш за счёт минимального количества обращений к свойствам DOM (lastChild, removeChild по одному разу).


Автор: MiF, дата: 17 октября, 2012 - 14:33
#permalink

А можно написать так:

function removeChildren(node) {
    var children = node.childNodes

    for(i=children.length; i--; )
        node.removeChild(children[0]);
    }
}

Работает быстрее из за однократного обращения к length


Автор: Vasy, дата: 9 февраля, 2013 - 13:28
#permalink

Скажите пожалуста почему данный код

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 ...как он удаляет эти элементы?


Автор: Гость (не зарегистрирован), дата: 8 декабря, 2015 - 23:00
#permalink

При первом нажатии(запуске цикла) удаляются пробелы. При втором нажатии удаляются 0 и 3 элементы, цикл завершается, поскольку i=2, а children.length=1 (маша-0, даша-1). При 3 выход из цикла уже при i=1, ну и дальше остается одна даша, для которой опять придется запускать цикл.


Автор: Гость (не зарегистрирован), дата: 8 декабря, 2015 - 23:01
#permalink

Очепятался не 0й и 3й, а 0й и 2й элементы. Извините


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

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
13 + 2 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Реклама
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum