无依赖自定义轮播类currentItem变量出现NaN问题求助
轮播组件点击切换时变量出现NaN的原因及解决方法
问题背景
实现不依赖第三方库的简易轮播组件,定义了Carousel类,但点击切换按钮时相关值变为NaN。核心代码、日志及DOM结构如下:
核心代码
class Carousel { /** * @param {HTMLElement} element * @param {Object} options.slidesToScroll Number of elements to slide * @param {Object} options.slidesVisible Number of elements to display */ constructor (element, options = {}) { this.element = element this.options = Object.assign({}, { slidesToScroll: 1, slidesVisible: 1 }, options) let children = [].slice.call(element.children) let currentItem = 0 // 问题根源所在 this.root = this.createDivWithClass('carousel') this.container = this.createDivWithClass('carousel-container') this.root.appendChild(this.container) this.element.appendChild(this.root) this.items = children.map((child) => { let item = this.createDivWithClass('carousel-item') item.appendChild(child) this.container.appendChild(item) return item }); this.setStyle() this.createNavigation() } setStyle () { let ratio = this.items.length / this.options.slidesVisible this.container.style.width = (ratio * 100) + "%" this.items.forEach(item => item.style.width = ((100 / this.options.slidesVisible) / ratio) + "%") } createNavigation () { let nextButton = this.createDivWithClass('carousel-next') let prevButton = this.createDivWithClass('carousel-prev') this.root.appendChild(nextButton) this.root.appendChild(prevButton) nextButton.addEventListener('click', this.next.bind(this)) prevButton.addEventListener('click', this.prev.bind(this)) } next () { this.goToItem(this.currentItem + this.options.slidesToScroll) } prev () { this.goToItem(this.currentItem - this.options.slidesToScroll) } goToItem (index) { this.currentItem = index let translateX = index * (-100 / this.items.length) this.container.style.transform = 'translate3d(' + translateX + '%, 0, 0)' console.log("translateX", translateX); console.log("currentItem", this.currentItem); console.log("typeof(currentItem)", typeof(this.currentItem)); console.log("typeof(index)", typeof(index)); console.log("index", index); } createDivWithClass (className) { let div = document.createElement('div') div.setAttribute('class', className) return div } } document.addEventListener('DOMContentLoaded', function () { new Carousel(document.querySelector('#carousel1'), { slidesVisible: 3, slidesToScroll: 1 }) })
日志输出
translateX NaN currentItem NaN typeof(currentItem) number typeof(index) number index NaN
生成的DOM结构
<div id="carousel1"> <div class="carousel"> <div class="carousel-container" style="width: 166.667%"> <div class="carousel-item" style="width: 20%"></div> <div class="carousel-item" style="width: 20%"></div> <div class="carousel-item" style="width: 20%"></div> <div class="carousel-item" style="width: 20%"></div> <div class="carousel-item" style="width: 20%"></div> </div> <div class="carousel-next"></div> <div class="carousel-prev"></div> </div> </div>
手动给index赋值时轮播可正常运行,说明轮播核心逻辑无问题,问题出在索引变量的访问机制上。
原因分析
构造函数中用let currentItem = 0定义的是局部变量,仅在构造函数内部有效,无法通过this.currentItem在类的其他方法中访问。点击按钮调用next()或prev()时,this.currentItem的值为undefined,undefined与数字进行加减运算会得到NaN,最终导致translateX也变成NaN。
解决方法
1. 将currentItem改为实例属性
把构造函数中的局部变量currentItem改为类的实例属性,让所有类方法都能通过this.currentItem访问和修改它:
// 构造函数中修改这一行 this.currentItem = 0
2. 补充索引边界判断
为防止索引越界(比如小于0或超过最大可滑动索引),在goToItem方法中添加边界处理:
goToItem (index) { // 计算最大允许的索引:总项数 - 可见项数 const maxIndex = this.items.length - this.options.slidesVisible; // 限制index在0到maxIndex之间 this.currentItem = Math.max(0, Math.min(index, maxIndex)); let translateX = this.currentItem * (-100 / this.items.length); this.container.style.transform = `translate3d(${translateX}%, 0, 0)`; }
修改后的完整代码
class Carousel { /** * @param {HTMLElement} element * @param {Object} options.slidesToScroll Number of elements to slide * @param {Object} options.slidesVisible Number of elements to display */ constructor (element, options = {}) { this.element = element; this.options = Object.assign({}, { slidesToScroll: 1, slidesVisible: 1 }, options); let children = [].slice.call(element.children); // 改为实例属性 this.currentItem = 0; this.root = this.createDivWithClass('carousel'); this.container = this.createDivWithClass('carousel-container'); this.root.appendChild(this.container); this.element.appendChild(this.root); this.items = children.map((child) => { let item = this.createDivWithClass('carousel-item'); item.appendChild(child); this.container.appendChild(item); return item; }); this.setStyle(); this.createNavigation(); } setStyle () { let ratio = this.items.length / this.options.slidesVisible; this.container.style.width = `${ratio * 100}%`; this.items.forEach(item => item.style.width = `${((100 / this.options.slidesVisible) / ratio)}%`); } createNavigation () { let nextButton = this.createDivWithClass('carousel-next'); let prevButton = this.createDivWithClass('carousel-prev'); this.root.appendChild(nextButton); this.root.appendChild(prevButton); nextButton.addEventListener('click', this.next.bind(this)); prevButton.addEventListener('click', this.prev.bind(this)); } next () { this.goToItem(this.currentItem + this.options.slidesToScroll); } prev () { this.goToItem(this.currentItem - this.options.slidesToScroll); } goToItem (index) { const maxIndex = this.items.length - this.options.slidesVisible; this.currentItem = Math.max(0, Math.min(index, maxIndex)); let translateX = this.currentItem * (-100 / this.items.length); this.container.style.transform = `translate3d(${translateX}%, 0, 0)`; } createDivWithClass (className) { let div = document.createElement('div'); div.setAttribute('class', className); return div; } } document.addEventListener('DOMContentLoaded', function () { new Carousel(document.querySelector('#carousel1'), { slidesVisible: 3, slidesToScroll: 1 }); });
效果验证
修改后点击切换按钮,currentItem会正确递增/递减,translateX计算正常,轮播功能可正常运行,不会再出现NaN的情况。
内容的提问来源于stack exchange,提问作者kevkevkev
相关产品推荐
相关产品推荐

