Kurzfassung
Hier ist ein Geheimnis: Möglicherweise benötigen Sie in Ihrer nächsten App keine scroll
-Ereignisse. Mit einem IntersectionObserver
zeige ich, wie Sie ein benutzerdefiniertes Ereignis auslösen können, wenn position:sticky
-Elemente fixiert werden oder sich nicht mehr anklicken lassen. Und das ganz ohne Scroll-Listener. Hier ist eine Demo, die das beweist:
Das Ereignis sticky-change
Eine der praktischen Einschränkungen der Verwendung der CSS-Sticky-Position ist, dass kein Plattformsignal dafür vorhanden ist, wann die Property aktiv ist. Mit anderen Worten: Es gibt kein Ereignis, das angibt, wann ein Element fixiert wird oder wann es nicht mehr fixiert ist.
Im folgenden Beispiel wird ein Wäre es nicht schön, wenn der Browser anzeigen würde, wann die Elemente diese Markierung erreichen?
Offenbar bin ich nicht der Einzige, der das so sieht. Ein Signal für Basierend auf diesen Anwendungsfällen haben wir ein Endziel formuliert: ein Ereignis erstellen, das ausgelöst wird, wenn ein In der Demo wird dieses Ereignis verwendet, um Überschriften einen Schatten zu geben, wenn sie fixiert werden. Außerdem wird der neue Titel oben auf der Seite aktualisiert. Zuerst möchte ich einige Begriffe klären, damit ich im weiteren Verlauf des Artikels auf diese Namen Bezug nehmen kann: Damit wir wissen, welcher Header in den „Sticky-Modus“ wechselt, müssen wir den Scroll-Offset des Scroll-Containers ermitteln. So könnten wir den aktuell angezeigten Header berechnen. Das ist jedoch ohne Ohne Scroll-Ereignisse können wir also keine layoutbezogenen Berechnungen für die Überschriften mehr ausführen. Anstatt Wir benötigen zwei Sentinels, um vier Fälle des Scrollens nach oben und unten abzudecken: Es ist hilfreich, sich einen Screencast von 1 bis 4 in der Reihenfolge anzusehen, in der sie auftreten: Die Sentinels werden oben und unten in jedem Abschnitt platziert.
Intersection Observer überwachen asynchron Änderungen an der Schnittmenge eines Zielelements und des Dokument-Viewports oder eines übergeordneten Containers. In unserem Fall sehen wir uns Überschneidungen mit einem übergeordneten Container an.
Das Geheimnis ist Zuerst richte ich Beobachter für die Kopf- und Fußzeilen ein: Dann habe ich einen Beobachter hinzugefügt, der ausgelöst wird, wenn Der Beobachter ist mit Für den unteren Grenzwert ( Der Beobachter ist mit Zum Schluss gibt es noch zwei Tools zum Auslösen des benutzerdefinierten Ereignisses Geschafft! Wir haben ein benutzerdefiniertes Ereignis erstellt, wenn Elemente mit Ich habe mich oft gefragt, ob Nicht wirklich. Wir brauchten eine Möglichkeit, Stiländerungen an einem DOM-Element zu beobachten.
Leider gibt es in den APIs der Webplattform keine Möglichkeit, Stiländerungen zu beobachten. Eine In Zukunft könnte eine Erweiterung von Mutation Observers um Style Mutation Observer nützlich sein, um Änderungen an den berechneten Stilen eines Elements zu beobachten.
Sofern nicht anders angegeben, sind die Inhalte dieser Seite unter der Creative Commons Attribution 4.0 License und Codebeispiele unter der Apache 2.0 License lizenziert. Weitere Informationen finden Sie in den Websiterichtlinien von Google Developers. Java ist eine eingetragene Marke von Oracle und/oder seinen Partnern. Zuletzt aktualisiert: 2017-09-19 (UTC)..sticky {
position: sticky;
top: 10px;
}
position:sticky
könnte eine Reihe von Nutzungsfällen ermöglichen:
position:sticky
-Element fixiert wird. Nennen wir es das Ereignis sticky-change
:document.addEventListener('sticky-change', e => {
const header = e.detail.target; // header became sticky or stopped sticking.
const sticking = e.detail.stuck; // true when header is sticky.
header.classList.toggle('shadow', sticking); // add drop shadow when sticking.
document.querySelector('.who-is-sticking').textContent = header.textContent;
});
Scrolleffekte ohne Scrollereignisse?
position:sticky
.position:sticky
wird auf das Element angewendet.scroll
-Ereignisse ziemlich schwierig. Das andere Problem ist, dass position:sticky
das Element aus dem Layout entfernt, wenn es fixiert wird.Dummy-DOM hinzufügen, um die Scrollposition zu bestimmen
scroll
-Ereignisse verwenden wir ein IntersectionObserver
, um zu ermitteln, wann Überschriften den angepinnten Modus betreten und verlassen. Wenn Sie in jedem fixierten Bereich zwei Knoten (auch als Sentinels bezeichnet) hinzufügen, einen oben und einen unten, dienen diese als Wegpunkte für die Ermittlung der Scrollposition. Wenn diese Markierungen den Container betreten oder verlassen, ändert sich ihre Sichtbarkeit und Intersection Observer löst einen Rückruf aus.
Das Preisvergleichsportal
.sticky_sentinel--top
befindet sich oben in der Überschrift, während .sticky_sentinel--bottom
unten im Abschnitt zu sehen ist:
:root {
--default-padding: 16px;
--header-height: 80px;
}
.sticky {
position: sticky;
top: 10px; /* adjust sentinel height/positioning based on this position. */
height: var(--header-height);
padding: 0 var(--default-padding);
}
.sticky_sentinel {
position: absolute;
left: 0;
right: 0; /* needs dimensions */
visibility: hidden;
}
.sticky_sentinel--top {
/* Adjust the height and top values based on your on your sticky top position.
e.g. make the height bigger and adjust the top so observeHeaders()'s
IntersectionObserver fires as soon as the bottom of the sentinel crosses the
top of the intersection container. */
height: 40px;
top: -24px;
}
.sticky_sentinel--bottom {
/* Height should match the top of the header when it's at the bottom of the
intersection container. */
height: calc(var(--header-height) + var(--default-padding));
bottom: 0;
}
Intersection Observers einrichten
IntersectionObserver
. Jeder Sentinel erhält eine IntersectionObserver
, um die Sichtbarkeit der Überschneidung im Scrollcontainer zu beobachten. Wenn ein Sentinel in den sichtbaren Darstellungsbereich scrollt, wissen wir, dass eine Überschrift fixiert wurde oder nicht mehr fixiert ist. Das Gleiche gilt, wenn ein Sentinel den Darstellungsbereich verlässt./**
* Notifies when elements w/ the `sticky` class begin to stick or stop sticking.
* Note: the elements should be children of `container`.
* @param {!Element} container
*/
function observeStickyHeaderChanges(container) {
observeHeaders(container);
observeFooters(container);
}
observeStickyHeaderChanges(document.querySelector('#scroll-container'));
.sticky_sentinel--top
-Elemente den oberen Rand des Scrollcontainers in beide Richtungen passieren.
Die Funktion observeHeaders
erstellt die oberen Grenzwerte und fügt sie jedem Abschnitt hinzu. Der Beobachter berechnet die Schnittmenge des Sentinels mit dem oberen Rand des Containers und entscheidet, ob er den Darstellungsbereich betritt oder verlässt. Anhand dieser Informationen wird festgelegt, ob der Abschnittsheader fixiert ist oder nicht./**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--top` become visible/invisible at the top of the container.
* @param {!Element} container
*/
function observeHeaders(container) {
const observer = new IntersectionObserver((records, observer) => {
for (const record of records) {
const targetInfo = record.boundingClientRect;
const stickyTarget = record.target.parentElement.querySelector('.sticky');
const rootBoundsInfo = record.rootBounds;
// Started sticking.
if (targetInfo.bottom < rootBoundsInfo.top) {
fireEvent(true, stickyTarget);
}
// Stopped sticking.
if (targetInfo.bottom >= rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
fireEvent(false, stickyTarget);
}
}
}, {threshold: [0], root: container});
// Add the top sentinels to each section and attach an observer.
const sentinels = addSentinels(container, 'sticky_sentinel--top');
sentinels.forEach(el => observer.observe(el));
}
threshold: [0]
konfiguriert, sodass sein Rückruf ausgelöst wird, sobald der Sentinel sichtbar wird..sticky_sentinel--bottom
) ist das Verfahren ähnlich.
Ein zweiter Beobachter wird erstellt, der ausgelöst wird, wenn die Fußzeilen den unteren Rand des Scrollcontainers erreichen. Die Funktion observeFooters
erstellt die Sentinel-Knoten und fügt sie den einzelnen Abschnitten hinzu. Der Beobachter berechnet die Schnittmenge des Grenzwerts mit dem Boden des Containers und entscheidet, ob der Container betreten oder verlassen wird. Anhand dieser Informationen wird festgelegt, ob der Abschnittsheader fixiert ist oder nicht./**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--bottom` become visible/invisible at the bottom of the
* container.
* @param {!Element} container
*/
function observeFooters(container) {
const observer = new IntersectionObserver((records, observer) => {
for (const record of records) {
const targetInfo = record.boundingClientRect;
const stickyTarget = record.target.parentElement.querySelector('.sticky');
const rootBoundsInfo = record.rootBounds;
const ratio = record.intersectionRatio;
// Started sticking.
if (targetInfo.bottom > rootBoundsInfo.top && ratio === 1) {
fireEvent(true, stickyTarget);
}
// Stopped sticking.
if (targetInfo.top < rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
fireEvent(false, stickyTarget);
}
}
}, {threshold: [1], root: container});
// Add the bottom sentinels to each section and attach an observer.
const sentinels = addSentinels(container, 'sticky_sentinel--bottom');
sentinels.forEach(el => observer.observe(el));
}
threshold: [1]
konfiguriert, sodass sein Rückruf ausgelöst wird, wenn der gesamte Knoten im Sichtfeld ist.sticky-change
und zum Generieren der Prüfzeichen:/**
* @param {!Element} container
* @param {string} className
*/
function addSentinels(container, className) {
return Array.from(container.querySelectorAll('.sticky')).map(el => {
const sentinel = document.createElement('div');
sentinel.classList.add('sticky_sentinel', className);
return el.parentElement.appendChild(sentinel);
});
}
/**
* Dispatches the `sticky-event` custom event on the target element.
* @param {boolean} stuck True if `target` is sticky.
* @param {!Element} target Element to fire the event on.
*/
function fireEvent(stuck, target) {
const e = new CustomEvent('sticky-change', {detail: {stuck, target}});
document.dispatchEvent(e);
}
Endgültige Demo
position:sticky
fixiert werden, und Scrolleffekte ohne scroll
-Ereignisse hinzugefügt.Fazit
IntersectionObserver
ein hilfreiches Tool wäre, um einige der scroll
ereignisbasierten UI-Muster zu ersetzen, die sich im Laufe der Jahre entwickelt haben. Die Antwort lautet „Ja“ und „Nein“. Die Semantik der IntersectionObserver
API macht es schwierig, sie für alle Zwecke zu verwenden. Aber wie ich hier gezeigt habe, können Sie es für einige interessante Techniken verwenden.Gibt es eine andere Möglichkeit, Stiländerungen zu erkennen?
MutationObserver
wäre eine logische erste Wahl, funktioniert aber in den meisten Fällen nicht. In der Demo erhalten wir beispielsweise einen Rückruf, wenn der Klasse sticky
ein Element hinzugefügt wird, aber nicht, wenn sich der berechnete Stil des Elements ändert.
Die Klasse sticky
wurde bereits beim Laden der Seite deklariert.position: sticky
.