JS中getAttribute()调用报TypeError错误的原因与解决方法
报错原因
核心问题是var声明的变量不存在块级作用域,只有全局/函数级作用域,会导致循环变量被所有事件回调共享:
- 你在for循环里用
var i声明计数器,整个循环过程中全局环境下始终只有一个i变量,所有a标签的点击回调里引用的都是这同一个全局i。 - 当循环条件为
i < len时,循环结束后i的值会累加到等于len,但querySelectorAll返回的链接列表下标范围是0到len-1,allLists[len]是不存在的,值为undefined。这时候点击任意链接触发回调,执行allLists[i].getAttribute()就相当于在undefined上调用方法,直接抛出你看到的类型错误。 - 当你把循环条件改成
i < len-1时,首先循环只会给前len-1个链接绑定事件,最后一个链接根本没绑事件,点了自然没反应;其次循环结束时全局i的值固定为len-1,刚好是最后一个链接的下标。如果你测试时最后一个链接的href恰好是#,点击前len-1个链接时回调读到的是最后一个链接的href,会触发回到顶部逻辑,你就会误以为前序链接运行正常——本质上这时候逻辑也是错的,你点任何一个前序链接,拿到的都不是当前点击链接的href,只要最后一个链接的href不是#,前序链接的交互就会全部异常。
修复方案
任选一种即可,优先选前两种更简洁的写法:
- 改循环变量声明:把
var i换成let i。let是块级作用域,每一轮循环都会生成独立的i绑定,每个回调都能正确捕获到对应轮次的i值,不会出现变量污染。
修正后的完整代码:const allLists = document.querySelectorAll("a:link"); const len = allLists.length; for (let i = 0; i < len; i++) { allLists[i].addEventListener("click", function (e) { e.preventDefault(); const href = allLists[i].getAttribute("href"); console.log(href); if (href == "#") { window.scrollTo({ top: 0, behavior: "smooth", }); } }); } - 不依赖循环变量:直接从事件对象上取当前触发事件的元素。回调里的
e.currentTarget永远指向绑定了当前点击事件的DOM元素,完全不受循环变量作用域影响,哪怕保留var i的写法也不会出问题。
只需要修改取href的一行代码即可:// 替换原代码中const href = allLists[i].getAttribute("href"); const href = e.currentTarget.getAttribute("href"); - ES5兼容旧写法(无特殊兼容需求不推荐):通过立即执行函数给每轮循环创建独立作用域,把当前轮次的i作为参数传入闭包,隔离全局i的影响。
内容的提问来源于stack exchange,提问作者user19458229
相关产品推荐
相关产品推荐

