addEventListener не является функцией: причины и решение
Быстрый ответ
Ошибка .addEventListener is not a function
обычно указывает на то, что объект, к которому вы попытались применить метод, не является DOM-элементом либо DOM еще не был полностью загружен. Для устранения проблемы вам следует:
- Удостовериться, что цель — корректный DOM-элемент, а не HTMLCollection или null.
- Применить событие
document.addEventListener('DOMContentLoaded', callback)
, чтобы гарантировать готовность всех DOM-элементов.
Вот пример кода:
// Дождитесь полного загрузки DOM!
document.addEventListener('DOMContentLoaded', () => {
// Обращаться к кнопке можно только после полной загрузки страницы
const button = document.getElementById('myButton');
if (button) { // Проверяем наличие кнопки
button.addEventListener('click', () => console.log('Кнопка нажата!'));
}
});
Работа с отдельными элементами и коллекциями элементов
Необходимо отличать методы выбора элементов, такие как getElementById
, возвращающий один элемент, и getElementsByClassName
, возвращающий HTMLCollection. Для последнего требуется применять обработчики событий к каждому элементу коллекции по отдельности:
// Присваиваем обработчики отдельно каждой кнопке из коллекции
const buttons = document.getElementsByClassName('myButtons');
Array.from(buttons).forEach(button => {
button.addEventListener('click', () => console.log('Кнопка нажата!'));
});
Если вам требуется конкретный элемент из коллекции, обратитесь к нему напрямую:
// Будем обрабатывать только первую кнопку из коллекции
document.getElementsByClassName('myButton')[0].addEventListener('click', showComment);
Корректная адресация элементов
При динамическом создании элементов, например, в функции showComment
, убедитесь, что вы обращаетесь к правильному элементу, а новый элемент корректно вставлен в DOM:
function showComment() {
// Создаём новый элемент textarea и включаем его в DOM
let textarea = document.createElement('textarea');
// Поместим его сразу за кнопкой
this.parentNode.insertBefore(textarea, this.nextSibling);
}
// Каждая кнопка получает свой уникальный обработчик события
buttons.forEach(button => button.addEventListener('click', showComment));
Похожи на массивы, но не являются ими
Объекты, возвращаемые при помощи таких методов как getElementsByClassName
или querySelectorAll
, имеют некоторые признаки массивов, но ими не являются. Чтобы применить обработчики событий, представьте их в виде массива либо воспользуйтесь циклом for...of
:
// Присваиваем обработчики каждой кнопке в псевдомассиве
let buttons = document.querySelectorAll('.myButton');
for (let button of buttons) {
button.addEventListener('click', showComment);
}
Защита обработчиков событий от ошибок
Для предотвращения ошибки .addEventListener
необходимо:
- Проверить тип объекта перед использованием его в качестве обработчика события.
- Гарантировать загрузку всех элементов при работе с динамически создаваемыми элементами.
- Для динамически добавляемых элементов назначать обработчики событий после их создания и вставки в DOM.
Визуализация
Вы можете представить .addEventListener
как нарезатель для пиццы (🍕
), которым вы пытаетесь нарезать жидкий суп (🍲
):
🍕 -> 🍕 = ✅ Пицца нарезана идеально (метод addEventListener применим к DOM-элементам)
🍕 -> 🍲 = ❌ Невозможно нарезать суп – эта идея абсурдна! (метод addEventListener не применим к объектам отличным от DOM)
Основной посыл: .addEventListener
требует действительного DOM-объекта.
Причина ошибки: Применение метода к неподходящему объекту.
Решение: Использовать .addEventListener
только с DOM-элементами.
Работа с динамическим содержимым
Что касается элементов, динамически добавляемых в DOM, то нужно применять особый подход к назначению им обработчиков событий. Вот советы по этому поводу:
Верификация связывания с динамическими элементами
Убедитесь, что новым элементам, интегрированным в DOM, назначены обработчики событий:
// Динамически созданной кнопке назначаем обработчик
const newButton = document.createElement('button');
newButton.innerText = 'Нажми меня!';
newButton.addEventListener('click', showComment);
document.body.appendChild(newButton);
Использование делегирования событий
Поверьте обработку событий родительскому элементу – он сможет перехватывать и управлять событиями, генерируемыми вложенными элементами:
// Делегируем обработку нажатий кнопок родительскому элементу
document.addEventListener('click', function(event) {
if (event.target.classList.contains('comment-button')) {
showComment.call(event.target);
}
});
Удаление обработчиков событий
Для оптимизации производительности или избегания утечек памяти воспользуйтесь removeEventListener
, чтобы удалить неприменимые обработчики:
// После назначения каждому обработчику его можно убрать
button.addEventListener('click', showComment);
// Если кнопка больше не нужна, удаляем обработчик
button.removeEventListener('click', showComment);
Полезные материалы
- Метод EventTarget: addEventListener() – Справочник Web API | MDN
- Введение в события – Изучение веб-разработки | MDN
- Стандартные действия браузера
- Свойство Node: nodeType – Справочник Web API | MDN
- Function.prototype.bind() – JavaScript | MDN
- Делегирование событий
- javascript – В чем разница между call и apply? – Stack Overflow