web-dev-qa-db-fra.com

Comment capturer tous les événements de défilement sur une page sans attacher un gestionnaire onscroll à chaque conteneur

Considérez la page Web suivante:

 <html>
   <body onscroll="alert('body scroll event')">
     <div style='width:200px;height:200px;overflow:auto' onscroll="alert('div scroll event')">
       <div style='height:400px'>
       </div>
     </div>
   </body>
 </html>

Ce html crée un div avec une barre de défilement. Si vous déplacez la barre de défilement, l'événement "onscroll" sur l'élément div est déclenché. Cependant, l'événement "onscroll" sur le corps n'est PAS déclenché. Cela est attendu, car le W3C déclare que les événements de défilement d'élément ne "bouillonnent" pas.

Cependant, je développe un cadre Web côté client qui doit savoir à chaque fois qu'une barre de défilement sur N'IMPORTE QUEL élément de la page défile. Ce serait facile à faire si "onscroll" bouillonnait, mais malheureusement ce n'est pas le cas. Existe-t-il un autre moyen de détecter les événements de défilement sur une page entière? (En ce moment, je me concentre principalement sur Webkit, donc une solution spécifique à Webkit conviendrait ...)

Voici quelques choses que j'ai essayées: 1. Capturer DOMAttrModified (ne semble pas se déclencher pour déplacer les barres de défilement.) 2. Utiliser les observateurs DOM (ne semble pas non plus déclencher pour les barres de défilement) 3. Changer le type d'événement "onscroll" faire des bulles (semble impossible)

Il semble que la SEULE façon de capturer les événements onscroll à l'échelle mondiale consiste à attacher un événement onscroll à CHAQUE élément qui peut défiler, ce qui est très moche et va nuire aux performances de mon framework.

Quelqu'un connaît une meilleure façon?

Merci d'avance!

28
drcode

Le moyen le plus simple de détecter tous les événements de défilement dans un navigateur moderne serait d'utiliser la "capture" plutôt que le "bouillonnement" lors de la connexion de l'événement:

window.addEventListener('scroll', function(){ code goes here }, true)

Malheureusement, comme je le sais, il n'y a pas d'équivalent dans les anciens navigateurs tels que <= IE8

54
Norman Xu

* ... grillons chantant ... *

OK, je suppose que cette question ne suscitera aucun amour de stackoverflow, donc je pourrais aussi bien répondre à ma propre question que la meilleure solution que j'ai trouvée jusqu'à présent, au cas où un autre utilisateur tomberait sur cette question:

La meilleure solution que j'ai trouvée est de capturer "onmousedown" et "onkeydown" pour l'élément BODY: ces événements se déplacent, et donc si un utilisateur essaie de déplacer une barre de défilement sur la page, ces fonctions globales se déclencheront comme un by- produit. Ensuite, dans ces fonctions, recherchez simplement event.target et attachez un événement temporaire "onscroll" à ces objets jusqu'à ce que la souris/touche soit "relevée". En utilisant cette méthode, vous pouvez éviter le "ballonnement du gestionnaire" et toujours capturer globalement tous les événements "onscroll". (Je pense que cela fonctionnera également pour le défilement de la "molette de la souris", mais mes recherches sur cette ride finale sont toujours en attente.)

8
drcode

J'ai eu ce même problème.

La manière la plus simple est bien sûr d'utiliser jQuery. Soyez conscient que cette méthode pourrait potentiellement ralentir votre page de manière significative. De plus, il ne tiendra pas compte des nouveaux éléments ajoutés après la liaison de l'événement.

$("*").scroll(function(e) {
    // Handle scroll event
});

Dans JavaScript Vanilla, vous pouvez définir le booléen useCapture sur true lors de votre appel addEventListener, et il se déclenchera sur tous les éléments, y compris ceux ajoutés dynamiquement.

document.addEventListener('scroll', function(e) {
    // Handle scroll event
}, true);

Notez cependant que cela se déclenchera avant que l'événement de défilement ne se produise réellement. Si je comprends bien, les événements se déroulent en deux phases. La phase de capture se produit en premier, commence à la racine de la page (ownerDocument?) Et descend jusqu'à l'élément où l'événement s'est produit. Après cela vient la phase de bouillonnement, qui passe de l'élément à la racine.

Certains tests rapides ont également montré que cela peut être la façon dont jQuery le gère (pour le suivi des parchemins sur tous les éléments de la page au moins), mais je ne suis pas sûr à 100%.

Voici un JSFiddle montrant la méthode JavaScript de Vanilla http://jsfiddle.net/0qpq8pcf/

6
Conner H

Les éléments suivants fonctionnent correctement lorsque vous souhaitez fermer une boîte de dialogue après le défilement d'un élément en arrière-plan:

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

document.addEventListener('scroll', scrollListener, true);
1
Antwerp Danish