При клике на input внутри с listener'ом функция вызывается дважды
Всем привет!
Мне нужна помощь со следующей ситуацией. В разметке html есть следующий блок:
<td class="statistics-section__body-cell statistics-section__body-cell-breakpoint-checkbox flex">
<label class="statistics-section__body-cell-breakpoint-checkbox-input-label">
<input class="statistics-section__body-cell-breakpoint-checkbox-input" type="checkbox">
</label>
</td>
И на td, а точнее, на его класс, навешан прослушиватель:
document.querySelectorAll(".statistics-section__body-cell-breakpoint-checkbox").forEach(function(newBreakPointWindow) {
newBreakPointWindow.addEventListener("click", function(e) {
let parent = e.currentTarget;
(и остальная функция)
...
})
});
Label у меня стилизован под input, а сам input скрыт:
position: absolute;
visibility: hidden;
width: 0;
height: 0;
Задумка в том, что сама ячейка td имеет определённые размеры, а input - это всего лишь небольшой чекбокс, и дабы не целиться каждый раз в этот чекбокс, можно было бы просто кликнуть на ячейку с ним и активировать привязанную функцию.
Когда я кликаю на td, всё работает хорошо.
Проблема начинается, когда клик происходит непосредственно по "чекбоксу". По какой-то причине функция, привязанная к прослушивателю, вызывается дважды.
Это при том, что сам инпут скрыт css-свойствами. А даже если бы и не был скрыт, такое поведение мне всё равно непонятно.
Пробовал даже экспериментировать и заменял input на пустой div. В результате двойной вызов функции не происходит.
Возможно, кто-то сможет подсказать, что это за магия input'а такая, и как можно без загромождения кода в скрипте предотвратить двойной вызов по нажатию на чекбокс?
Ответы (2 шт):
Автор решения: Evgenii
e.currentTarget - Всегда совпадает с элементом, на котором обработчик события был назначен
e.target - Совпадает с элементом, на котором событие возникло.
Что происходит у вас в коде при выполнении кода:
let parent = e.currentTarget; if (e.target !== parent) return false;
При клике на ячейку у вас e.currentTarget === ячейка, и e.target === ячейка
Ваша запись вернет false и ничего не сработает
При клике на чекбокс будет клик на чекбоксе и на ячейке,
при этом всплытие никто не отменял, следовательно:
e.currentTarget === чекбоксу, e.target === чекбоксу, снова вернется false,
Далее будет всплытие события:
e.currentTarget === ячейка, e.target === чекбокс, и вот тут ваше условие срабатывает и вернется true
Т.е. Ваш код работает лишь при прямом нажатии на чекбокс.
Есть более элегантное решение:
Замените
document.querySelectorAll(".statistics-section__body-cell-breakpoint-checkbox") на document.querySelectorAll("td.statistics-section__body-cell-breakpoint-checkbox")
Таким образом ваш обработчик события будет назначаться лишь на ячейку таблицы, а не на вложенные элементы.
- Советую при скрытии элемента задавать ему отрицательный z-index
→ Ссылка
Автор решения: vegasmoscow
В комментариях вам уже написали, что label, при клике на нём, согласно спецификации, вызовет клик и на вложенном input. Такое у него поведение по умолчанию. Это как при клике на ссылку с пустым href происходит перезагрузка страницы. Или при клике на кнопку submit формы происходит также перезагрузка страницы.
В вашем случае решить данную проблему легко - вешайте обработчик события не на label, а на сам чекбокс. И тогда, даже по клику на label, автоматически произойдет клик и на чекбоксе (даже если он скрыт).
Запустите мой код ниже и увидите, что сам чекбокс скрыт, но событие на нём срабатвает.
// Селектором ищем не label, а сам checkbox
document.querySelector(".checkbox").addEventListener("click", function(e) {
console.log('Сработал клик по чекбоксу');
// (и остальная функция)
});
.checkbox {
position: absolute;
visibility: hidden;
width: 0;
height: 0;
}
.table-cell {
width: 120px;
height: 120px;
background-color: plum;
display: flex;
align-items: center;
justify-content: center;
}
.label {
display:flex; /* без этого label станет невидимым */
align-items: center;
justify-content: center;
color: #666666;
width: 70px;
height: 70px;
background-color: coral;
}
<table>
<td class="table-cell">
<label class="label">Label
<input class="checkbox" type="checkbox">
</label>
</td>
</table>
→ Ссылка
Всем привет!
Мне нужна помощь со следующей ситуацией. В разметке html есть следующий блок:
<td class="statistics-section__body-cell statistics-section__body-cell-breakpoint-checkbox flex">
<label class="statistics-section__body-cell-breakpoint-checkbox-input-label">
<input class="statistics-section__body-cell-breakpoint-checkbox-input" type="checkbox">
</label>
</td>
И на td, а точнее, на его класс, навешан прослушиватель:
document.querySelectorAll(".statistics-section__body-cell-breakpoint-checkbox").forEach(function(newBreakPointWindow) {
newBreakPointWindow.addEventListener("click", function(e) {
let parent = e.currentTarget;
(и остальная функция)
...
})
});
Label у меня стилизован под input, а сам input скрыт:
position: absolute;
visibility: hidden;
width: 0;
height: 0;
Задумка в том, что сама ячейка td имеет определённые размеры, а input - это всего лишь небольшой чекбокс, и дабы не целиться каждый раз в этот чекбокс, можно было бы просто кликнуть на ячейку с ним и активировать привязанную функцию.
Когда я кликаю на td, всё работает хорошо.
Проблема начинается, когда клик происходит непосредственно по "чекбоксу". По какой-то причине функция, привязанная к прослушивателю, вызывается дважды.
Это при том, что сам инпут скрыт css-свойствами. А даже если бы и не был скрыт, такое поведение мне всё равно непонятно.
Пробовал даже экспериментировать и заменял input на пустой div. В результате двойной вызов функции не происходит.
Возможно, кто-то сможет подсказать, что это за магия input'а такая, и как можно без загромождения кода в скрипте предотвратить двойной вызов по нажатию на чекбокс?
Ответы (2 шт):
e.currentTarget - Всегда совпадает с элементом, на котором обработчик события был назначен
e.target - Совпадает с элементом, на котором событие возникло.
Что происходит у вас в коде при выполнении кода:
let parent = e.currentTarget; if (e.target !== parent) return false;
При клике на ячейку у вас e.currentTarget === ячейка, и e.target === ячейка
Ваша запись вернет false и ничего не сработает
При клике на чекбокс будет клик на чекбоксе и на ячейке, при этом всплытие никто не отменял, следовательно:
e.currentTarget === чекбоксу, e.target === чекбоксу, снова вернется false,
Далее будет всплытие события:
e.currentTarget === ячейка, e.target === чекбокс, и вот тут ваше условие срабатывает и вернется true
Т.е. Ваш код работает лишь при прямом нажатии на чекбокс.
Есть более элегантное решение:
Замените
document.querySelectorAll(".statistics-section__body-cell-breakpoint-checkbox") на document.querySelectorAll("td.statistics-section__body-cell-breakpoint-checkbox")
Таким образом ваш обработчик события будет назначаться лишь на ячейку таблицы, а не на вложенные элементы.
- Советую при скрытии элемента задавать ему отрицательный z-index
В комментариях вам уже написали, что label, при клике на нём, согласно спецификации, вызовет клик и на вложенном input. Такое у него поведение по умолчанию. Это как при клике на ссылку с пустым href происходит перезагрузка страницы. Или при клике на кнопку submit формы происходит также перезагрузка страницы.
В вашем случае решить данную проблему легко - вешайте обработчик события не на label, а на сам чекбокс. И тогда, даже по клику на label, автоматически произойдет клик и на чекбоксе (даже если он скрыт).
Запустите мой код ниже и увидите, что сам чекбокс скрыт, но событие на нём срабатвает.
// Селектором ищем не label, а сам checkbox
document.querySelector(".checkbox").addEventListener("click", function(e) {
console.log('Сработал клик по чекбоксу');
// (и остальная функция)
});
.checkbox {
position: absolute;
visibility: hidden;
width: 0;
height: 0;
}
.table-cell {
width: 120px;
height: 120px;
background-color: plum;
display: flex;
align-items: center;
justify-content: center;
}
.label {
display:flex; /* без этого label станет невидимым */
align-items: center;
justify-content: center;
color: #666666;
width: 70px;
height: 70px;
background-color: coral;
}
<table>
<td class="table-cell">
<label class="label">Label
<input class="checkbox" type="checkbox">
</label>
</td>
</table>