При клике на 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>

→ Ссылка