Django+JS中addEventListener执行两次致单选框状态异常问题
解决Django表单分类切换时点击事件触发两次的问题
问题场景
用Django和JavaScript开发表单创建功能,需要点击分类标签切换单选框的checked属性来更改条目分类,但点击secondLabel时addEventListener会执行两次,导致checked属性刚被添加就立即移除。需求是点击secondLabel时,首次切换到分类2,再次点击切回分类1,循环切换。
原代码
Django表单模板
<div class="new-post-header"> <div class="new-post-functions"> <button type="submit" class="save"><ion-icon name="checkmark-outline"></ion-icon></button> <div class="categories"> <div class="first-category">{{ form.category.0 }}</div> <div class="second-category">{{ form.category.1 }}</div> </div> </div> </div> <div class="new-post-content"> <div class="new-title">{{ form.title }}</div> <div class="new-description">{{ form.description }}</div> </div>
原JavaScript代码
const firstLabel = document.querySelector('[for="id_category_0"]') const secondLabel = document.querySelector('[for="id_category_1"]') const firstInput = document.querySelector('input[value="1"]') const secondInput = document.querySelector('input[value="2"]') secondInput.hasAttribute('checked') ? localStorage.setItem('secondInput', true) : localStorage.setItem('secondInput', false) secondLabel.addEventListener('click', function() { let secondInputItem = localStorage.getItem('secondInput') if (secondInputItem === 'false') { firstLabel.innerHTML = `<input type="radio" name="category" value="1" required="" id="id_category_0"><ion-icon name="bookmark-outline"></ion-icon>` secondLabel.innerHTML = `<input type="radio" name="category" value="2" required="" id="id_category_1" checked="checked"><ion-icon name="archive-outline"></ion-icon>` localStorage.setItem('secondInput', true) console.log(true) } else { firstLabel.innerHTML = `<input type="radio" name="category" value="1" required="" id="id_category_0" checked="checked"><ion-icon name="bookmark-outline"></ion-icon>` secondLabel.innerHTML = `<input type="radio" name="category" value="2" required="" id="id_category_1"><ion-icon name="archive-outline"></ion-icon>` localStorage.setItem('secondInput', false) console.log(false) } })
问题原因
- 每次点击都通过
innerHTML重新渲染label内部元素,破坏原有DOM结构,导致事件触发逻辑异常; - 手动修改HTML结构切换选中状态,忽略了radio按钮的原生互斥特性(同一
name下的radio会自动取消其他选项的选中状态); - localStorage存储布尔值时会自动转为字符串,直接用字符串判断容易出现类型匹配问题。
解决方案
优化后的JavaScript代码(保留localStorage状态)
const firstInput = document.querySelector('input[value="1"]'); const secondInput = document.querySelector('input[value="2"]'); const secondLabel = document.querySelector('[for="id_category_1"]'); // 初始化localStorage,用JSON处理布尔值类型 const isSecondChecked = secondInput.checked; localStorage.setItem('secondInput', JSON.stringify(isSecondChecked)); secondLabel.addEventListener('click', function(e) { // 阻止事件冒泡,避免额外触发 e.stopPropagation(); // 解析localStorage中的布尔值 let secondInputItem = JSON.parse(localStorage.getItem('secondInput')); if (!secondInputItem) { secondInput.checked = true; localStorage.setItem('secondInput', JSON.stringify(true)); } else { firstInput.checked = true; localStorage.setItem('secondInput', JSON.stringify(false)); } });
极简版(无需localStorage,直接依赖radio状态)
如果不需要页面刷新后保留选中状态,可简化为:
const firstInput = document.querySelector('input[value="1"]'); const secondInput = document.querySelector('input[value="2"]'); const secondLabel = document.querySelector('[for="id_category_1"]'); secondLabel.addEventListener('click', function(e) { e.stopPropagation(); // 直接根据当前选中状态切换,radio互斥特性会自动处理另一个选项 secondInput.checked = !secondInput.checked; });
优化说明
- 不再修改
innerHTML,直接操作input的checked属性,保留原有DOM结构,避免事件触发异常; - 利用radio按钮的原生互斥特性,无需手动修改两个input的状态,简化代码逻辑;
- 用
JSON.stringify和JSON.parse处理localStorage的布尔值,确保类型判断准确; e.stopPropagation()阻止事件冒泡,避免父元素事件或浏览器原生label行为导致的额外触发。
内容的提问来源于stack exchange,提问作者heyz
相关产品推荐
相关产品推荐

