Во-первых, давайте посмотрим, что делает метод querySelectorAll.

Метод «Document querySelectorAll() возвращает статический (не живой) NodeList, представляющий список элементов документа, соответствующих указанной группе селекторов.»

Пример:

Для примера HTML

если мы запустим document.querySelectorAll("div"), вы получите коллекцию всех элементов "div", т.е.

(3) [div.footer, div#back-image, div#forward-image]

0: раздел.нижний колонтитул

1: div#заднее изображение

2: div#вперед-изображение

Итак, вы вводите строку селектора (как вы пишете селекторы в css), и метод возвращает вам все элементы, которые будут выбраны выражением.

Теперь давайте реализуем… !

Во-первых, нам нужно пройти через DOM. вот рекурсивная процедура, чтобы сделать это...

Объяснение:
Итак, DOM является древовидной структурой с n дочерними элементами, и вы можете перемещаться по ней как по одной.
— проверка нулевого базового случая,
— печать узла (HTMLElement здесь),
— повторение с каждым дочерним элементом в качестве следующего узла (здесь node.children возвращает коллекцию всех дочерних элементов)

Во-вторых, нам нужно оценить все правила css для элемента.

как это сделать ? мы можем проанализировать выражение селектора css, используя регулярное выражение, на основе всех существующих селекторов css. Затем оцените каждое выражение, например, проверьте имя тега, имя класса, идентификатор и т. д..
Но подождите, разве браузер не оценивает css именно так, верно?
Да, и все современные браузеры также предоставляют этот метод, Element.matches(). Давайте воспользуемся этим.

Вот функция, которая оценивает, соответствует ли селектор элементу:

Функция проверяет, предоставляет ли браузер функцию match(), если нет, мы оцениваем, используя собственный селектор, предоставляемый разными браузерами по-разному.

Наконец,мы можем объединить два вышеуказанных метода для реализации querySelectorAll()
вот как выглядит окончательный код:

Объяснение:
мы просматриваем дерево DOM (обход вспомогательного метода) и оцениваем выражение селектора ввода для каждого элемента (вспомогательный метод isMatch()). Если элемент совпадает, мы сохраняем его (результат var). После обхода возвращаем результат.

Выше приведен рабочий код, вы можете запустить его в консоли браузера.
Если в настоящее время в дереве есть n узлов, временная сложность будет O(n). это лучшее, что мы можем сделать, поскольку нам нужно проверить каждый узел хотя бы один раз.

Примечание. Существует реализация Element.querySelectorAll(), которая работает не со всем деревом DOM, а с поддеревом элемента, на котором вызван элемент.
Вышеприведенный код можно легко изменить для этого:
- Добавьте метод myQuerySelectorAll в Element.prototype вместо Document.prototype. [Строка: 26]
— Обход с использованием «this» (Элемент) в качестве корня вместо «this.documentElement» (элемент html). [Строка: 41]

Примечание. Фактический метод возвращает NodeList, но вместо этого мы возвращаем массив. что похоже, но не совсем то же самое.

Примечание. Для реализации querySelector().
который возвращает только первое совпадение, используя предварительный обход в глубину. (именно так мы обходим…)
мы можем легко изменить рекурсию, чтобы она останавливалась, когда мы получаем первое истинное значение от isMatch. Вы можете сделать это правильно?