本文详解为何在 for 循环中直接为 radio 按钮赋值 `onclick` 会导致所有按钮点击时都显示最后一个值,并提供使用 `addeventlistener` 和事件委托两种现代、可靠的解决方案。
你在循环中为每个单选按钮绑定点击事件时,看似逻辑清晰,但实际运行结果却出人意料:无论点击哪个选项,弹窗总是显示 "You chose writing."(即数组末项的值)。根本原因在于 闭包与变量作用域的误解——var 声明的 i 在循环结束后仍被所有箭头函数共享,而 let 虽然创建了块级作用域,但你的写法中 radio[i] 在回调执行时才求值,此时循环早已结束,i 已越界(i === radio.length),导致 radio[i] 为 undefined,进而引发运行时错误或意外行为(取决于浏览器环境)。
更准确地说,问题核心并非“事件只触发一次”,而是 事件监听器确实被正确绑定并每次点击都执行,但回调中访问的 radio[i] 引用了已失效的索引。以下给出两种推荐解法:
const radio = document.getElementsByName('subject');
for (let i = 0; i < radio.length; i++) {
radio[i].addEventListener('click', function() {
alert(`You chose ${this.value}.`);
});
}✅ 优势:
document.querySelector('form[name="testForm"]').addEventListener('click', function(e) {
if (e.target.type === 'radio' && e.target.name === 'subject') {
alert(`You chose ${e.target.value}.`);
}
});✅ 优势:
⚠️ 注意事项:
总结:事件绑定发生在页面加载时(一次性执行循环),而事件响应发生在用户交互时(多次触发回调)。关键在于回调内部如何安全、准确地获取当前目标元素的数据——this 和 e.target 是最可靠的选择。