93 votes

Détecter si l'événement de défilement a été créé par l'utilisateur

Est-il possible de savoir si un événement de défilement a été déclenché par le navigateur ou par l'utilisateur? Plus précisément, lors de l'utilisation du bouton retour, un navigateur peut sauter à la dernière position de défilement connue. Si je lie un événement de défilement, comment puis-je savoir si cela a été causé par l'utilisateur ou par le navigateur?

$(document).scroll( function(){ 
    //qui a fait cela ?!
});

Je distingue trois types de situations qui provoquent un défilement dans un navigateur.

  1. L'utilisateur effectue une action. Par exemple, utilise la molette de la souris, les touches de direction, les touches de page haut/bas, les touches de début/fin, clique sur la barre de défilement ou en fait glisser le curseur.
  2. Le navigateur défile automatiquement. Par exemple, lorsque vous utilisez le bouton retour dans votre navigateur, il sautera automatiquement à la dernière position de défilement connue.
  3. Le JavaScript effectue un défilement. Par exemple, element.scrollTo(x,y).

1 votes

Je ne suis pas sûr de votre question, si vous considérez le saut en utilisant le bouton retour vers moi un événement de défilement du navigateur ou de l'utilisateur. En général : Que considérez-vous comme "défilement par le navigateur" ? Si vous voulez dire le défilement initié par votre script, alors tout ce que vous avez à faire, c'est lorsque votre script défile, soit de désactiver le gestionnaire d'événements, soit de définir un indicateur pour que le gestionnaire d'événements sache l'ignorer.

0 votes

J'ai considéré le défilement via le bouton de retour comme un "défilement du navigateur". Tout autre chose - la molette de la souris, les flèches haut/bas, le clic sur le bouton central, etc. serait un défilement de l'utilisateur. Je suppose que ma vraie question pourrait être - y a-t-il un moyen de différencier d'où provient un événement ? J'ai regardé les propriétés sur l'objet événement, mais je n'ai rien trouvé. Les trois scénarios que je peux imaginer sont le défilement initié par le navigateur, le défilement initié par JavaScript et le défilement initié par l'utilisateur. J'espère que cela clarifie les choses.

0 votes

@mrtsherman J'ai trouvé certains de ces éléments tout en obtenant le même résultat : stackoverflow.com/questions/2834667/…

32voto

Mrchief Points 25418

Malheureusement, il n'y a pas de moyen direct de le dire.

Je dirais que si vous pouvez redessiner votre application pour qu'elle ne dépende pas de ce type de flux, allez-y.

Si ce n'est pas le cas, une astuce à laquelle je pense est de suivre les scrolls initiés par l'utilisateur et de vérifier cela pour voir si le scroll a été déclenché par le navigateur ou par l'utilisateur.

Voici un exemple que j'ai mis en place qui le fait assez bien (à l'exception des navigateurs où jQuery history pose problème).

Vous devez exécuter cela localement pour pouvoir le tester complètement (jsFiddle/jsbin ne sont pas adaptés car ils encapsulent le contenu dans un iFrame).

Voici les cas de test que j'ai validés:

  • Chargement de la page - userScroll est false
  • Défilement avec la souris/le clavier - userScroll devient true
  • Cliquer sur le lien pour sauter en bas de page - userScroll devient false
  • Cliquer sur Retour/Avant - userScroll devient false;

     bonjour  
     cliquez ici pour descendre  

     juste là  

var userScroll = false;     

function mouseEvent(e) { 
    userScroll = true; 
} 

$(function() { 

    // réinitialiser le drapeau sur Retour/Avant
    $.history.init(function(hash){ 
        userScroll = false; 
    }); 

    $(document).keydown(function(e) { 
        if(e.which == 33        // page up 
           || e.which == 34     // page dn 
           || e.which == 32     // espace
           || e.which == 38     // haut 
           || e.which == 40     // bas 
           || (e.ctrlKey && e.which == 36)     // ctrl + home 
           || (e.ctrlKey && e.which == 35)     // ctrl + fin 
          ) { 
            userScroll = true; 
        } 
    }); 

    // détecter le défilement par l'utilisateur via la souris
    // Mozilla/Webkit 
    if(window.addEventListener) {
        document.addEventListener('DOMMouseScroll', mouseEvent, false); 
    }

    //pour IE/OPERA etc 
    document.onmousewheel = mouseEvent; 

    // réinitialiser le drapeau lorsque les ancres nommées sont cliquées
    $('a[href*=#]').click(function() { 
        userScroll = false;
    }); 

      // détecter le défilement par le navigateur/l'utilisateur
    $(document).scroll( function(){  
        console.log('Défilement initié par ' + (userScroll == true ? "l'utilisateur" : "le navigateur"));
    });
}); 

Notes:

  • Cela ne suit pas le défilement lorsque l'utilisateur fait glisser la barre de défilement avec la souris. Cela peut être ajouté avec un peu plus de code, que j'ai laissé en exercice pour vous.
  • Les event.keyCodes peuvent varier en fonction du système d'exploitation, vous devrez donc les adapter en conséquence.

J'espère que cela vous aidera!

0 votes

Merci M. Chief. Je pense que c'est la meilleure réponse même si ce n'est pas ce que j'espérais que ce serait (Quoi ? Il n'y a pas de propriété event.thrower ?!).

0 votes

Vous n'avez pas besoin d'appuyer sur la touche Contrôle lorsque vous utilisez les touches Accueil et Fin pour faire défiler.

0 votes

DOMMouseScroll ne fonctionne pas sur la dernière version de Chrome sur Mac. Il est préférable d'utiliser document.addEventListener('mousewheel' au lieu de définir onmousewheel.

10voto

this.lau_ Points 23290

Au lieu d'essayer de capturer tous les événements utilisateur, il est beaucoup plus facile de faire l'inverse et de gérer uniquement les événements programmés - et d'ignorer ceux-ci

Par exemple, ce type de code fonctionnerait:

// Élément qui doit être défilé
var myElement = document.getElementById('my-container');

// Drapeau pour indiquer si le changement était programmé ou par l'utilisateur
var ignoreNextScrollEvent = false;

function setScrollTop(scrollTop) {
    ignoreNextScrollEvent = true;
    myElement.scrollTop = scrollTop
}

myElement.addEventListener('scroll', function() {
    if (ignoreNextScrollEvent) {
        // Ignorer cet événement car il a été fait de manière programmée
        ignoreNextScrollEvent = false;
        return;
    }

    // Traiter l'événement initié par l'utilisateur ici
});

Ensuite, lorsque vous appelez setScrollTop(), l'événement de défilement sera ignoré, tandis que si l'utilisateur fait défiler avec la souris, le clavier ou tout autre moyen, l'événement sera traité.

2 votes

Cela ne détecte/ignore pas les événements de défilement déclenchés par le navigateur.

1 votes

J'ai pensé à quelque chose de similaire il faut ajouter - cela ne prend pas en compte que le comportement de défilement peut être défini sur "smooth"

8voto

WTK Points 7883

Aussi loin que je sache, il est impossible (sans aucun travail) de dire si un événement de défilement a été déclenché par un "utilisateur" ou par d'autres moyens.

Vous pourriez essayer (comme d'autres l'ont mentionné) de capturer les événements de la molette de la souris, puis probablement essayer de capturer l'événement keydown sur les touches qui peuvent déclencher un défilement (flèches, espace etc.) tout en vérifiant quel élément est actuellement sélectionné, car par exemple vous ne pouvez pas faire défiler en utilisant les touches de direction tout en tapant dans un champ de saisie. En général, ce serait un script complexe et confus.

En fonction de la situation à laquelle vous êtes confronté, vous pourriez, je suppose, "inverser la logique", et au lieu de détecter les événements de défilement déclenchés par l'utilisateur, simplement vous connecter à tous les défilements effectués de manière programmatique et traiter tout événement de défilement non effectué par votre code comme effectué par un utilisateur. Comme je l'ai dit, cela dépend de la situation et de ce que vous essayez d'atteindre.

0 votes

Merci WTK. C'était la meilleure réponse pendant un moment, mais malheureusement pour vous Mrchief a développé votre réponse avec quelques exemples de code donc je lui ai donné la prime. Si j'avais pu la diviser, je l'aurais fait !

4 votes

C'est bon. Il ne s'agit pas de prime, mais de diffuser et d'acquérir des connaissances :)

4voto

Oui, c'est 100% possible. Je l'utilise actuellement dans une application où IE n'est pas une exigence - uniquement destinée aux clients. Lorsque mon application Backbone lance une animation qui modifie le défilement, le défilement se produit mais ne déclenche pas ces captures d'événements. Cela a été testé dans les dernières versions de FF, Safari et Chrome.

$('html, body').bind('scroll mousedown wheel DOMMouseScroll mousewheel keyup', function(evt) {
  // détecter uniquement l'initiation par l'utilisateur, pas par une .animate cependant
    if (evt.type === 'DOMMouseScroll' || evt.type === 'keyup' || evt.type === 'mousewheel') {
    // haut
    if (evt.originalEvent.detail < 0 || (evt.originalEvent.wheelDelta && evt.originalEvent.wheelDelta > 0)) { 
    // bas
    } else if (evt.originalEvent.detail > 0 || (evt.originalEvent.wheelDelta && evt.originalEvent.wheelDelta < 0)) { 
  }
}
});

1 votes

Merci pour la suggestion. Cependant, il s'agit d'une solution binaire - détecte le défilement initié par JavaScript par rapport au défilement sans JavaScript. J'ai vraiment besoin d'une solution ternaire. (javascript, utilisateur, navigateur).

2voto

Gerben Points 10821

Essayez d'utiliser les événements Mousewheel et DOMMouseScroll à la place. Voir http://www.quirksmode.org/dom/events/scroll.html

0 votes

Merci pour cette suggestion. Cependant, je ne pense pas que cela corresponde à ce que je veux. En fait, j'ai besoin de différencier entre le défilement du navigateur et celui de l'utilisateur. Pas seulement cibler la molette de la souris.

0 votes

Je pense que l'événement mousewheel est déclenché avant l'événement onscroll. Ainsi, vous pouvez définir var userscroll=true sur mousewheel, et le détecter dans onscroll (et le réinitialiser à false).

3 votes

Il existe un grand nombre d'événements de défilement initiés par l'utilisateur qui ne seraient pas couverts pour autant. Touches fléchées, redimensionnement de la fenêtre, etc. Il est beaucoup plus sûr de dire que l'expéditeur de cet événement est "X" que de dire que tout ce qui se trouve en dehors de cet événement doit être "X". Cela ne fonctionne également pas pour moi, car je veux identifier un défilement initié par le navigateur, et non par l'utilisateur. Ainsi, pour répondre à ce cas d'utilisation, je devrais suivre tous les défilements de la molette de la souris, puis essayer de déduire si l'événement de défilement ultérieur était basé sur l'utilisateur. J'ai bien peur que ce ne soit simplement pas faisable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X