Инспектор элементов: смотрим дерево вживую

окружение≈ 7 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Вкладка Elements — это не просмотр исходника, а окно в живой DOM: то же самое дерево, по которому ходит ваш querySelector. Научиться читать и править его мышью быстрее, чем обвешивать код строчками console.log.

Разделы темы:·

Откройте инструменты разработчика (F12, в macOS — Cmd+Option+I) и перейдите на вкладку Elements (в Firefox она называется «Инспектор»). Похоже на HTML-файл с отступами, но это результат разбора: браузер уже прочитал текст документа, построил из него дерево узлов и показывает вам именно дерево. Наведите мышь на строку — на странице подсветится область элемента; внизу панели появятся «хлебные крошки» вроде html > body > ul > li, то есть путь от корня.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

В разметке этого примера нет ни одного закрывающего </li> — и всё равно в дереве три полноценных элемента. Парсер закрыл пункты сам, как велит спецификация. Инспектор покажет ровно это: три аккуратные строки с </li> на конце, которых вы не писали. Первый повод запомнить: панель показывает итог, а не то, что вы набрали в редакторе.

Текстовые узлы. Переводы строк и отступы между тегами — тоже узлы, о них статья про дерево документа. Поэтому childNodes почти всегда длиннее children: во втором только элементы.

Комментарии. Это полноценные узлы дерева; некоторые фреймворки оставляют их как невидимые метки-разделители.

Псевдоэлементы. Строки ::before и ::after инспектор рисует отдельно, хотя в разметке их нет и достать их из JavaScript нельзя — они целиком порождены CSS.

#shadow-root. Под узлом может прятаться собственное внутреннее дерево. Так устроены веб-компоненты, а ещё <input type="range"> и <video> — у них шейдоу закрытый, браузер показывает его только по особой настройке.

Кликните по строке в Elements — узел становится выбранным (справа от него появляется пометка == $0). Теперь переключитесь в Console и наберите $0: это ссылка на тот самый элемент. Не нужно сочинять селектор — ткнули мышью и продолжили кодом. Ещё удобнее наоборот: кнопка со стрелкой в углу панели (Ctrl+Shift+C) позволяет выбрать элемент прямо на странице.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
Что набрать в консолиЧто это даёт
$0элемент, выбранный в Elements прямо сейчас
$1 … $4выбранные раньше, от свежего к старому
$("h2")то же, что document.querySelector
$$(".item")то же, что querySelectorAll, но сразу массив
inspect(el)перепрыгнуть на этот узел во вкладке Elements
copy(el.outerHTML)положить разметку узла в буфер обмена

$0, $$, inspect, copy существуют только внутри консоли DevTools. В файле скрипта их нет — код с $0 упадёт с ReferenceError. А если страница объявила свою переменную $ (классика — jQuery), она перебивает помощника: $$ при этом обычно продолжает работать.

Разница между $$(".note") и querySelectorAll не косметическая: второй возвращает NodeList, а у него нет привычных методов массива.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

По тексту узла можно дважды кликнуть и переписать его. Правый клик → Edit as HTML отдаёт на правку весь фрагмент целиком, Delete удаляет узел, а мышью строку дерева можно перетащить в другое место. Всё это — обычные операции над DOM, ровно те же, что делает код.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Правки в Elements живут ровно до F5: вы редактируете дерево в памяти вкладки, а не файл на диске. Подобрали удачный отступ или цвет — переносите в CSS сразу, иначе перезагрузка съест находку.

Правая часть панели — блок Styles. Там видно, какое правило откуда пришло и что чем перекрыто (перечёркнутое объявление проиграло). Галочка слева выключает объявление, не удаляя его; кнопка :hov принудительно включает состояния :hover, :focus, :active — иначе стиль исчезает ровно в тот момент, когда вы уводите мышь к панели. Новые объявления, набранные в блоке element.style, попадают в атрибут style элемента: это то же самое, что запись в el.style из кода.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Самое частое недоумение: в файле одно, в инспекторе другое. Так и задумано. С сервера приходит текст; браузер разбирает его, чинит ошибки разметки и достраивает обязательные узлы, которых вы не писали. Хрестоматийный случай — таблица: между <table> и <tr> парсер молча вставляет <tbody>, и селектор table > tr из-за этого не находит ничего.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Второй источник расхождений — код. Приложение, которое рисует интерфейс в браузере, присылает почти пустой документ: в исходнике вы увидите <div id="app"></div>, а в Elements — сотни узлов. Добавьте сюда расширения браузера, которые тоже дописывают свои элементы в чужие страницы, — и вопрос «откуда взялся этот div» перестаёт быть загадкой.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
Куда смотримЧто видим
view-source: (Ctrl+U) или Network → документ → Responseбайты, которые действительно прислал сервер
Вкладка ElementsDOM прямо сейчас: после парсера, скриптов и расширений
document.documentElement.outerHTMLто же текущее дерево, сериализованное обратно в строку

  • Вкладка Elements показывает не HTML-файл, а текущее состояние DOM: с достроенными узлами, результатами работы скриптов и вставками расширений.
  • Выбранный в панели узел доступен в консоли как $0; рядом живут $1…$4, $(), $$(), inspect() и copy() — они существуют только внутри DevTools.
  • Правка текста, Edit as HTML, перетаскивание узла и добавление стилей — обычные операции над DOM, которые исчезают при перезагрузке.
  • Ctrl+F внутри Elements ищет по тексту, CSS-селектору и XPath: счётчик совпадений позволяет проверить селектор до того, как он попадёт в код.
  • Настоящий ответ сервера смотрят в view-source: или в Network → Response; расхождение с деревом — норма, а не признак поломки.

1/3В файле было написано <table><tr><td>1</td></tr></table>, а инспектор показывает внутри table ещё и tbody. Почему?

2/3Что такое $0 в консоли инструментов разработчика?

3/3Вы через Edit as HTML заменили заголовок на странице интернет-магазина и нажали F5. Что произойдёт?

Проверьте селектор в дереве, прежде чем писать его в код

В корзине три пункта, один из них помечен классом sold. Наивный селектор ловит все три. Подберите такой, который вернёт только непроданные, и убедитесь двумя способами: через Ctrl+F во вкладке Elements (счётчик должен показать 2 совпадения) и через querySelectorAll в консоли.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Годится псевдокласс отрицания: он отсекает пункты с классом sold.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Тот же текст #cart .item:not(.sold) можно вставить в поиск по дереву: браузер поймёт его как CSS-селектор, подсветит два узла и покажет счётчик 1 of 2. Это и есть смысл упражнения — счётчик отвечает на вопрос «сколько элементов попадёт» до того, как селектор уедет в код.

Повторите правку из инспектора кодом

Всё, что вы делаете мышью в Elements, выражается парой строк на JavaScript. Возьмите значок-черновик и сделайте с ним то же, что сделали бы вручную: смените текст на «опубликовано» и допишите ему в атрибут style зелёный цвет. Затем выведите outerHTML и сравните с тем, что показывала бы панель.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Двойной клик по тексту — это textContent, строчка в блоке element.style — это el.style.

tsxлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Обратите внимание: цвет ушёл в атрибут style самого элемента — поэтому в панели он и появляется в блоке element.style, поверх всех правил из файлов CSS. И, как и правка мышью, эта строка живёт до перезагрузки.