To consider I know how to make a sticky table of contents. On which I need help is how to display the say 5 items up and 5 item down next to the current active item and that scrolling up or down updates accordingly active and up and down items.
Example of base TOC
So if I have scrolled to " City Detail" it should only 5 items above it:
-Request & Response
-Authentication
-Endpoints
-Root
-Cities Overview
then it wil go " City Detail"
and below the other 5 items
-City Config
-City Spots Overview
-City Spot Detail
-City Icons Overview
-City Icon Detail
Meaning all the other elements should be hidden at that scroll position.
If you want these nav links to show only a max of 11 (5 above, + 1 active + 5 below), you could continue through the use of intersection observer + css styles.
To give you an idea, you could assign certain classes to 5 above and 5 below you currently intersected element.
By the way, good use of intersection observer! First time I’ve seen it used directly (without an external library).
You could use indexing but I know that intersectionObserver has an entries array and the entry has a bunch of properties that may be of use and give the ones not within your range a display of none (when not near the intersecting element)
I dont know honestly how to count 5 item with intersection obsever, so I will resort to previous/next siblings i guess, is like it only takes into account the area…
Also sometimes more than one item in the table of contents is active, i would like to restrict that to only one…
you could get the array of all intersection records then filter out the array so you get items closes to your specified index.
More than one item is active because multiple items are intersecting. Right now it is checking what is in your viewport as that’s the rootMargin by default.
You can use the rootMargin when initially setting up Intersection Observer to essentially say what part of the viewport you want to consider intersecting.
MDN has good docs on it
Intersecting ratio may not be ideal IF the section is greater than the viewport (you’ll won’t intersect). You’ll need to know these details ahead of time since you’re putting the intersection on the section element which has a bunch of padding applied. In the IntersectionObserverEntry you can see the intersectionRatio. You wrote that if it is greater than 0, you give the active class.
You could put the observer on the headings itself but then that can also be problematic if you’re not on that section (if you are on Authentication, and scroll up it’s not going to intersect until you get to the Request heading).
Thank you!!!
I found a pen that has only one item at a time with intersection observer. Problem is I don’t fully grasp it, it has an additional conditional for an entry intersecting… relevant code below:
if (entry.isIntersecting) {
if (currentY > previousY && index !== 0) {
console.log(id + ":1 enter top");
} else {
console.log(id + ":2 enter bottom");
setLinkActive(link);
}
} else {
if (currentY > previousY) {
console.log(id + ":3 leave bottom");
const lastLink = document.querySelector(`#${headingIds[index - 1]}-link`);
setLinkActive(lastLink);
} else {
console.log(id + ":4 leave top");
}
}
And complete source here:
Also ideally I would like to replicate MDN’s " In this article" behavior
These two observers (codepen vs mozilla) are implemented differently.
If you notice, in the codepen, it is making the last intersecting element the active one. The if condition is checking the y values on the entry
Mozilla’s is different, you’ll notice that if you go to a heading, then scroll up a little, it will change the active section even though that heading isn’t visible in the viewport.
I see then I should go for Mozilla’s one, but just in case I explain: of all the intersecting elements I intend to only select the top most that’s the idea at least
btw sorry for the delay… and thanks
Ok I’m officially lost, I don’t know how to select only the top most of all the intersecting elements in JavaScript… If I use an array it only works when scrolling down, but scrolling up messes the order. Help is appreciated…
Update: this works but selects the last element instead of the top most/first
if (entry.isIntersecting) {
document.querySelectorAll(".text-danger").forEach((z) => {
z.classList.remove("text-danger")
});
itemTabla.classList.add("text-danger");
}