纯JS循环中无法移除前元素finished类的问题排查
问题描述
我在滚动事件中编写了如下纯JavaScript代码:
stickySliderItems.forEach( function( item, i ) { if(i) { // exclude first let meScrollStart = scroll - sliderWrapperT - ( i * sliderItemH ); let meScroll = _clamp( meScrollStart, min, max); let meShift = (i === sliderItemNum) ? maxS : meScroll.map(min, max, minS, maxS); // except last one item.style.transform = "translate(" + meShift + "px," + meScroll + "px)"; if ( meScroll >= max - fadeMargin && !item.classList.contains("finished") ) { item.classList.add("finished"); stickySliderItems[i-1].classList.remove("finished"); }; // can’t get this to work backwards }; });
我期望在为元素添加“finished”类的同时,移除前一个兄弟元素的该类,但尝试previousSibling等方法均无效。此前实现相同功能的jQuery代码可正常运行,请问我的思路哪里出错了?
附可正常工作的jQuery代码:
$stickySliderItem.each( function( i ) { if(i) { // exclude first var $this = $(this); let meScrollStart = scroll - sliderWrapperT - ( i * sliderItemH ); let meScroll = clamp( meScrollStart, min, max); let meShift = (i === sliderItemNum) ? maxS : meScroll.map(min, max, minS, maxS); // except last one let cssTransform = "translate(" + meShift + "px," + meScroll + "px);"; $this.attr("style", "transform:" + cssTransform + " -webkit-transform:" + cssTransform) .toggleClass("finished", meScroll >= max - fadeMargin); }; });
问题分析与解决方案
你的核心问题是只处理了“添加类”的正向逻辑,完全没覆盖“滚动回退时移除当前类、恢复前一个类”的反向逻辑,而jQuery的toggleClass会自动根据条件双向切换类状态。
具体问题点:
- 原生JS代码仅在
meScroll >= max - fadeMargin且元素无finished类时,执行添加类+移除前一个类的操作;但当滚动回退、meScroll < max - fadeMargin时,没有任何代码处理类的移除,也不会恢复前一个元素的finished类。 - 你尝试的
previousSibling无效,大概率是因为DOM中存在空白文本节点(换行、空格等),previousSibling会选中这些非元素节点;而jQuery的prev()会自动过滤空白节点,直接定位到前一个兄弟元素。
修正方案
方案1:实现双向类切换逻辑(模仿jQuery的toggleClass)
stickySliderItems.forEach( function( item, i ) { if(i) { // exclude first let meScrollStart = scroll - sliderWrapperT - ( i * sliderItemH ); let meScroll = _clamp( meScrollStart, min, max); let meShift = (i === sliderItemNum) ? maxS : meScroll.map(min, max, minS, maxS); // except last one item.style.transform = "translate(" + meShift + "px," + meScroll + "px)"; const shouldBeFinished = meScroll >= max - fadeMargin; // 处理当前元素的类切换 if (shouldBeFinished) { if (!item.classList.contains("finished")) { item.classList.add("finished"); // 通过数组索引取前一个元素,比DOM兄弟关系更可靠 if (stickySliderItems[i-1]) { stickySliderItems[i-1].classList.remove("finished"); } } } else { if (item.classList.contains("finished")) { item.classList.remove("finished"); // 滚动回退时,检查并恢复前一个元素的finished类 const prevItem = stickySliderItems[i-1]; if (prevItem) { const prevScrollStart = scroll - sliderWrapperT - ((i-1) * sliderItemH); const prevScroll = _clamp(prevScrollStart, min, max); if (prevScroll >= max - fadeMargin) { prevItem.classList.add("finished"); } } } } }; });
方案2:用正确的DOM兄弟节点API
如果一定要依赖DOM节点的兄弟关系,不要用previousSibling,改用previousElementSibling——它会直接选中前一个元素节点,自动忽略空白文本节点:
// 替换原代码中的stickySliderItems[i-1]为: const prevItem = item.previousElementSibling; if (prevItem) { prevItem.classList.remove("finished"); }
内容的提问来源于stack exchange,提问作者Garavani
相关产品推荐
相关产品推荐

