#Intersection Observer not working properly

24 messages ยท Page 1 of 1 (latest)

cunning sun
#

What I am trying to achieve is to check the collision between two elements. I got a page h1 tag, and want to check if it turns invisible because it's hidden behind the very top header when scrolling. The header is defined in my layout.tsx file with a component, which contains a simple header-tag.
With the current attempt I get the visible message when refreshing the page (and it's not colliding by the start), then scrolling so it hides and I get the invisible message. After these two console messages, it doesn't work at all anymore.
This is what I currently got:

  const titleElement = useSignal<HTMLHeadingElement | undefined>(undefined);
  const visibility = useSignal<boolean>(false); // Track previous visibility

  useVisibleTask$(() => {
    const options = {
      root: null
    };
    
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        const isVisible = entry.intersectionRatio > 0 && entry.boundingClientRect.top >= 0;

        // Check if visibility state has changed
        if (isVisible !== visibility.value) {
          visibility.value = isVisible;

          if (isVisible) {
            console.log("Title element is fully visible.");
            // Perform actions when the title element is fully visible
          } else {
            console.log("Title element is fully invisible.");
            // Perform actions when the title element is fully invisible
          }
        }
      });
    }, options);

    if (titleElement.value) {
      observer.observe(titleElement.value);
      return () => {
        observer.disconnect();
      };
    }
  })

  return (
    <div class="p-16">
      <h1 ref={titleElement} class="text-[4rem] text-indigo-300 font-bold text-center">Basics of nutrition</h1>
    </div>
  )
harsh wave
#

try using cleanup instead of return inside the useVisibleTask. Keep in mind this needs to be destructured from the task function like so

useTask$(({ cleanup }) => {
    cleanup(() => observer.disconnect());
 });
cunning sun
#

@harsh wave I also want to render a text inside the header in case the title is invisible because of scrolling... The only way I can think of getting this done is working with localStorage, what do you think?

harsh wave
#

Also did it work with the cleanup? ๐Ÿ˜›

cunning sun
#

Wait why do u try it with useTask?

#

Isn't useVisibleTask better in this case

#

@harsh wave

harsh wave
#

Yea sorry, I just wanted to show you the cleanup function, you can ignore useTask and pick whichever suits your needs the best ๐Ÿ˜‰

cunning sun
#

So just to clarify, I have to use this at the beginning of useVisibleTask?:

cleanup(() => observer.disconnect());
#

Oh sorry you said instead of return, my bad. xD

harsh wave
#

nah the end is fine, it's kinda like the return in useEffect from the react world

cunning sun
#

Got this now:

const titleElement = useSignal<HTMLHeadingElement | undefined>(undefined);
  const visibility = useSignal<boolean>(false); // Track previous visibility

  useVisibleTask$(({ cleanup }) => {
    const options = {
      root: null
    };
    
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        const isVisible = entry.intersectionRatio > 0 && entry.boundingClientRect.top >= 0;

        // Check if visibility state has changed
        if (isVisible !== visibility.value) {
          visibility.value = isVisible;

          if (isVisible) {
            console.log("Title element is fully visible.");
            // Perform actions when the title element is fully visible
          } else {
            console.log("Title element is fully invisible.");
            // Perform actions when the title element is fully invisible
          }
        }
      });
    }, options);

    if (titleElement.value) {
      observer.observe(titleElement.value);
      cleanup(() => observer.disconnect());
    }
  })
cunning sun
cunning sun
#

@harsh wave Can you help out? ๐Ÿ˜„

harsh wave
#

You have just shared your code, what is the issue? it doesn't work or?

cunning sun
#

It's like in the beginning, refer to my very first message to read the effect.

cunning sun
#

@harsh wave can you help out? ๐Ÿ˜„

harsh wave
#

I guess for me to help you'd have to layout exactly what you're trying to achieve it and why. From my point of view, it would be easier to just make sure that overlapping doesn't happen rather than go about it with observers, useVisibleTask and local storage for a purpose I don't quite get. By the looks of it, it seems like you're trying to solve a very simple problem (overlapping) with a very complex solution.

cunning sun
#

well I try to get it done working that when the h1 is visible due to scrolling, that then theres a title in the header, so users know the page they're at (besides sidebar).

cunning sun
#

I got it working except the localStorage thing. I can easily check it with "isIntersecting". If the h1 is invisible due to scrolling, it's false, and if it's visible, the value is true.
I'm having trouble with localStorage though... relevant part my layout.tsx currently:

export default component$(() => {
  const headerTitle = useSignal('');
  const checkTitle$ = $(() => {
    if(localStorage.getItem('header-title') != null) {
      headerTitle.value = localStorage.getItem('header-title') ?? '';
    }
  })

  return (
    <div class="layout h-screen w-screen flex flex-col">
      <Header title={headerTitle.value}/>
      <div class="content-wrapper h-full w-full flex">
        <Sidebar />
        <main onScroll$={checkTitle$} class="main-content float-right 
          flex-1 flex flex-col overflow-hidden overflow-y-auto">
            <Slot />
            <Footer />
        </main>
      </div>
    </div>
  );
});

index.tsx (page which gets the title in the header):

  useVisibleTask$(() => {
    const options = {
      root: null
    };
    
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if(localStorage.getItem('header-title') != null) localStorage.removeItem('header-title');
        if(!entry.isIntersecting) {
          localStorage.setItem('header-title', titleElement.value?.textContent ?? '');
        }
        console.log(entry);
      });
    }, options);

    if (titleElement.value) {
      observer.observe(titleElement.value);
      //cleanup(() => observer.disconnect());
    }
  })
#

Finally, I got it. It was just the else part inside the layout.tsx I had to add in the checkTitle$ method. I am sure there are more efficient ways than checking scroll all the time, but that works out fine for me.

#

This is like literally the proof that I have to read more and not just get straight into it. It was such a simple fix. kekw
@harsh wave Fixed now. ^^