JS: Деревья
Теория: HTML-дерево
Древовидные структуры встречаются в разных областях: генеалогическое древо, файловая система и т.д. В этом уроке мы познакомимся с деревом разметки HTML, которое постоянно встречается в веб-разработке.
Корнем является тег html. Важно отметить, что некоторые теги не могут иметь вложенные теги внутри себя, например, hr и input.
Попробуем описать это дерево в такой структуре, с которой было бы удобно работать. Первым шагом необходимо описать для каждого тега свойства, которыми он обладает. Как минимум можно выделить такие свойства: имя, тип, класс, дети. В реальности таких свойств бывает гораздо больше, но сейчас нам достаточно этих. Теперь опишем дерево html в этой структуре:
Главным свойством в каждом узле является тип узла. В нашем дереве есть теги и текст. Текст может быть вложен в тег, то есть может быть потомком. Поэтому текст является листовым узлом. Также у нас есть некоторые теги, которые являются листовыми узлами. Поэтому для тегов выделено два типа: tag-internal — внутренние узлы, это теги, которые могут иметь детей; tag-leaf — листовые узлы, это теги, которые не могут иметь детей. Итак, для описания нашего дерева html достаточно определить три типа узлов:
tag-internal- теги, которые могут иметь детей, внутренний узелtag-leaf- теги, которые не могут иметь детей, листовой узелtext- простой текст, листовой узел
Теперь мы можем работать с нашим деревом. Например, отфильтруем все пустые теги. Для этого прежде всего надо определить, как фильтровать каждый тип. Каждый тип фильтруется по-своему:
tag-internal- если нет детей или все дети пустые, значит и родитель пустойtag-leaf- не может иметь детей, такой тег всегда выводитсяtext- текстовый узел не может содержать детей, вместо этого он может содержать текстовый контент, поэтому фильтруем по пустому контенту
Функция фильтрации будет выглядеть следующим образом:
Фильтр в качестве параметра принимает узел с типом tag-internal и обрабатывает вложенные в него элементы. Вначале проходимся по всем потомкам и у всех, с типом tag-internal, фильтруем также вложенные элементы с помощью нашей же функции (рекурсия). Далее вызывается метод filter(), в нём каждый тип уже фильтруется по той логике, которую мы определили.
После фильтрации получим такое дерево:
Это дерево не содержит элемент div с классом hexlet-community, даже несмотря на то, что оно содержало другие элементы. Это произошло потому, что перед фильтрацией родителя были отфильтрованы его пустые потомки. Теперь можно собрать дерево в строку:
Если расставить отступы и каждый тег на новой строке, то на выходе получаем html без пустых тегов:
Код для обработки деревьев выглядит довольно лаконичным. Это - следствие удобной структуры для представления дерева html. Выделив в этом дереве несколько типов узлов, остаётся только описать логику для каждого типа. Любой внутренний узел является таким же деревом, поэтому обрабатывается рекурсивно этой же функцией. Правильное представление структуры значительно упрощает обработку дерева.



