Динамические списки с делегированием
Управляйте событиями элементов, добавленных во время выполнения.
«Динамические списки с делегированием» — бесплатный урок JavaScript Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения JavaScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс JavaScript Academy содержит 4 уроков всего.
Проблема динамических элементов
Если прикреплять обработчики непосредственно к элементам, у любого элемента, добавленного в DOM после настройки, обработчика не будет. Делегирование решает эту проблему, потому что обработчик родительского элемента также перехватывает события будущих дочерних элементов.
Прямые обработчики не работают
Здесь обработчик добавляется к существующим элементам. Новый добавленный элемент ничего не получает и остаётся неактивным.
document.querySelectorAll(".item").forEach((el) =>
el.addEventListener("click", handle)
);
list.appendChild(newItem); // newItem has NO handlerДелегирование работает автоматически
При наличии обработчика с делегированием на родительском элементе новый элемент обрабатывается автоматически. После каждой вставки повторно привязывать обработчик не нужно.
list.addEventListener("click", (e) => {
const item = e.target.closest(".item");
if (item) console.log("Clicked", item.dataset.id);
});
list.appendChild(newItem); // works immediatelyДобавление элементов
Создавайте новые элементы и добавляйте их. Поскольку родительский элемент уже использует делегирование, они становятся интерактивными сразу после появления.
function addItem(text) {
const li = document.createElement("li");
li.className = "item";
li.textContent = text;
list.appendChild(li);
}Удаление элементов
Удаление также выполняется просто. Обработчик с делегированием может обнаружить кнопку удаления внутри строки и удалить строку.
list.addEventListener("click", (e) => {
if (e.target.matches(".delete-btn")) {
e.target.closest(".item").remove();
}
});Различение поддействий
В строке может быть несколько интерактивных частей. Используйте matches() или closest(), чтобы определить, на какой из них произошёл щелчок, а затем выполните нужную ветвь.
list.addEventListener("click", (e) => {
if (e.target.matches(".edit-btn")) edit(e);
else if (e.target.matches(".delete-btn")) del(e);
});Отрисовка из данных
Распространённый шаблон: хранить состояние в массиве, отрисовывать строки на его основе и делегировать обработку щелчков. Повторная отрисовка заменяет дочерние элементы, но единственный обработчик родительского элемента продолжает работать без изменений.
function render(items) {
list.innerHTML = items
.map((i) => "<li class=\"item\" data-id=\"" + i.id + "\">" + i.name + "</li>")
.join("");
}Без сложностей с очисткой
При использовании обработчиков на отдельных элементах перед удалением узлов нужно не забыть вызвать removeEventListener, чтобы избежать утечек. Делегирование снимает эту обязанность: удаление дочернего элемента не оставляет обработчик без владельца.
Переключение классов
Делегирование хорошо сочетается с переключением классов для состояний выбора. Обработчик находит строку и переключает класс.
list.addEventListener("click", (e) => {
const row = e.target.closest(".item");
if (row) row.classList.toggle("selected");
});Чтение стабильного идентификатора
Поскольку узлы DOM заменяются при повторной отрисовке, никогда не храните ссылки на них. Храните стабильный идентификатор в data-id и ищите данные в своей модели по этому идентификатору внутри обработчика.
const id = e.target.closest(".item").dataset.id;
const record = state.find((r) => r.id === id);События ввода тоже
Делегирование не ограничивается щелчками. Вы можете таким же способом делегировать события input или change для динамически добавленных полей формы.
form.addEventListener("input", (e) => {
if (e.target.matches(".qty")) recalc(e.target);
});Быстрая проверка
Динамические списки и делегирование.
Итоги
Делегирование особенно полезно для динамических списков: один обработчик родительского элемента обслуживает текущие и будущие дочерние элементы, сохраняется после повторной отрисовки и не требует очистки отдельных узлов. Используйте matches() и closest(), чтобы выбирать ветвь по поддействию, и храните стабильные идентификаторы в data-*, чтобы обработчики могли находить Вашу модель данных.
Часто задаваемые вопросы
Урок «Динамические списки с делегированием» бесплатный?
Да — полный текст урока «Динамические списки с делегированием» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс JavaScript Academy, подпишись на CoddyKit PRO. Курс JavaScript Academy содержит 4 уроков всего.
Чему я научусь в уроке «Динамические списки с делегированием»?
Управляйте событиями элементов, добавленных во время выполнения. Ты практикуешь JavaScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать JavaScript Academy?
Предыдущий опыт не требуется. JavaScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Динамические списки с делегированием»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке JavaScript Academy?
Да. Каждый урок JavaScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Фазы перехвата и всплытия
- Шаблон делегирования событий
- Динамические списки с делегированием
- Пользовательские события