循环中向setTimeout传入选中元素失效,是否因querySelector速度问题?
问题原因与解决方案
你的代码没按预期工作,根本原因不是el.querySelector('.b')执行速度比setTimeout慢,而是变量作用域的问题:
你在forEach外部用var span声明了变量,var属于函数级作用域,整个forEach循环过程中只会存在这一个span变量。forEach是同步执行的,会在极短时间内完成三次循环,每次循环都会把span重新赋值为当前元素的.b节点。而setTimeout是异步任务,要等20ms后才执行回调,这时候span已经被最后一次循环赋值为第三个.b节点了,所以所有回调都会给最后一个<span>添加visible类。
修复方案
方案1:把span的声明放到循环内部(用let实现块级作用域)
将span的声明移到forEach的回调函数里,用let代替var,这样每次循环都会创建独立的span变量,回调函数会捕获当前循环的变量:
var ids = document.querySelectorAll('.a') ids.forEach(el => { let span = el.querySelector('.b') // 用let声明,形成块级作用域 setTimeout(function() { span.classList.add('visible'); }, 20); })
方案2:利用setTimeout的参数传递(你已经传参但未使用)
你已经把span作为setTimeout的第三个参数传进去了,只需要在回调函数里接收这个参数,替代外部的span变量即可:
var ids = document.querySelectorAll('.a'), span ids.forEach(el => { span = el.querySelector('.b') setTimeout(function(targetSpan) { // 接收传入的目标节点参数 targetSpan.classList.add('visible'); }, 20, span); // 传递当前span作为参数 })
方案3:使用箭头函数简化(利用词法作用域)
箭头函数会继承外部作用域,结合块级变量可以更简洁地解决问题:
var ids = document.querySelectorAll('.a') ids.forEach(el => { const span = el.querySelector('.b') setTimeout(() => span.classList.add('visible'), 20) })
内容的提问来源于stack exchange,提问作者Toniq
相关产品推荐
相关产品推荐

