DOMContentLoaded в шаблонах компонентов
При разработке шаблонов компонентов часто встречается такой код:
document.addEventListener("DOMContentLoaded", function () {
// Инициализация скриптов компонента
});
В контексте решений, где компоненты могут подгружаться в модальные окна или через AJAX, такой подход
приводит к проблемам.
В чём проблема
Событие DOMContentLoaded срабатывает один раз — когда основной HTML-документ загружен и разобран
браузером.
Если ваш компонент:
- открывается в модальном окне и HTML подгружается динамически;
- подставляется на страницу через
AJAX; - перерисовывается без полной перезагрузки страницы,
то на момент, когда скрипт внутри шаблона компонента подключается, событие DOMContentLoaded уже давно
прошло.
В результате:
- обработчик добавляется после срабатывания события;
- функция инициализации никогда не выполнится.
К каким сложностей это приводит
В таких сценариях перестают работать:
- обработчики кликов на кнопках/ссылках;
- маски и валидация форм;
- слайдеры, табы, аккордеоны и прочий интерактив;
- любая логика, которая должна запускаться "после загрузки компонента".
При этом на "обычной" странице без AJAX всё может работать корректно, из-за чего ошибка долго остаётся
незамеченной.
Как делать правильно
- Не привязывать код к
DOMContentLoadedвнутри шаблона компонентаВ большинстве случаев HTML компонента уже есть в DOM к моменту выполнения скрипта. Можно вызывать инициализацию сразу:
(function () { // Инициализация скриптов компонента })(); - Использовать отдельную функцию и вызывать её после вставки компонента
- после AJAX-подгрузки HTML;
- после открытия модального окна, когда разметка вставлена в DOM.
- Использовать делегирование событий
Вынесите инициализацию в функцию:
function initMyComponent(container) {
// container — корневой элемент компонента
// навешиваем обработчики, запускаем плагины и т.д.
}
А затем вызывайте её:
Вместо того чтобы навешивать обработчики на элементы при загрузке, используйте делегирование:
document.addEventListener("click", function (e) {
if (e.target.closest(".js-my-button")) {
// Обработка клика по кнопке компонента
}
});
Тогда новые элементы, добавленные через AJAX или в модалку, будут работать без повторной
инициализации.
Заключение
document.addEventListener("DOMContentLoaded", ...), потому
что событие уже могло отработать до подключения вашего кода.
Для компонентов, которые могут загружаться динамически, лучше вызывать инициализацию сразу, использовать отдельные
init-функции и/или делегирование событий.