Nico R.: IntersectionObserver mit root-Element

Hallo zusammen,

ich bekomme es irgendwie nicht hin, mit dem IntersectionObserver zwei Elemente aufeinander reagieren zu lassen. Ich habe eine Navigation, die fest steht. Sobald ein Test-DIV die Navigation berührt, soll eine Konsolenausgabe erfolgen.

Hier die Testseite und hier nochmal das grundlegende JS:

const observer = new IntersectionObserver (function (entries) {
  if (entries[0].isIntersecting) {
    console.log("isIntersecting");
  } 
  else {
    console.log("!isIntersecting");		
  }
}, { root: nav, rootMargin: "0px", treshold: 0 } );	

observer.observe(container);	

Die Ausgabe ist immer !isIntersecting, auch wenn ich mit dem Container unter der Navigation hindurch scrolle.

Lasse ich das root: nav weg, beziehe mich also auf den Viewport, reagiert das ganze wie erwartet, also beim ersten Aufruf der Seite: isIntersecting, beim Herausscrollen des Containers: !isIntersecting.

Hab ich das mit dem root-Element falsch verstanden?

Schöne Grüße

Nico

  1. Hallo Nico R.,

    Hab ich das mit dem root-Element falsch verstanden?

    Ja. Das Root-Element muss ein Elternelement Vorfahre des beobachteten Elements sein, sonst geht's nicht. Lässt Du root weg oder setzt es auf null, ist das "Elternelement" „Vorfahrenelement“ der Browserviewport.

    Damit bleibt jetzt aber die Frage offen, wie Du elegant eine „Kollision“ zwischen nav und #container erkennst. Antwort: Nicht mit einem Observer. Dir bleibt wohl nur, das scroll-Event zu behandeln und (mit Häufigkeitslimitierung) zu testen, ob die BoundingRects von nav und #container sich überlappen.

    In Deinem Fall reicht dazu die Abfrage, ob die Oberkante von #container oberhalb der Unterkante von nav liegt, also

    if (container.getBoundingClientRect().top 
          < nav.getBoundingClientRect().bottom) {
       // Überlappung
    }
    

    Da getBoundingClientRect eine Layoutphase im Browser triggert, muss man diesen Aufruf über einen Throttler laufen lassen. Wir haben sowas im Wiki: hier

    Einfach zu Beginn des Scripts einbauen, er erweitert Funktionen um eine Methode throttle, wodurch man eine „gebremste“ Version der Funktion bekommt.

    Function.prototype.throttle = ...
    
    function scrollHandler(scrollEvent) {
       if (container.getBoundingClientRect().top 
             < nav.getBoundingClientRect().bottom) {
          // Überlappung
       }
    }
    
    document.addEventListener("scroll", scrollHandler.throttle(100));
    

    Es gibt auch fertige Throttler in anderen Bibliotheken, z.B. lodash oder underscore. Wenn Du die eh drin hast, dann guck dort.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. @@Rolf B

      Ja. Das Root-Element muss ein Elternelement des beobachteten Elements sein, sonst geht's nicht.

      Nein. Du meinst Vorfahrenelement

      „Deine Mudda sieht aus wie deine Omma.“

      Kwakoni Yiquan

      --
      Ad astra per aspera
      1. Servus!

        „Deine Mudda sieht aus wie deine Omma.“

        Jetzt gehst du aber zu weit! 😉

        Herzliche Grüße

        Matthias Scharwies

        --
        Das wirksamste Mittel gegen Sonnenbrand
        ist Urlaub am Ostseestrand!
      2. Hallo Gunnar,

        jo, Vorfahren natürlich.

        „Deine Mudda sieht aus wie deine Omma.“

        Gaaaa nich!!! Meine Omma war vor lauter Wasser im Leib rund und unbeweglich und ist viel zu früh gestorben, und meine Mudda ist gottseidank weder aufgebläht noch tot. Meine andere Omma war zwar dürr wie ein Stock und ist fast 90 geworden, aber weil sie die Mudda von meinem Babba war, sieht meine Mudda gaaanz anners aus 😉

        Rolf

        --
        sumpsi - posui - obstruxi
    2. Hallo Rolf,

      danke für die wieder mal ausführliche und (vermutlich) hilfreiche Antwort. Ich bin mir fast sicher, dass ich das irgendwann gebrauchen kann.

      Aktuell bin ich aber zu dem Schluss gekommen, dass sich das, was ich vor habe, viel einfacher über CSS (position: sticky) realisieren lassen sollte. Im angepassten Testscript funktionierts, nur leider nicht auf meiner Originalseite. Das war auch der Grund, warum ich ersatzweise den IntersectionObserver versucht habe. Ich muss jetzt mal herausfinden, was da hakt. Eventuell folgt hierzu noch eine Frage ;-)

      Besten Dank

      Nico