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

如何根据滚动位置匹配有序数组区间获取对应索引(支持任意长度)

优雅解决滚动位置匹配动态区间的问题

这个需求我之前做导航栏高亮的时候正好碰到过,完全不需要写一堆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的元素索引,这个索引就是当前区间的左边界。通过不断缩小查找范围,快速定位到目标位置。


关键细节说明

  1. 防抖处理:必须加!滚动事件每秒会触发几十次,不加防抖会导致频繁计算,影响页面性能。你可以根据需求调整防抖的延迟时间(比如50ms或100ms)。
  2. 兼容性:window.pageYOffset和document.documentElement.scrollTop是为了兼容不同浏览器获取滚动距离的方式。
  3. 动态数组适配:不管你的数组长度是3还是100,这两个方案都能直接用,完全不需要修改核心逻辑,只要把数组传进去就行。
  4. 特殊值处理:返回-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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:16:18