如何让IntersectionObserver仅在屏幕宽度大于1000px时运行并支持窗口resize
实现方案
你现有代码的问题是只在初始化时判断了一次窗口宽度,没有监听窗口尺寸变化的事件,所以调整窗口大小时逻辑不会同步更新。下面是两种主流可直接落地的实现方案:
方案1:使用matchMedia(推荐最优方案)
matchMedia是原生专门用来处理CSS媒体查询匹配的API,性能远高于监听resize事件,是当前实现响应式断点逻辑的首选方案。
你可以直接复用下面的改造后代码:
if ( 'IntersectionObserver' in window && !document.documentElement.classList.contains('dont-sticky') ) { const body = document.body; const sentinel = document.getElementById('sentinel'); const stickyEl = document.querySelector('.test'); const stickyClass = "sticky"; let observer = null; // 定义1000px宽度的媒体查询规则 const mq = window.matchMedia('(min-width: 1000px)'); // IntersectionObserver的处理逻辑 const intersectionHandler = (entries) => { if (!stickyEl) return; entries[0].isIntersecting ? body.classList.remove(stickyClass) : body.classList.add(stickyClass); }; // 激活observer的方法 const enableObserver = () => { if (!observer && sentinel) { observer = new IntersectionObserver(intersectionHandler); observer.observe(sentinel); } }; // 停用observer的方法,同时清理已经添加的sticky类 const disableObserver = () => { if (observer) { observer.disconnect(); observer = null; } body.classList.remove(stickyClass); }; // 媒体查询匹配状态变化的处理函数 const mqChangeHandler = (e) => { // 宽度大于等于1000px时启用逻辑,否则停用 e.matches ? enableObserver() : disableObserver(); }; // 兼容新旧浏览器的事件绑定写法 if (mq.addEventListener) { mq.addEventListener('change', mqChangeHandler); } else { mq.addListener(mqChangeHandler); } // 页面初始化时先判断一次当前宽度是否符合要求 mq.matches ? enableObserver() : disableObserver(); }
这个方案的优势:
- 只有当窗口宽度跨越1000px阈值时才会执行逻辑,不会像resize事件一样每次窗口变动都触发,性能极佳
- 和CSS媒体查询规则完全对齐,避免前后端断点不一致的问题
- 自动处理窗口调整的场景,小于1000px时会自动清理sticky类和Observer实例,不会有多余的性能损耗
方案2:resize事件+防抖(兼容旧场景方案)
如果需要兼容非常老的浏览器(比如IE10及以下),可以用传统的resize事件加防抖的方式实现:
if ( 'IntersectionObserver' in window && !document.documentElement.classList.contains('dont-sticky') ) { const body = document.body; const sentinel = document.getElementById('sentinel'); const stickyEl = document.querySelector('.test'); const stickyClass = "sticky"; let observer = null; let resizeTimer = null; // 防抖定时器,避免频繁触发逻辑 const intersectionHandler = (entries) => { if (!stickyEl) return; entries[0].isIntersecting ? body.classList.remove(stickyClass) : body.classList.add(stickyClass); }; const updateStickyStatus = () => { const isWideEnough = window.innerWidth >= 1000; if (isWideEnough && !observer && sentinel) { observer = new IntersectionObserver(intersectionHandler); observer.observe(sentinel); } else if (!isWideEnough && observer) { observer.disconnect(); observer = null; body.classList.remove(stickyClass); } }; // 监听resize事件 window.addEventListener('resize', () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(updateStickyStatus, 100); }); // 初始化执行一次 updateStickyStatus(); }
注意这个方案因为resize事件触发频率非常高,所以必须加防抖逻辑,否则会造成页面卡顿。
关于ResizeObserver的说明
ResizeObserver主要是用来监听DOM元素的尺寸变化的,如果你要监听的是窗口大小变化,用上面两个方案更合适,ResizeObserver不属于这个场景的最优解。
内容的提问来源于stack exchange,提问作者user3643520
相关产品推荐
相关产品推荐

