Перестает работать функция onсlick в innerHTML, если в html скрипту добавить type='module'
Имеется html документ, фрагмент из него:
<body class="body">
<button class="hello">hello</button>
</body>
<script src="index.js"></script>
и к нему index.js скрипт:
const body = document.querySelector('.body')
const helloButton = document.querySelector('.hello')
helloButton.addEventListener('click', (e)=> hello(100500))
const x = 5;
function hello(number) {
console.log('button',number)
}
for(let i=0;i<x;i++) {
const button = `<button onclick='hello(${i})'}>${i}</button>`
body.innerHTML += button;
}
Вопрос 1: в таком виде на страницу в цикле добавляется 5 кнопок, по нажатию на каждую срабатывает функция hello, в консоль выводится номер кнопки. Все ломается если при подключении скрипта в html, указать что это модуль, не могу понять почему:
<script type="module" src="index.js"></script>
Кнопки добавляются на страницу, но при нажатии получаю ошибки:
index.html:52 Uncaught ReferenceError: hello is not defined
at HTMLButtonElement.onclick (index.html:52:9)
Вопрос 2: при наличии этого цикла с кнопками, нажатие на кнопку, которая уже есть в html, которой добавлен eventListener через js
<button class="hello">hello</button>
helloButton.addEventListener('click', (e)=> hello(100500))
Нажатие на кнопку ничего не дает, eventListener не отрабатывает, не важно стоит ли type='module' или нет. Если цикл убрать, кнопка работает в обоих вариантах. Прошу объясните, гуглеж и stackOverflow на конкретно этот случай ответа не нашел, даже на английском.
Ответы (1 шт):
Модули - это "независимые" компоненты. Все объявленные переменные и функции доступны только внутри модуля. Для обмена переменными и функциями используются ключевые слова export/import.
Рассмотрим ваш пример с модулем и кнопкой.
Кнопка у вас создается так:
`<button onclick='hello(${i})'}>${i}</button>`
Где на событие клика должна присутствовать функция hello в текущем локальном или глобальном окружении, однако функция объявлена в модуле и не доступна из текущего окружения кнопки.
Я вижу 2 рещения:
- Создавать кнопку с помощью document.createElement и вешать ей событие клика через addEventListener в том же модуле
- После объявления функции hello выполнить
window.hello = hello;
. Это положит функцию hello в глобальное пространство браузера (не рекомендую)
Вот ваш HTML контейнер
<body class="body">
<button class="hello">hello</button>
</body>
Вы создали собыие клика:
const helloButton = document.querySelector('.hello')
helloButton.addEventListener('click', (e)=> hello(100500))
Далее вы в цикле каждую итерацию удаляете все из контейнера и заново добавляете.
Вот этот код
for(let i=0;i<x;i++) {
const button = `<button onclick='hello(${i})'}>${i}</button>`
body.innerHTML += button;
}
, если разложить его на более мелкие части выглядит так:
for(let i=0;i<x;i++) {
// Создали строку
const button = `<button onclick='hello(${i})'}>${i}</button>`;
// Получили содержимое контейнера в виде строки
const prevHTML = body.innerHTML;
// Говорим, добавь строку, но преобразуй ее в HTML элементы
body.innerHTML = prevHTML + button;
}
т.е. каждая итерация улаляет все содержимое и устанавливает заново НОВОЕ. Все события, которые были у удаляемых элементов удаляются.