滚动时为下方出现背景图的#logo-icon添加.inverted类的实现问题
问题
我有一个固定元素#logo-icon,需要在用户滚动页面时,当任何带有背景图的div出现在该元素下方时,为其添加.inverted类,但当前编写的代码无法正常运行。
原实现代码
// Select the target element var logoIcon = document.querySelector('#logo-icon'); // Create a new Intersection Observer instance var observer = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { // Check if the target element is intersecting with any other element if (entry.isIntersecting) { // Check if the intersecting element or its children have a background image var intersectingElement = entry.target; var hasBackgroundImage = checkBackgroundImage(intersectingElement); if (hasBackgroundImage) { // Add the "inverted" class to the logoIcon element logoIcon.classList.add('inverted'); } else { // Remove the "inverted" class from the logoIcon element logoIcon.classList.remove('inverted'); } } }); }, { threshold: 0.5 }); // Start observing the logoIcon element observer.observe(logoIcon); // Function to check if an element or its children have a background image function checkBackgroundImage(element) { // Check if the element itself has a background image var computedStyle = window.getComputedStyle(element); if (computedStyle.getPropertyValue('background-image') !== 'none') { return true; } // Check if any child element has a background image var childElements = element.children; for (var i = 0; i < childElements.length; i++) { var childComputedStyle = window.getComputedStyle(childElements[i]); if (childComputedStyle.getPropertyValue('background-image') !== 'none') { return true; } } return false; }
问题根源
你的代码逻辑完全搞反了:
- 你现在监听的是
#logo-icon自身与视口的交叉状态,entry.target始终是#logo-icon,所以你一直在检查logo元素本身有没有背景图,而非出现在它下方的背景图元素。 - Intersection Observer默认监测目标元素和视口的交叉,无法直接实现"目标元素与其他页面元素交叉"的检测。
修复方案
要实现需求,需要反过来操作:监听页面中所有带背景图的元素,当它们出现在视口且位置处于logo下方时,切换logo的类。以下是修复后的代码:
var logoIcon = document.querySelector('#logo-icon'); // 获取页面中所有div,过滤出带有背景图的元素 var targetBgElements = Array.from(document.querySelectorAll('div')).filter(el => { const computedStyle = window.getComputedStyle(el); return computedStyle.backgroundImage !== 'none'; }); // 创建Intersection Observer实例 var observer = new IntersectionObserver(function(entries) { // 判断是否存在可见且处于logo下方的背景图元素 var hasBgBelowLogo = entries.some(entry => { if (!entry.isIntersecting) return false; const logoRect = logoIcon.getBoundingClientRect(); const elRect = entry.target.getBoundingClientRect(); // 元素顶部 >= logo底部,说明元素在logo下方 return elRect.top >= logoRect.bottom; }); logoIcon.classList.toggle('inverted', hasBgBelowLogo); }, { threshold: 0.1, // 元素进入视口10%即触发检测 rootMargin: '0px 0px -100% 0px' // 仅监测视口内的元素 }); // 开始监听所有符合条件的背景图元素 targetBgElements.forEach(el => observer.observe(el)); // 可选:窗口大小变化或动态添加元素时,重新更新监听列表 window.addEventListener('resize', () => { // 先取消原有监听 targetBgElements.forEach(el => observer.unobserve(el)); // 重新筛选元素 targetBgElements = Array.from(document.querySelectorAll('div')).filter(el => { const computedStyle = window.getComputedStyle(el); return computedStyle.backgroundImage !== 'none'; }); // 重新添加监听 targetBgElements.forEach(el => observer.observe(el)); });
补充说明
- 如果背景图元素是动态生成的,需要在元素添加后重复执行"筛选+监听"的逻辑
- 可调整
threshold和rootMargin参数,优化检测的灵敏度和范围 - 也可以给
#logo-icon下方创建一个固定位置的不可见占位元素(如高度1px、与logo同宽),直接监听该占位元素与背景图元素的交叉,逻辑会更简洁
内容的提问来源于stack exchange,提问作者lnvrt
相关产品推荐
相关产品推荐

