简体   繁体   中英

How to get all entries using Intersection Observer API?

I want to make left side navigation showing the current visible section. But my sections have different height and I can't figure out how to properly track them.

It would be better if my callback function every time it is invoked could see all entries and their state, but it gets entries that matches threshold values.

I probably missing something out. There should be a way to make this working.

Here is my 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>

It is little bit tricky, but it should work as you described. More info in javascript comments:

 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM