Checkbox点击事件在Chromium浏览器中触发两次的问题求助
问题背景
页面里有多个<label>元素,每个标签包含一个checkbox和图片,label通过for属性和checkbox关联。给每个checkbox绑定了click事件监听器genB,逻辑是:勾选时创建按钮,取消勾选时移除按钮;按钮点击后要取消对应checkbox的勾选。
但在Chromium内核浏览器里出现异常:点击按钮后,checkbox的click事件会触发两次,导致checkbox始终处于勾选状态。两次触发的事件对象里nodeName、target、currentTarget都是INPUT,调用stopPropagation()也没用。Firefox浏览器无此问题。
操作流程:
- 首次点击
- 点击创建的按钮后,弹出两个弹窗,依次显示“check1 false”和“check1 true”
问题原因
核心问题出在按钮点击事件里的document.getElementById(event_checkbox).click()这行代码:
在Chromium内核中,通过JS调用checkbox的click()方法时,会先触发一次原生的状态切换,同时因为label和checkbox通过for属性关联,这个click操作会被label捕获,进而再次触发checkbox的click事件,最终导致两次事件触发。而Firefox中JS调用click()不会触发label的联动逻辑,所以没有问题。
另外,stopPropagation()无效是因为两次事件都是直接绑定在checkbox上的,并非事件冒泡导致,所以阻止冒泡起不到作用。
解决方案
优先方案:用change事件替代click事件,直接修改checked属性
change事件只会在checkbox的状态真正改变时触发,不会因为label联动或JS调用click()而重复触发,更适合处理状态变化逻辑。同时按钮点击时直接修改checkbox的checked属性,避免调用click()方法引发的额外事件。
修改后的代码如下:
JavaScript代码
chbx = document.querySelectorAll('.some_checkboxes'); for (let i = 0; i < chbx.length; ++i) { chbx[i].addEventListener("change", genB); // 替换为change事件 } function genB(event) { alert(event.target.id + " " + event.target.checked); if (event.target.checked) { let bt_r = createButton('buttonr'); event.target.parentNode.insertBefore(bt_r, event.target.nextSibling); bt_r.style.cssText = "top: 50vh; left: 50vw"; addBL(bt_r, event); } else { event.target.nextSibling.remove(); } } function addBL(button, event) { let event_checkbox = event.target.id; button.onclick = function() { const checkbox = document.getElementById(event_checkbox); checkbox.checked = !checkbox.checked; // 直接修改状态 // 如果有其他依赖change事件的逻辑,可手动触发change事件: // checkbox.dispatchEvent(new Event('change')); } } function createButton(buttonClass) { let button = document.createElement('button'); button.setAttribute('type', 'button'); button.classList.add(buttonClass, 'btn'); return button; }
HTML代码(修正img标签写法)
<label for="check1"> <input type="checkbox" id="check1" class="some_checkboxes"> <img alt="image"> </label> <label for="check2"> <input type="checkbox" id="check2" class="some_checkboxes"> <img alt="image"> </label> <label for="check3"> <input type="checkbox" id="check3" class="some_checkboxes"> <img alt="image"> </label>
备选方案(必须用click事件时)
如果因为特殊原因不能替换为change事件,可以在调用click()时暂时断开label和checkbox的关联,操作完成后恢复:
button.onclick = function(event) { const checkbox = document.getElementById(event_checkbox); const label = checkbox.closest('label'); const originalFor = label.getAttribute('for'); label.removeAttribute('for'); // 暂时断开关联 checkbox.click(); label.setAttribute('for', originalFor); // 恢复关联 }
不过这个方案不如优先方案简洁可靠,仅作为特殊场景下的备选。
内容的提问来源于stack exchange,提问作者Nikulok

