DOM и браузер
DOM — выбор и изменение, атрибуты против свойств, обработка событий, всплытие и пользовательские элементы.
8 вопросов
JuniorТеорияОчень частоЧем getElementById, querySelector и querySelectorAll различаются при поиске элементов DOM?
Чем getElementById, querySelector и querySelectorAll различаются при поиске элементов DOM?
getElementById ищет один элемент по атрибуту id и возвращает его или null. querySelector принимает любой CSS-селектор и возвращает первое совпадение или null. querySelectorAll возвращает статический NodeList со всеми совпадениями (пустой, если их нет). Методы по CSS-селектору гибче, но медленнее поиска по id.
Типичные ошибки
- ✗Думать, что
querySelectorAllвозвращает живойNodeList, который обновляется — он статический - ✗Забывать, что
querySelector('#id')требует префикс#, которыйgetElementById('id')опускает - ✗Ожидать, что
querySelectorAllвернётnullпри отсутствии совпадений, а не пустой список
Уточняющие вопросы
- →Почему перебор
NodeListчерезforEachработает, аmap— нет без предварительного преобразования? - →Чем статический
NodeListотличается от живогоHTMLCollection, возвращаемогоgetElementsByClassName?
MiddleТеорияОчень частоКаковы три фазы распространения события, и чем всплытие отличается от погружения?
Каковы три фазы распространения события, и чем всплытие отличается от погружения?
Событие проходит три фазы: погружение идёт вниз от window через предков к цели, фаза цели срабатывает на самом элементе, затем всплытие идёт обратно вверх через предков. Обработчики по умолчанию работают в фазе всплытия; передача true (или {capture: true}) в addEventListener заставляет их работать в фазе погружения. Всплытие позволяет родителю реагировать на события его потомков.
Типичные ошибки
- ✗Думать, что обработчики по умолчанию работают в погружении — по умолчанию всплытие
- ✗Считать, что погружение идёт вверх, а всплытие вниз — на самом деле наоборот
- ✗Полагать, что события срабатывают только на цели и не достигают предков
Уточняющие вопросы
- →Чем отличается
stopPropagation, вызванный в фазе погружения, от вызванного при всплытии? - →Почему некоторые события вроде
focusне всплывают, и что за их аналогfocusin?
JuniorТеорияЧастоВ чём разница между HTML-атрибутом и соответствующим ему свойством DOM?
В чём разница между HTML-атрибутом и соответствующим ему свойством DOM?
Атрибут — это строка, записанная в исходном HTML; getAttribute('value') возвращает текст исходной разметки. Свойство — живое значение на DOM-объекте, поэтому input.value отражает то, что ввёл пользователь, и меняется по мере ввода. Для большинства атрибутов они синхронизируются лишь при разборе; затем смена свойства оставляет атрибут замороженным на исходной разметке. Имена тоже различаются — атрибут class соответствует свойству className.
Типичные ошибки
- ✗Ожидать, что
getAttribute('value')вернёт текущий введённый пользователем текст - ✗Считать, что имя каждого атрибута равно имени свойства (
classпротивclassName) - ✗Полагать, что атрибут и свойство остаются синхронными в обе стороны после разбора
Уточняющие вопросы
- →Почему атрибуты
data-*ходят туда-обратно через свойствоdatasetкак живые значения? - →Когда установка булева свойства вроде
checkedотличается от установки его атрибута?
JuniorТеорияЧастоКак addEventListener регистрирует обработчик, и что за объект события передаётся в него?
Как addEventListener регистрирует обработчик, и что за объект события передаётся в него?
addEventListener(type, handler) привязывает колбэк к типу события, не затирая уже добавленные, поэтому у цели может быть много обработчиков. Когда событие срабатывает, браузер вызывает каждый обработчик с объектом Event: type, target (где оно возникло), currentTarget и данные конкретного типа, например key или координаты. removeEventListener отвязывает обработчик по той же ссылке.
Типичные ошибки
- ✗Думать, что второй
addEventListenerзатирает первый, а не добавляет ещё один - ✗Передавать анонимную функцию, а затем не мочь снять её через
removeEventListener - ✗Вызывать обработчик
onClick()вместо передачи ссылки, тут же его срабатывая
Уточняющие вопросы
- →Почему нельзя снять слушатель, добавленный как встроенная анонимная стрелочная функция?
- →Что задаёт третий аргумент (
captureили объект опций) уaddEventListener?
MiddleТеорияЧастоКак textContent, innerHTML, style, атрибуты и classList изменяют элемент DOM?
Как textContent, innerHTML, style, атрибуты и classList изменяют элемент DOM?
textContent читает или пишет обычный текст элемента, трактуя теги буквально. innerHTML читает или заменяет его разметку, разбирая строку как HTML. style задаёт встроенный CSS по свойствам (el.style.fontSize). setAttribute/getAttribute работают с HTML-атрибутами, а classList добавляет, удаляет или переключает отдельные CSS-классы, не затирая остальные. Для недоверенного текста предпочитайте textContent вместо innerHTML, чтобы избежать инъекций.
Типичные ошибки
- ✗Использовать
innerHTMLс недоверенными строками, открывая XSS-инъекцию - ✗Присваивать
classNameдля добавления одного класса, стирая существующие - ✗Ожидать, что
textContentотрисует HTML-теги, а не покажет их буквально
Уточняющие вопросы
- →Почему переприсваивание
innerHTMLу крупного элемента медленнее добавления одного узла? - →Когда
setAttribute('value', x)расходится с прямой установкой свойства.value?
MiddleТеорияЧастоЧто такое делегирование событий, и чем target отличается от currentTarget внутри обработчика?
Что такое делегирование событий, и чем target отличается от currentTarget внутри обработчика?
Делегирование событий вешает один слушатель на общего предка и опирается на всплытие, чтобы обрабатывать события многих потомков, вместо привязки к каждому ребёнку. Внутри обработчика event.target — элемент, который реально вызвал событие, а event.currentTarget — элемент, к которому привязан слушатель. Через target (часто с closest) определяют, на каком ребёнке произошло действие, включая добавленных позже.
Типичные ошибки
- ✗Путать
targetиcurrentTarget—targetэто то, на чём произошло действие - ✗Думать, что делегирование требует слушатель на каждом дочернем элементе
- ✗Забывать, что делегирование ловит и динамически добавленных потомков
Уточняющие вопросы
- →Почему
event.target.closest('.item')безопаснееevent.targetпри кликах по вложенным детям? - →Как делегирование снижает память и затраты на настройку в списке с тысячами строк?
MiddleТеорияИногдаКак определить пользовательский элемент, и что такое его коллбэки жизненного цикла и shadow DOM?
Как определить пользовательский элемент, и что такое его коллбэки жизненного цикла и shadow DOM?
Вы определяете класс, наследующий HTMLElement, и регистрируете его через customElements.define('my-tag', MyClass); имя обязано содержать дефис. Коллбэки жизненного цикла вызываются автоматически: connectedCallback при входе в DOM, disconnectedCallback при удалении и attributeChangedCallback при смене наблюдаемого атрибута. attachShadow({ mode: 'open' }) даёт элементу инкапсулированное shadow-дерево, чьи разметка и стили ограничены областью и изолированы от внешнего документа.
Типичные ошибки
- ✗Забывать, что имя тега пользовательского элемента обязано содержать дефис
- ✗Ожидать, что
attributeChangedCallbackсработает для атрибутов не изobservedAttributes - ✗Полагать, что стили shadow DOM вытекают в окружающий документ или из него
Уточняющие вопросы
- →Почему
observedAttributesнадо объявить, чтобы сработалattributeChangedCallback? - →Как элемент
<slot>проецирует потомков из light DOM в shadow-дерево?
SeniorТеорияИногдаЧем различаются preventDefault, stopPropagation, stopImmediatePropagation и return false?
Чем различаются preventDefault, stopPropagation, stopImmediatePropagation и return false?
preventDefault отменяет действие по умолчанию, но не останавливает движение события. stopPropagation прекращает дальнейшее погружение или всплытие к другим элементам, но всё же запускает прочие обработчики текущего; stopImmediatePropagation ещё пропускает эти оставшиеся обработчики. Пассивный слушатель обещает не вызывать preventDefault, давая браузеру прокручивать без ожидания. В инлайн-обработчике HTML return false делает и prevent-default, и stop-propagation.
Типичные ошибки
- ✗Смешивать
preventDefault(отменяет default) иstopPropagation(останавливает движение) - ✗Ожидать, что
return falseотменит default внутри колбэкаaddEventListener - ✗Думать, что пассивный слушатель всё же может вызвать
preventDefault, чтобы блокировать прокрутку
Уточняющие вопросы
- →Почему браузеры сделали слушатели
touchstart/wheelпассивными по умолчанию ради прокрутки? - →Когда на одном элементе два обработчика, что мешает второму запуститься?