52 Stimmen

Wie man alle Scroll-Ereignisse auf einer Seite erfasst, ohne einen onscroll-Handler an jedes einzelne Container anzuhängen

Betrachten Sie die folgende Webseite:

Dieses HTML erstellt ein div mit einer Scrollleiste. Wenn Sie die Scrollleiste bewegen, wird das onscroll Ereignis des div-Elements ausgelöst. Jedoch wird das onscroll Ereignis des body NICHT ausgelöst. Das ist zu erwarten, da das W3C feststellt, dass Element-onscroll-Ereignisse nicht "blasen".

Ich entwickle jedoch ein clientseitiges Webframework, das jedes Mal informiert werden muss, wenn eine Scrollleiste eines beliebigen Elements der Seite gescrollt wird. Dies wäre einfach zu tun, wenn onscroll blasen würde, aber leider tut es das nicht. Gibt es einen anderen Weg, um onscroll-Ereignisse auf der gesamten Seite zu erkennen? (Derzeit konzentriere ich mich hauptsächlich auf Webkit, daher wäre eine Webkit-spezifische Lösung in Ordnung...)

Hier sind einige Dinge, die ich ausprobiert habe:

  1. Erfassen von DOMAttrModified (scheint nicht für das Bewegen von Scrollleisten zu funktionieren.)
  2. Verwendung von DOM-Beobachtern (scheinen auch nicht für Scrollleisten zu funktionieren)
  3. Ändern des onscroll-Ereignistyps auf bubbeln (scheint nicht möglich zu sein)

Es scheint, dass der EINZIGE Weg, onscroll-Ereignisse global zu erfassen, darin besteht, ein onscroll-Ereignis an JEDES Element anzuhängen, das scrollen kann, was sehr hässlich ist und die Leistung meines Frameworks beeinträchtigen wird.

Weiß jemand einen besseren Weg?

111voto

Norman Xu Punkte 1265

Der einfachste Weg, um alle Scroll-Ereignisse im modernen Browser zu erfassen, besteht darin, "Capturing" anstelle von "Bubbling" zu verwenden, wenn das Ereignis angefügt wird:

window.addEventListener('scroll', function(){ Code hier eingeben }, true)

Leider gibt es meines Wissens nach keine Entsprechung in älteren Browsern wie <= IE8

13voto

Conner H Punkte 357

Ich hatte das gleiche Problem.

Der einfachste Weg ist natürlich, jQuery zu verwenden. Beachten Sie, dass diese Methode potenziell die Leistung Ihrer Seite erheblich verlangsamen könnte. Außerdem berücksichtigt sie nicht alle neuen Elemente, die nach dem Binden des Ereignisses hinzugefügt werden.

$("*").scroll(function(e) {
    // Behandeln Sie das Scroll-Ereignis
});

In reinem JavaScript können Sie das useCapture-Boolean auf true in Ihrem addEventListener-Aufruf setzen, und es wird bei allen Elementen, einschließlich der dynamisch hinzugefügten Elemente, ausgelöst.

document.addEventListener('scroll', function(e) {
    // Behandeln Sie das Scroll-Ereignis
}, true);

Beachten Sie jedoch, dass dies vor dem tatsächlichen Scroll-Ereignis ausgelöst wird. Soweit ich weiß, durchlaufen Ereignisse zwei Phasen. Zuerst kommt die Erfassungsphase, die vom Wurzelelement der Seite (ownerDocument?) ausgeht und zum Element hinunter traversiert, bei dem das Ereignis auftritt. Danach folgt die Bubbling-Phase, die vom Element zurück zum Wurzelelement traversiert.

Auch einige schnelle Tests haben gezeigt, dass dies möglicherweise ist, wie jQuery es handhabt (zumindest für das Verfolgen von Scroll-Ereignissen auf allen Seitenelementen), aber ich bin mir nicht zu 100% sicher.

Hier ist ein JSFiddle, das die Methode in reinem JavaScript zeigt http://jsfiddle.net/0qpq8pcf/

10voto

drcode Punkte 3207

*... Grillen zirpen ... *

OK, anscheinend wird diese Frage keine Aufmerksamkeit auf Stackoverflow bekommen, also werde ich meine eigene Frage beantworten, was meiner Meinung nach die beste Lösung ist, die ich bisher gefunden habe, falls ein anderer Benutzer auf diese Frage stößt:

Die beste Lösung, die ich gefunden habe, besteht darin, "onmousedown" und "onkeydown" für das BODY-Element zu erfassen: Diese Ereignisse blubbern, und wenn ein Benutzer versucht, eine Bildlaufleiste auf der Seite zu bewegen, werden diese globalen Funktionen als Nebenprodukt ausgeführt. Dann müssen Sie in diesen Funktionen einfach event.target nachschlagen und ein vorübergehendes "onscroll" Ereignis an diese Objekte anhängen, bis die Maus/Taste wieder "oben" ist. Mit dieser Methode können Sie "handler bloat" vermeiden und trotzdem alle "onscroll" Ereignisse global erfassen. (Ich glaube, dies funktioniert auch für das Scrollen mit dem "Mausrad", aber meine Recherche zu diesem letzten Detail ist noch ausstehend.)

1voto

Antwerp Danish Punkte 21

Das folgende funktioniert gut, wenn Sie z.B. einen Dialog schließen möchten, nachdem im Hintergrund gescrollt wurde:

var scrollListener = function(e) {
    // TODO: Dialog ausblenden
    document.removeEventListener('scroll', scrollListener, true);
};

document.addEventListener('scroll', scrollListener, true);

0voto

jimmont Punkte 1960

Ich musste das Scrollen in jedem Kontext innerhalb eines Komplexes von benutzerdefinierten Elementen mit Scroll-Ereignissen in shadowRoots behandeln. Dies deckt zumindest einen Teil des Problems in meinem Szenario ab und leitet im Allgemeinen aus den Antworten und Kommentaren hier im Zusammenhang mit diesen neueren Webkomponenten-APIs ab. Das Anfügen und Abtrennen des Listeners in den entsprechenden Lebenszyklus-Callbacks funktioniert bisher gut (es könnte jedoch möglicherweise nicht für Ihren Anwendungsfall geeignet sein).

self.addEventListener('mousewheel', handler, {capture: true, once: true});
self.addEventListener('keydown', handler, {capture: true, once: true});

Beachten Sie auch, dass event.composedPath() den gesamten Ereignisweg durch alle shadowRoots mit allen Knoten bereitstellt, falls spezifische Informationen zum Scroll-Kontext benötigt werden. Wenn dies der Fall ist, könnte es sinnvoll sein, diesen Ansatz zu verwenden und einen neuen Handler für dieses spezifische Szenario am interessierenden Knoten anzufügen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X