You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无依赖自定义轮播类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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 17:18:00