@Alex_87

Как рекурсивно удалить текстовые узлы?

Как рекурсивно удалить текстовые узлы?

Пример:
После выполнения функции, дерево
<span> <div> <b>привет</b> </div> <p>loftchool</p> !!!</span>

должно быть преобразовано в <span><div><b></b></div><p></p></span>

Попытался решить следующим образом, но никак не пойму, почему свойство childNodes ведёт себя так странно, не могу понять, в чём ошибка.
  • Вопрос задан
  • 567 просмотров
Решения вопроса 2
0xD34F
@0xD34F Куратор тега JavaScript
Во-первых - вы передаёте в функцию строку и используете её в качестве селектора, непонятно, как вы намерены делать это рекурсивно? Пусть функция сразу получает элемент.

Во-вторых - вы обходите childNodes, который представляет собой динамический NodeList и одновременно пытаетесь его модифицировать. Удаляя один элемент, вы пропускаете следующий - т.е., если несколько текстовых элементов идут подряд, то удалён будет только каждый второй; а те нетекстовые элементы, которые расположены после текстовых, тут никакой рекурсии не случится, их содержимое вообще никак не будет обработано. Надо делать копию childNodes, и перебирать её. Или вместо for-of использовать цикл со счётчиком, при удалении элемента счётчик увеличиваться не должен.

В-третьих - какой-то бред с nextElementSibling, не знаю, как это комментировать. Надо было просто вызвать функцию, передав ей текущий элемент.

В четвёртых.
const deleteTextNodes = el =>
  el.nodeType === 3
    ? el.remove()
    : [...el.childNodes].forEach(deleteTextNodes);
Ответ написан
@syntaxorange
Начинать обход дерева `DOM` следует с корневого элемента. В нашем случае рутовым элементов является `body`.
Рекомендация - лучше стараться максимально упрощать требования, чтобы легче получалось обдумывать создаваемый алгоритм.

Начните с простой разметки, а затем добавляйте дополнительные, вложенные элементы.
<span>text1</span><span>text2</span>

Следует сформулировать два вопроса.

1. Как лучше удалять текстовый узел?
2. Как избежать бесконечного вызова рекурсии?

Алгоритм:

1. Вызывая функцию, стартуем обход дерева с элемента `body`. Передаём элемент при первом вызове.
2. Получаем коллекцию дочерних сущностей переданного элемента.
3. Фильтруем сущности с типом `ELEMENT_NODE`, кроме элемента с именем `SCRIPT`.
4. Удаляем сущности с типом `TEXT_NODE` в отдельной итерации или предыдущей итерации. Используем для этого метод `remove` прямо на узле (ответ на первый вопрос).
5. Итерируем только по найденным элементам (ответ на второй вопрос). Вызываем рекурсивно функцию, передавая найденный элемент. Если во вложенном элементе будут найдены только текстовые узлы, повторного вызова функцию не случится для текущей глубины. Но программа возвратится к предыдущей итерации и продолжит рекурсивный вызов со следующими элементами.

UPD. Раз уже есть примеры реализации, добавлю код.
function deleteTextNodesRecursive(element) {
  const childNodes = element.childNodes;
  
  var elements = [...childNodes].filter((val) => {
    if (val.nodeType === 3) {
      val.remove();
    }
    
    return val.nodeType === 1 && val.nodeName.toLowerCase() !== 'script';
  });
  
  if (elements.length) {
    elements.forEach((el) => {
      return deleteTextNodesRecursive(el);
    });
  }
  
  return document.body;
}
console.log(deleteTextNodesRecursive(document.body));
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
summer Ярославль
от 100 000 до 140 000 ₽
КРАФТТЕК Санкт-Петербург
от 60 000 до 80 000 ₽