Rik Schennink dokumentiert ein System, um CSS-Selektoren schreiben zu können, die eine Seite stylen, wenn sie bis zu einem bestimmten Punkt gescrollt wurde. Wenn Sie so sind wie ich, sind Sie bereits auf der Suche nach document.addEventListener('scroll' ... und haben Angst um die Performance. Rik geht dem sofort auf den Grund, indem er die Funktion sowohl debounct als auch das Event als passive markiert.
Das Endergebnis ist ein data-scroll-Attribut auf dem <html>-Element, das in CSS verwendet werden kann. Das bedeutet, wenn Sie die Seite um 640 Pixel nach unten gescrollt haben, haben Sie <html data-scroll="640"> und könnten einen Selektor wie diesen schreiben:
html:not([data-scroll='0']) {
padding-top: 3em;
}
html:not([data-scroll='0']) header {
position: fixed;
}
See the Pen
Writing Dumb JS 🧟♂️ and Smart CSS 👩🔬 von Rik Schennink (@rikschennink)
auf CodePen.
Leider haben wir in CSS keine Größer als (>) oder Kleiner als (<) Selektoren für Dinge wie nummerierte Attribute, sodass die CSS-Styling-Möglichkeiten hier ziemlich begrenzt sind. Möglicherweise müssen Sie die JavaScript-Funktion letztendlich so anpassen, dass sie andere Klassen oder Datenattribute basierend auf Ihrer Mathematik anwendet. Aber hier sind Sie bereits für gute Performance gerüstet.
„Stile anwenden, wenn der Benutzer vom oberen Rand weggescrollt hat“ ist ein legitimer Anwendungsfall. Das erinnert mich an eine once function (wie wir sie in jQuery haben), bei der jedes Scroll-Ereignis nur einmal ausgelöst und dann nicht wieder. Sie haben gescrollt! Sie sind also per Definition nicht mehr ganz oben! Aber das befasst sich nicht damit, wann sie wieder nach oben scrollen.
Ich finde es generell nützlicher, IntersectionObserver für das Stylen von Dingen basierend auf der Scroll-Position zu verwenden. Damit können Sie Dinge tun wie: „Wurde dieses Element ins Blickfeld oder darüber hinaus gescrollt?“, was allgemein nützlich ist und auch für Dinge verwendet werden kann, die vom oberen Rand weggescrollt werden.
Hier ist ein Beispiel, das eine Klasse hinzufügt oder entfernt, wenn ein Benutzer einen versteckten Pixel passiert hat, der 500 Pixel unterhalb der Seite positioniert ist.
See the Pen
Fixed Header with IntersectionObserver von Chris Coyier (@chriscoyier)
auf CodePen.
Das ist ebenfalls performant und vermeidet jegliche Scroll-Event-Handler.
Und wo wir gerade von IntersectionObserver sprechen, werfen Sie einen Blick auf „Trust is Good, Observation is Better—Intersection Observer v2“.
Ich denke, dass die Verwendung der passiven Option für den Scroll-Listener redundant ist, da man ihn sowieso nicht verhindern kann. Der Grund dafür ist, dass das Scroll-Ereignis ausgelöst wird, nachdem das Scrollen im Browser bereits stattgefunden hat. Das Wheel-Ereignis kann jedoch verhindert werden, daher sollten wir die passive Option für seinen Listener verwenden, wenn wir können.
IntersectionObserver – huh, definitiv cool… aber keine IE11-Unterstützung macht es leider unbrauchbar für die Produktion.
FYI: In Zukunft werden wir Bereichsabfragen haben, siehe https://drafts.csswg.org/mediaqueries-4/#mq-range-context
Die Frage, die sich stellt, ist, wie weit sollen wir in CSS gehen? Logik kann leicht mit JavaScript hinzugefügt und einfach Klassen gesetzt werden. Was vielleicht sinnvoller ist. So wie das Beispiel mit IntersectionObserver.
Und ich glaube, das Setzen einer Klasse einmal ist performanter als das ständige Aktualisieren eines HTML-Attributs.
Ich bin auch neugierig, wie oft dieses Debouncing tatsächlich debounct, auf meiner Entwicklermaschine tut es nichts. Ich bevorzuge immer noch ein zeitbasiertes Debouncing mit innerhalb des Callbacks nur einem einzigen requestAnimationFrame. Oder in diesem Fall ein Throttling.
Ich habe einen Logikfehler in meiner Scroll-Position gemacht, da sie über 100 % geht, aber ich habe ein einfaches Demo gemacht, um tatsächlich Stile bei jeder Scroll-Position mit CSS-Variablen zu ändern. Beachten Sie, dass es in der stabilen Version von Chrome funktioniert, aber die Kombination von CSS-Variablen und calc innerhalb von CSS-Werten immer noch riskant ist. (hat beim letzten Mal, als ich etwas Ähnliches in Firefox getestet habe, nicht funktioniert)
scheint „in window“ ein Listener zu sein, der effektiv konstant ist, also wie vergleicht er sich mit dem Ersetzen des Scroll-Events durch requestAnimationFrame a la https://gist.github.com/Warry/4254579?
Tatsächlich gibt es ein offizielles w3c Polyfill für IntersectionObserver: https://github.com/w3c/IntersectionObserver/tree/master/polyfill
Sehen Sie hier ein Beispiel dafür in Aktion: https://200.cravath.dev/industrialization-civil-war
Ich ziehe es vor, Sticky-Elemente und IO für Spies zu verwenden.
https://developers.google.com/web/updates/2017/09/sticky-headers#introducing_the_sticky-change_event
Habe ein paar neue Sachen gelernt. Danke. Normalerweise habe ich früher mit document.addEventListener('scroll' …) animiert, aber ich bin neugierig auf die Performance-Probleme mit diesem Ansatz.
Ist das zweite Beispiel (mit IntersectionObserver) eigenständig gedacht? Oder benötigt es den Code aus dem ersten Beispiel? Auf jeden Fall erhalte ich einen Fehler:
Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'.Danke.ADMIN EDIT
Stellen Sie sicher, dass der DOM bereit ist ;)
Für alle anderen, die relativ neu im Spiel sind, ist das Markup im Beispiel SCSS, nicht CSS. Aber selbst nachdem ich das SCSS in CSS konvertiert habe, erhalte ich denselben Fehler.
Ich konnte den JavaScript-Fehler beheben (und das hier zum Laufen bringen), indem ich das JavaScript direkt im Dokument unter den Elementen platziert habe, auf die es verweist. Wenn jemand einen Weg kennt, diese Funktionalität zu erreichen und gleichzeitig das JavaScript in einer eigenen externen Datei zu belassen (die im Head-Element referenziert wird), würde ich es gerne hören. Danke.