Intersection observer firing
I have this code:
For the following webpage: https://myntsu.github.io/e-commerce-sample/landing-page/
It targets the navbar. Is there anyway to make it fire once it reaches
#Products
and stays like that going downwards? And to keep the class removal when going back.
Currently it removes it when entering and leaving that section, with a margin of -300px (both up and down).
I tried many options, but no luck.47 Replies
It might be easier to not use intersectionobserver in this case. Add an event listener to the window on scroll, and compare the current window.scrollY position with the products container position. If it's less than (you are above), remove class and otherwise (you are below) add it.
Usually with scroll events you want to apply some throttle function as they fire many times with each scroll so it can cause performance issues.
You could use two observers, one for the production and one for the section under the production.
And use only if condition without the else.
Something like this:
Sorry if it's a little messy.. writing on my phone 😅
Two observers... I don't really like this approach for something this simple 🫤
This could cause another issue, I have more sections than this 2.
If the section intersects it will stay like this until you scroll back up to the product section
You're only using if, no else
I thought of that approach initially, but perceived as inefficient, specially with other observers for the animations 🤔
How do you throttle events?
I will do the on scroll function, then can I post it here for you to review it?
Idk i feel like these extra couple of lines won't hurt 😬
I'm delivering this as a professional page for a client. Wouldn't like to have repeated code for simpler solutions, that would look bad on me, at least don't want to do it in this case here I would throttle the on scroll, correct?
Inside
someFn
.You provide it with a function and that will return another function that even when called repeatedly, will only run every
timeInMs
miliseconds
Yes, on the scroll event.
If you just try quickly yourself with a simple console.log, you will see that it will call your function hundreds of times with a little scrolling
Normally not an issue, and with your example probably won't cause any issues either. But just in case it's good practice. Specially if you plan on having similar events for other sections of the pageCurrently the other events have
unobserve
, assuming that would help with performance.Yes, definitely
In this case though you want to monitor the position constantly so that is not an option. I would just use this as it's effective, performant and simple.
I questioned it at first, based on Kevin's video.
Kevin Powell
YouTube
How to change your navigation style on scroll
This video explores using the Intersection Observer API to watch for an element leaving the page and then changing the style of a fixed navigation bar.
GitHub repo: https://github.com/kevin-powell/navbar-change-on-scroll
(includes start and finished versions)
I set up some custom properties ahead of time to make the change really super simple,...
He scrolls down, and it fires only once, and removes it going back up.
But he never went further down, so I was questioning if I was doing something wrong or if they changed how the function works.
You can also if you prefer, add a check for the same thing (window position vs element position) inside the intersection observer callback, when it's intersecting
Kevin's observer is observing the home section. Nothing above this section only beneath it.
Your observer is observing a section between two sections.
So whenever you scroll above this "product" section the nav will change, Same as scrolling down.
If there's a section above the home section In Kevin's code, then the same of what's happening to your Product section will happen to his home section.
Observer fires once the target is in viewport, or once it reaches the margin you set it to.
You will need to do extra work than using an intersection observer to control your nav appearance.
Either what Joao suggested or what I did or what others might suggest later.
In his demo the event fires when the top of the screen reaches the top of the observed element, right?
In his demo he's using:
He's setting his condition to if the home section isn't in viewport then the nav appearance will change.
The ! In the entery.isIntersecting means if not intersecting.
Yeah, but notice it that it has to be in 100% of the viewport to fire.
Contrary to mine, as soon as one pixel sees it, it fires.
That's what confuses me 😓
You can use "threshold" it sets how much of the target element have to be in the viewport in order for the event to fire.
Like 20% (or 50% or whatever number you wish) of the element appears in the viewport then the event will fire.
But it has nothing to do with your case.
Yeah, either threshold or root margin, but still doesn't answer my question his behavior is different than mine.
He doesn't have any value to start with, and it fires as soon as the top of his screen reaches the beginning of that section.
I feel like there's something weird with mine, definitely.
Look, your observer is looking at the product section and once it appears in the viewport then the nav appearance will change. And once it leaves the nav will change back. This works both sides weather up or down from it.
You want your nav to change only once the product section appears and stays like that even if you scrolled beneath it, right?
I changed it to the main tag, but it still follows the same behavior.
There's definitely something weird.
If I set a root higher than 300, it won't work.
Or a threshold higher than .2, same as well.
And root accepts a margin not higher than the element itself (in pixels), or a % value, as for threshold, it accepts from 0 to 1.
Can you add the code in a codepen?
What exactly?
Does this work like you mean?
https://codepen.io/D10f/pen/VwdPEVM
Actually nevermind.. I'll make a quick one
Well nevermind again lol
Joao did it 🙌
Like I said: effective, performant and simple 😄
I like it 👌
Oh, so the throttle is within the event listener itself.
I was actually doing the window on scroll, but my stubborn head wanted to make the observer work 😔
I would recommend storing it as a separate function, I just did it like that in the codepen for simplicity
I still don't understand why the behavior was different.
May I use this code instead?
You must pay me many 💰
The only difference from what I had is the throttle, and bounding (was calculating from the top to the element ).
I will just do my own then, thanks for the help.
Darn, worth a try anyway 😂
Regardless, if anyone else pops in here, please help me understanding the observer behavior, that I clearly don't perceive to understand with my use case.
I think the confusion is because the IntersectionObserver uses the viewport to start detecting intersections. It doesn't check based on element positions, which is what we manually do in the codepen example. That's why you can actually access this same information from within the entry object, inside the callback.
The name is a bit misleading, it doesn't actually observe for intersecting elements. It observes for elements that are visible.
Which is to say, elements that intersect with the viewport.
Yeah, I understand that part, but that's not when the confusion arises.
In my case, it fires as soon as the viewport touches the element.
In Kevin's case, it fires as soon as the top of the element reaches the top of the viewport.
While we both had similar code.
I figured there was something weird, because, in example, the threshold wasn't accepting a value from 0 to 1, just 0 to 0.2, that's why I was so confused
There's a nuance here with this, although I'm not 100% sure right now, I will have to confirm it later. The thing is that the callback function is invoked when both the element enters and leaves the viewport. Since the example in the video uses
if ( ! entry.isIntersecting )
, it gives the impression that when the page first loads nothing happens. In fact it does run, but no visible change happens.Hmm, I tried it as well, also swapping the remove and add, or with toggle.
In theory, he has a color theme (which targets the root variables), and swaps it, exactly what I was doing.
But in my case I just replaced the background color from transparent to blue.
Ah I see, in the example the observer is observing the very first section. So when it leaves the viewport it fires the callback
I've updated this to include a version with intersection observer:
https://codepen.io/D10f/pen/VwdPEVM
Notice how in the scroll event example I'm comparing the coordinates of the
s2
element which is what we're actually after. It's a more literal approach. In the intersection observer example I'm using, just like in Kevin's video, the s1
element which is already within view to start with. When it leaves the viewport the callback fires. So it's not intersecting the element we actually care about, s2
, it's simply leaving the section above. This is also the reason why the navbar doesn't change back when s2
leaves the viewport.So it would work even without the
const
for s2
.
Now the question is, which approach would be better.
To observe it, or based on windows Yes, I left that there for the example below (commented out).
Personally, I prefer window scroll event. It's very simple to do and gives you more control.
In this case it does, but now I understand my fault.
I use IntersectionObserver when I need to interact with both enter and leave events. Or when I need to load something before reaching a particular element. For example I've used this to load images, and to start/stop videos loopbacks so that they won't play needlessly when they are not being watched (since they are out of viewport)
I was treating it from the incorrect approach. The event fires accordingly and appropriately, but the first element was always visible on screen.
So it was doing a reverse effect, when it reality, if it had more content above, it would have done it like my use case.
That makes it more clear, and now I understand it better, thank you for the help though. Very insightful, if I had pennies to spare, wouldn't doubt compensating for it. Keep at it though, cheers!