如何根据滚动位置匹配有序数组区间获取对应索引(支持任意长度)
优雅解决滚动位置匹配动态区间的问题
这个需求我之前做导航栏高亮的时候正好碰到过,完全不需要写一堆if-else,用遍历或二分查找就能轻松适配任意长度的有序数组,还能完美处理滚动回滚的场景。下面给你两种实现方案,按需选择:
核心逻辑梳理
先明确规则避免歧义:
- 滚动位置
scrollTop <= arrScroll[0]:返回特殊值(比如-1,代表未进入任何区间) - 滚动位置
arrScroll[i] < scrollTop <= arrScroll[i+1]:返回左边界索引i(i从0到arrScroll.length-2) - 滚动位置
scrollTop > arrScroll[arrScroll.length-1]:返回最后一个索引arrScroll.length-1 - 回滚时自动匹配当前区间的左边界索引(比如从200+回滚到100-200之间,自动切回索引0)
方案一:线性遍历(适合小数组,代码简单直观)
如果你的数组元素不多(比如几十条以内),线性遍历完全够用,代码可读性强:
// 防抖函数,避免滚动事件频繁触发计算 function debounce(func, delay = 100) { let timer; return function(...args) { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), delay); }; } // 获取当前滚动对应的左边界索引 function getCurrentScrollIndex(scrollTop, arrScroll) { // 处理未进入第一个区间的情况 if (scrollTop <= arrScroll[0]) return -1; // 从后往前找,找到第一个小于scrollTop的元素索引 for (let i = arrScroll.length - 1; i >= 0; i--) { if (scrollTop > arrScroll[i]) { return i; } } // 兜底(理论上不会走到这里,因为前面已经判断了scrollTop > arrScroll[0]) return -1; } // 绑定滚动事件 const arrScroll = [5, 10, 15, 50, 100, 250]; const colorArr = ['red', 'blue', 'green', 'yellow', 'purple', 'orange']; window.addEventListener('scroll', debounce(() => { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; const index = getCurrentScrollIndex(scrollTop, arrScroll); if (index === -1) { // 处理0到arrScroll[0]的特殊场景,比如重置样式 $(element).css('background-color', 'transparent'); } else { $(element).css('background-color', colorArr[index]); } }));
为什么从后往前遍历? 因为滚动位置通常是递增的,从后往前找能更快定位到目标区间,比从前往后遍历少几次循环。
方案二:二分查找(适合大数组,性能更优)
如果你的数组元素很多(比如上百条),二分查找的时间复杂度是O(logn),比线性遍历的O(n)高效太多:
// 防抖函数同上 function debounce(func, delay = 100) { let timer; return function(...args) { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), delay); }; } // 二分查找获取左边界索引 function getCurrentScrollIndex(scrollTop, arrScroll) { const len = arrScroll.length; if (scrollTop <= arrScroll[0]) return -1; if (scrollTop > arrScroll[len - 1]) return len - 1; let left = 0; let right = len - 1; let result = -1; while (left <= right) { const mid = Math.floor((left + right) / 2); if (arrScroll[mid] < scrollTop) { // 记录当前符合条件的索引,继续往右找更大的可能 result = mid; left = mid + 1; } else { // 当前mid的值大于等于scrollTop,往左找 right = mid - 1; } } return result; } // 滚动事件绑定同方案一 const arrScroll = [5, 10, 15, 50, 100, 250]; const colorArr = ['red', 'blue', 'green', 'yellow', 'purple', 'orange']; window.addEventListener('scroll', debounce(() => { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; const index = getCurrentScrollIndex(scrollTop, arrScroll); if (index === -1) { $(element).css('background-color', 'transparent'); } else { $(element).css('background-color', colorArr[index]); } }));
二分查找的逻辑:我们要找的是最大的那个小于scrollTop的元素索引,这个索引就是当前区间的左边界。通过不断缩小查找范围,快速定位到目标位置。
关键细节说明
- 防抖处理:必须加!滚动事件每秒会触发几十次,不加防抖会导致频繁计算,影响页面性能。你可以根据需求调整防抖的延迟时间(比如50ms或100ms)。
- 兼容性:
window.pageYOffset和document.documentElement.scrollTop是为了兼容不同浏览器获取滚动距离的方式。 - 动态数组适配:不管你的数组长度是3还是100,这两个方案都能直接用,完全不需要修改核心逻辑,只要把数组传进去就行。
- 特殊值处理:返回
-1代表未进入第一个区间,你可以根据自己的需求改成null或者其他标识,然后在滚动事件里做对应的处理。
比如你补充的颜色示例,把arrScroll改成[100,200,300],colorArr改成['red','blue','green'],直接就能用:
- 100 < scrollTop <=200 → 返回0 → 红色
- 200 < scrollTop <=300 → 返回1 → 蓝色
- scrollTop >300 → 返回2 → 绿色
- scrollTop <=100 → 返回-1 → 可以重置为默认颜色
内容的提问来源于stack exchange,提问作者AKor
相关产品推荐
相关产品推荐

