【问题标题】:How to get all entries using Intersection Observer API?如何使用 Intersection Observer API 获取所有条目?
【发布时间】:2020-01-26 13:47:34
【问题描述】:

我想让左侧导航显示当前可见部分。但是我的部分有不同的高度,我不知道如何正确跟踪它们。

如果我的回调函数每次被调用时都能看到所有条目及其状态,但它会获取与阈值匹配的条目,那就更好了。

我可能遗漏了一些东西。应该有办法让它发挥作用。

这是我的 jsBin https://jsbin.com/homibef/edit?html,css,js,output

stickybits('#sticky', { stickyBitStickyOffset: 0 });

if (window.IntersectionObserver) {

      const callback = function(entries) {
			
        // Find all visible and then with biggest intersectionRatio
        let currentNav = null;
        const visibleEntries = entries.filter(entry => entry.isIntersecting);
        if ( Array.isArray(visibleEntries) && visibleEntries.length > 0 ) {
          currentNav = visibleEntries.reduce((prev, current) => {
            return (prev.intersectionRatio > current.intersectionRatio) ? prev : current;
          });
        } else {
          currentNav = visibleEntries;
        }

        if (currentNav.target) {
					// Handle navigation change
          const wasCurrent = document.querySelector('.navItem.isCurrent');
          if ( wasCurrent ) {
            wasCurrent.classList.remove('isCurrent');
          }
          const currentName = currentNav.target.getAttribute('name');
          const current =
            document.querySelector(`.navItem[data-link='${currentName}']`);
          current.classList.add('isCurrent');
        }

      };

      const observer = new IntersectionObserver(callback, {
        root: null,
        rootMargin: '0px',
        threshold: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
      });

      const section = document.querySelectorAll('section');
      section.forEach(item => observer.observe(item));
  }
* {
	margin: 0;
	padding: 0;
}

.row {
	width: 100%;
	display: flex;
	flex-wrap: nowrap;
}

.navigation {
	margin-right: 50px;
}

.navItem {
	color: #666;
}

.navItem.isCurrent {
	font-weight: bold;
	color: #000;
}

.content {
	width: 100%;
	flex: 1;
}

section {
	padding: 10px;
	width: 200px;
	border-radius: 10px;
	margin-bottom: 30px;
}

.section1 {
	height: 800px;
	background: #90ee90;
}

.section2 {
	height: 200px;
	background: #add8e6;
}

.section3 {
	height: 150px;
	background: #808080;
}

.section4 {
	height: 400px;
	background: #800080;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/stickybits/3.7.3/stickybits.min.js"></script>
</head>
<body>
	
	<div class="row">
		<!-- Navigation -->
		<div class="navigation">
			<div id="sticky" class="sticky">
				<ul class="nav">
					<li data-link="section1" class="navItem">Section 1</li>
					<li data-link="section2" class="navItem">Section 2</li>
					<li data-link="section3" class="navItem">Section 3</li>
					<li data-link="section4" class="navItem">Section 4</li>
				</ul>
			</div>
		</div>
		
		<!-- Content -->
		<div class="content">
			<section name="section1" class="section1"></section>
			<section name="section2" class="section2"></section>
			<section name="section3" class="section3"></section>
			<section name="section4" class="section4"></section>
		</div>
		
		
	</div>

</body>
</html>

【问题讨论】:

    标签: javascript intersection-observer


    【解决方案1】:

    这有点棘手,但它应该像你描述的那样工作。 javascript cmets 中的更多信息:

    stickybits('#sticky', { stickyBitStickyOffset: 0 });
    
    if (window.IntersectionObserver) {
          const section = document.querySelectorAll('section');
          const sectionArr = Array.from(section);
          const callback = function(entries) {
            // this is intialized for all targets
            // after that, only entries which pass threshold(any) in same viewport position(scroll)
            // thus it will be most likely one or two sections, not all
            for (entry of entries) {
              // setting properties on native Objects is ugly, but most straightforward
              // instead of intersectionRatio, we want intersectionRect to compare height
              // more tresholds => more precise behaviour
              // step 0.1 = 10%, 10% of 3000px height section = 300px => pretty large breakpoints
              entry.target._intersectionHeight = entry.intersectionRect.height;
            }
            
            // compare visibility of sections(all) after every intersection
            const mostVisibleSection = sectionArr.reduce((prev, current) => {
              if (current._intersectionHeight > (prev ? prev._intersectionHeight : 0)) {
                return current;
              } else {
                return prev;
              }
            }, null);
            
            // TIP: you can store this variable outside of callback instead of selecting
            const prevMostVisibleLink = document.querySelector('.isCurrent');
            
            // no section is visible
            if (!mostVisibleSection) {
              prevMostVisibleLink && prevMostVisibleLink.classList.remove('isCurrent');
              return;
            }
            
            // ok, there is most visible section, lets target link
            const mostVisibleLink = document.getElementById(mostVisibleSection.dataset.id);
              
            if (mostVisibleLink !== prevMostVisibleLink) {
              prevMostVisibleLink && prevMostVisibleLink.classList.remove('isCurrent');
              mostVisibleLink.classList.add('isCurrent');          
            }
          };
          
          // zero covers also entries comming out of viewport
          const observer = new IntersectionObserver(callback, {
            threshold: [0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1],
          });
    
          section.forEach(item => observer.observe(item));
    }
    * {
    	margin: 0;
    	padding: 0;
    }
    
    .row {
    	width: 100%;
    	display: flex;
    	flex-wrap: nowrap;
    }
    
    .navigation {
    	margin-right: 50px;
    }
    
    .navItem {
    	color: #666;
    }
    
    .navItem.isCurrent {
    	font-weight: bold;
    	color: #000;
    }
    
    .content {
    	width: 100%;
    	flex: 1;
    }
    
    section {
    	padding: 10px;
    	width: 200px;
    	border-radius: 10px;
    	margin-bottom: 30px;
    }
    
    .section1 {
    	height: 800px;
    	background: #90ee90;
    }
    
    .section2 {
    	height: 200px;
    	background: #add8e6;
    }
    
    .section3 {
    	height: 150px;
    	background: #808080;
    }
    
    .section4 {
    	height: 400px;
    	background: #800080;
    }
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    	<script src="https://cdnjs.cloudflare.com/ajax/libs/stickybits/3.7.3/stickybits.min.js"></script>
    </head>
    <body>
    	
    	<div class="row">
    		<!-- Navigation -->
    		<div class="navigation">
    			<div id="sticky" class="sticky">
    				<ul class="nav">
    					<li id="section1" class="navItem">Section 1</li>
    					<li id="section2" class="navItem">Section 2</li>
    					<li id="section3" class="navItem">Section 3</li>
    					<li id="section4" class="navItem">Section 4</li>
    				</ul>
    			</div>
    		</div>
    		
    		<!-- Content -->
    		<div class="content">
    			<section name="section1" class="section1" data-id="section1"></section>
    			<section name="section2" class="section2" data-id="section2"></section>
    			<section name="section3" class="section3" data-id="section3"></section>
    			<section name="section4" class="section4" data-id="section4"></section>
    		</div>
    		
    		
    	</div>
    
    </body>
    </html>

    【讨论】:

    • 这个答案可以简化,但它是很好的起点。还要注意,如果你的部分足够小,最后一个永远不会被选为 mostVisible 并且使用比率而不是高度实际上更好,如果你使用高度并且你有两个完全可见的部分并且一个比另一个高,将选择最高的,而使用比率时,将选择最高的
    猜你喜欢
    • 1970-01-01
    • 2022-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多