Статьи

Удаление бесполезных узлов из DOM

В третьей статье этой серии, посвященной кратким и приятным функциям, я хотел бы показать вам простую функцию, которую я считаю необходимой при работе с HTML DOM . Функция называется clean()

Функция принимает в качестве аргумента ссылку на один элемент и удаляет все нежелательные узлы внутри нее. Функция работает непосредственно с рассматриваемым элементом, поскольку объекты в JavaScript передаются по ссылке — это означает, что функция получает ссылку на исходный объект, а не его копию. Вот код функции clean()

 function clean(node)
{
  for(var n = 0; n < node.childNodes.length; n ++)
  {
    var child = node.childNodes[n];
    if
    (
      child.nodeType === 8 
      || 
      (child.nodeType === 3 && !/\S/.test(child.nodeValue))
    )
    {
      node.removeChild(child);
      n --;
    }
    else if(child.nodeType === 1)
    {
      clean(child);
    }
  }
}

Таким образом, чтобы очистить эти нежелательные узлы внутри элемента <body>

 clean(document.body);

Кроме того, чтобы очистить весь документ, вы можете сделать это:

 clean(document);

Хотя обычной ссылкой будет узел Element#document Функция также не ограничена работой с HTML и может работать с любым другим видом XML DOM.

Зачем чистить DOM

При работе с DOM в JavaScript мы используем стандартные свойства, такие как firstChildnextSibling К сожалению, могут возникнуть сложности, когда в DOM присутствует пробел, как показано в следующем примере.

 <div>
  <h2>Shopping list</h2>
  <ul>
    <li>Washing-up liquid</li>
    <li>Zinc nails</li>
    <li>Hydrochloric acid</li>
  </ul>
</div>

Для большинства современных браузеров (кроме IE8 и более ранних версий ) предыдущий код HTML приведет к следующей структуре DOM.

 DIV
#text ("\n\t")
+ H2
| + #text ("Shopping list")
+ #text ("\n\t")
+ UL
| + #text ("\n\t\t")
| + LI
| | + #text ("Washing-up liquid")
| + #text ("\n\t\t")
| + LI
| | + #text ("Zinc nails")
| + #text ("\n\t\t")
| + LI
| | + #text ("Hydrochloric acid")
| + #text ("\n\t")
+ #text ("\n")

Разрывы строк и вкладки внутри этого дерева отображаются как пробельные узлы #text Так, например, если бы мы начали со ссылки на элемент <h2>h2.nextSiblingне будет ссылаться на элемент <ul> Вместо этого он будет ссылаться на пробельный узел #text Или, если бы мы начали со ссылки на элемент <ul>ul.firstChild<li>

HTML-комментарии также являются узлами, и большинство браузеров также сохраняют их в DOM — как они должны, потому что браузеры не должны решать, какие узлы важны, а какие нет. Но для сценариев очень редко нужны данные в комментариях. Гораздо более вероятно, что комментарии (и промежуточный пробел) являются нежелательными «мусорными» узлами.

Есть несколько способов борьбы с этими узлами. Например, повторяя их:

 var ul = h2.nextSibling;
while(ul.nodeType !== 1)
{
  ul = ul.nextSibling;
}

Самый простой, самый практичный подход — просто удалить их. Так вот, что делает функция clean()

После очистки элемента <div>h2.nextSiblingul.firstChild Очищенный DOM показан ниже.

 SECTION
+ H2
| + #text ("Shopping list")
+ UL
| + LI
| | + #text ("Washing-up liquid")
| + LI
| | + #text ("Zinc nails")
| + LI
| | + #text ("Hydrochloric acid")

Как работает функция

Функция clean() Рекурсия является очень мощной функцией и означает, что функция может очищать поддерево любого размера и глубины. Ключом к рекурсивному поведению является конечное условие оператора if

 else if(child.nodeType === 1)
{
  clean(child);
}

Итак, каждый из дочерних элементов передается в clean() Затем дочерние элементы этого дочернего узла передаются в clean() Это продолжается до тех пор, пока все потомки не будут очищены.

При каждом вызове clean()childNodes#commentnodeType#textnodeType#text Регулярное выражение на самом деле является обратным тестом, ищущим узлы, которые не содержат непробельных символов.

Функция, конечно, не удаляет все пробелы. Любые пробельные символы, являющиеся частью узла #text Таким образом, затрагиваются только узлы childeNodes.length

Обратите внимание, что итератор должен каждый раз запрашивать length Мы сделали это, потому что по мере продвижения мы удаляем узлы, что, очевидно, меняет длину коллекции.