使用for循环初始化Splide Slider时仅最后一个滑块进度条可更新如何解决?
问题原因
这是JavaScript中var声明的变量没有块级作用域导致的经典循环绑定问题:
- 你用
var声明的splide、splideClassname等变量不存在块级作用域隔离,每次循环都会覆盖上一次循环的变量值 - 你绑定的
mounted move事件是异步触发的,事件触发时整个循环已经执行完毕,所有事件回调拿到的都是最后一次循环的变量值,因此只会更新最后一个滑块的进度条
额外逻辑问题:你原代码中通过
splideClassname拼接选择器的逻辑本身就有问题,所有滑块容器的首个类名都是splide-slider,拼接出来的选择器.splide-slider .my-slider-progress-bar会全局查询,默认命中第一个匹配的进度条,就算变量作用域问题修复了也会出现进度条对应错误。
修复方案
推荐用以下两种方案修复,第二种适配性更强、逻辑更简洁:
方案1:替换var为let声明变量
ES6新增的let支持块级作用域,每次循环都会生成独立的变量实例,不会被后续循环覆盖:
var splides = document.querySelectorAll('.splide-slider'); if(splides.length){ // 循环变量i改用let声明 for(let i=0; i<splides.length; i++){ // 循环内部变量全部改用let声明 let splideElement = splides[i]; let splideDefaultOptions = { lazyLoad: 'nearby', preloadPages: 2, autoWidth: true, wheel: true, arrows: true, pagination: false, waitForTransition: false, perPage: 1, } let splide = new Splide( splideElement, splideDefaultOptions ); splide.on( 'mounted move', function () { // 直接从当前Splide实例的容器下查询进度条,不用拼接选择器 let bar = splide.root.querySelector('.my-slider-progress-bar'); let end = splide.Components.Controller.getEnd() + 1; bar.style.width = String( 100 * ( splide.index + 1 ) / end ) + '%'; } ); splide.mount(); } }
方案2:利用Splide事件回调的this指向优化(无需依赖ES6块级作用域)
Splide的事件回调中this默认指向当前触发事件的Splide实例,不需要依赖外部变量,兼容更低版本的JS运行环境:
var splides = document.querySelectorAll('.splide-slider'); if(splides.length){ // 公共配置提到循环外,避免重复生成对象 var splideDefaultOptions = { lazyLoad: 'nearby', preloadPages: 2, autoWidth: true, wheel: true, arrows: true, pagination: false, waitForTransition: false, perPage: 1, } // 用forEach遍历避免循环变量作用域问题 splides.forEach(function(splideElement) { var splide = new Splide( splideElement, splideDefaultOptions ); splide.on( 'mounted move', function () { // this直接指向当前触发事件的Splide实例 var bar = this.root.querySelector('.my-slider-progress-bar'); var end = this.Components.Controller.getEnd() + 1; bar.style.width = String( 100 * ( this.index + 1 ) / end ) + '%'; } ); splide.mount(); }) }
内容的提问来源于stack exchange,提问作者Pelle Skogsberg
相关产品推荐
相关产品推荐

