Ermitteln Sie, ob das Bildlaufereignis vom Benutzer erstellt wurde

80

Kann festgestellt werden, ob ein Bildlaufereignis vom Browser oder vom Benutzer ausgeführt wurde? Insbesondere bei Verwendung der Zurück-Taste kann ein Browser zur letzten bekannten Bildlaufposition springen. Wenn ich an ein Bildlaufereignis binde, wie kann ich feststellen, ob dies vom Benutzer oder vom Browser verursacht wurde?

$(document).scroll( function(){ 
    //who did this?!
});

Ich sehe drei Arten von Situationen, die ein Scrollen in einem Browser verursachen.

  1. Der Benutzer führt eine Aktion aus. Verwendet beispielsweise Mausrad, Pfeiltasten, Bild-auf / ab-Tasten, Home- / End-Tasten.
  2. Der Browser scrollt automatisch. Wenn Sie beispielsweise die Zurück-Schaltfläche in Ihrem Browser verwenden, wird automatisch zur letzten bekannten Bildlaufposition gesprungen.
  3. Javascript-Schriftrollen. Zum Beispiel element.scrollTo(x,y).
mrtsherman
quelle
1
Ich bin mir aus Ihrer Frage nicht sicher, ob Sie den Sprung mit dem Back Botton für mich als Scroll-Ereignis des Browsers oder des Benutzers betrachten. Allgemein: Was halten Sie vom "Scrollen mit dem Browser"? Wenn Sie meinen, dass das Scrollen von Ihrem Skript initiiert wird, müssen Sie nur scrollen, um entweder den Ereignishandler zu deaktivieren oder ein Flag zu setzen, damit der Ereignishandler ihn ignorieren kann.
RoToRa
Ich betrachtete das Scrollen über die Zurück-Schaltfläche als "Browser-Scroll". Alles andere - Mausrad, Aufwärts- / Abwärtspfeile, Klicken auf die mittlere Taste usw. - wäre ein Benutzerscroll. Ich denke, meine eigentliche Frage könnte sein: Gibt es eine Möglichkeit zu unterscheiden, woher ein Ereignis kam? Ich habe mir die Eigenschaften des Ereignisobjekts angesehen, konnte aber nichts finden. Die drei Szenarien, die ich mir vorstellen kann, sind vom Browser initiiertes Scrollen, von Javascript initiiertes Scrollen und vom Benutzer initiiertes Scrollen. Hoffe das macht die Dinge klarer.
Mrtsherman
@ Mrtsherman Ich habe einige davon gefunden, während ich die gleiche Ausgabe erzielt habe
AlphaMale

Antworten:

28

Leider gibt es keine direkte Möglichkeit, dies zu sagen.

Ich würde sagen, wenn Sie Ihre App so umgestalten können, dass sie nicht von dieser Art von Flow abhängt, entscheiden Sie sich dafür.

Wenn nicht, besteht eine Problemumgehung darin, vom Benutzer initiierte Schriftrollen zu verfolgen und zu überprüfen, ob der Bildlauf vom Browser oder vom Benutzer ausgelöst wurde.

Hier ist ein Beispiel, das ich zusammengestellt habe und das dies ziemlich gut macht (mit Ausnahme von Browsern, mit denen der jQuery-Verlauf Probleme hat).

Sie müssen dies lokal ausführen, um es vollständig testen zu können (jsFiddle / jsbin passen nicht gut zusammen, da sie den Inhalt iFrame).

Hier sind die Testfälle , die ich validiert habe:

  • Seitenladevorgänge - userScrollistfalse
  • Scrollen mit Maus / Tastatur - userScrollwirdtrue
  • Klicken Sie auf den Link, um zur Seite unten zu springen - userScrollwirdfalse
  • Klicken Sie auf Zurück / Vorwärts - userScrollwird false;

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="utf-8" /> 
    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script> 
    <script type="text/javascript" src="https://raw.github.com/tkyk/jquery-history-plugin/master/jquery.history.js"></script> 
</head> 
<body> 
    <span> hello there </span><br/> 
    <a href="#bottom"> click here to go down </a> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <a name="bottom"> just sitting </a> 
</body> 
<script type="text/javascript"> 

var userScroll = false;     

function mouseEvent(e) { 
    userScroll = true; 
} 


$(function() { 

    // reset flag on back/forward 
    $.history.init(function(hash){ 
        userScroll = false; 
    }); 

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

    // detect user scroll through mouse
    // Mozilla/Webkit 
    if(window.addEventListener) {
        document.addEventListener('DOMMouseScroll', mouseEvent, false); 
    }

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


    // to reset flag when named anchors are clicked
    $('a[href*=#]').click(function() { 
        userScroll = false;
    }); 

      // detect browser/user scroll
    $(document).scroll( function(){  
        console.log('Scroll initiated by ' + (userScroll == true ? "user" : "browser"));
    });
}); 
</script> 
</html>

Anmerkungen:

  • Das Scrollen wird nicht verfolgt, wenn der Benutzer die Bildlaufleiste mit der Maus zieht. Dies kann mit etwas mehr Code hinzugefügt werden, den ich als Übung für Sie hinterlassen habe.
  • event.keyCodes kann je nach Betriebssystem variieren, daher müssen Sie dies möglicherweise entsprechend ändern.

Hoffe das hilft!

Mrchief
quelle
Vielen Dank, Mrchief. Ich denke, dies ist die beste Antwort, obwohl es nicht das ist, was ich mir erhofft hatte (Was? Es gibt kein event.throwerEigentum ?! ).
Mrtsherman
Sie müssen die Steuerung nicht drücken, wenn Sie die Home- und End-Taste zum Scrollen verwenden.
Curtis
DOMMouseScroll funktioniert nicht mit dem neuesten Chrome auf dem Mac. Verwenden Sie am besten document.addEventListener ('Mausrad' anstatt auf Mausrad zu setzen
Curtis
8

Soweit ich weiß, ist es unmöglich (ohne Arbeit) zu sagen, wann ein Bildlaufereignis von "Benutzer" oder auf andere Weise ausgegeben wurde.

Sie könnten versuchen (wie bereits erwähnt), Mausradereignisse abzufangen, und dann wahrscheinlich versuchen, das Keydown-Ereignis auf allen Tasten abzufangen, die einen Bildlauf auslösen können (Pfeile, Leerzeichen usw.), während Sie überprüfen, welches Element derzeit fokussiert ist, da Sie beispielsweise nicht mit scrollen können Pfeiltasten beim Eingeben eines Eingabefeldes. Im Allgemeinen wäre das ein komplexes und chaotisches Skript.

Abhängig von der Situation, mit der Sie es zu tun haben, könnte ich "die Logik zurücksetzen", und anstatt vom Benutzer ausgegebene Bildlaufereignisse zu erkennen, schließen Sie sich einfach an alle programmgesteuert erstellten Bildläufe an und behandeln Sie alle von Ihrem Code nicht erstellten Bildlaufereignisse wie von einem Benutzer erstellt. Wie ich schon sagte, es hängt von einer Situation ab und davon, was Sie erreichen wollen.

WTK
quelle
Danke WTK. Dies war die beste Antwort für eine Weile, aber leider hat Mrchief Ihre Antwort mit einigen Codebeispielen erläutert, also habe ich ihm das Kopfgeld gegeben. Wenn ich es hätte teilen können, hätte ich es getan!
Mrtsherman
4
Das ist okay. Es geht nicht um Kopfgeld, sondern darum, Wissen zu verbreiten und zu erlangen :)
WTK
7

Anstatt zu versuchen, alle Benutzerereignisse zu erfassen, ist es viel einfacher, das Gegenteil zu tun und nur die programmatischen Ereignisse zu behandeln - und diese zu ignorieren.

Zum Beispiel würde diese Art von Code funktionieren:

// Element that needs to be scrolled
var myElement = document.getElementById('my-container');

// Flag to tell if the change was programmatic or by the user
var ignoreNextScrollEvent = false;

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

myElement.addEventListener('scroll', function() {
    if (ignoreNextScrollEvent) {
        // Ignore this event because it was done programmatically
        ignoreNextScrollEvent = false;
        return;
    }

    // Process user-initiated event here
});

Wenn Sie dann anrufen setScrollTop(), wird das Bildlaufereignis ignoriert. Wenn der Benutzer mit der Maus, der Tastatur oder auf andere Weise blättert, wird das Ereignis verarbeitet.

Laurent
quelle
2
Dies erkennt / ignoriert keine vom Browser ausgelösten Bildlaufereignisse.
mfluehr
Ich habe über etwas Ähnliches nachgedacht, das hinzugefügt werden muss - dies berücksichtigt nicht, dass das
Bildlaufverhalten
3

Ja, das ist zu 100% möglich. Ich verwende dies derzeit in einer Anwendung, in der IE nicht erforderlich ist - nur für Kunden. Wenn meine Backbone-App eine Animation initiiert, bei der der Bildlauf geändert wird, wird ein Bildlauf ausgeführt, der diese Ereigniserfassungen jedoch nicht auslöst. Dies wird in FF, Safari & Chrome spätestens getestet.

$('html, body').bind('scroll mousedown wheel DOMMouseScroll mousewheel keyup', function(evt) {
  // detect only user initiated, not by an .animate though
    if (evt.type === 'DOMMouseScroll' || evt.type === 'keyup' || evt.type === 'mousewheel') {
    // up
    if (evt.originalEvent.detail < 0 || (evt.originalEvent.wheelDelta && evt.originalEvent.wheelDelta > 0)) { 
    // down.
    } else if (evt.originalEvent.detail > 0 || (evt.originalEvent.wheelDelta && evt.originalEvent.wheelDelta < 0)) { 
  }
}
}); 

quelle
1
Danke für den Vorschlag. Dies ist jedoch eine binäre Lösung - erkennt Javascript-initiierte Bildlauf im Vergleich zu Nicht-JS-Bildlauf. Ich brauche wirklich eine ternäre Lösung. (Javascript, Benutzer, Browser).
Mrtsherman
2

Versuchen Sie stattdessen, die Ereignisse Mousewheel und DOMMouseScroll zu verwenden. Siehe http://www.quirksmode.org/dom/events/scroll.html

Gerben
quelle
Vielen Dank für diesen Vorschlag. Ich glaube jedoch nicht, dass dies das hat, was ich will. Ich muss tatsächlich zwischen Browser- und Benutzer-Bildlauf unterscheiden. Nicht nur auf das Mausrad zielen.
Mrtsherman
Ich denke, das Mausrad-Ereignis wird vor dem Onscroll-Ereignis ausgelöst. Sie können also var userscroll = true für das Mausrad festlegen und in onscroll erkennen (und auf false zurücksetzen).
Gerben
3
Es gibt eine große Anzahl von vom Benutzer initiierten Bildlaufereignissen, die jedoch nicht behandelt werden. Pfeiltasten, Fenstergröße ändern usw. Es ist viel sicherer zu sagen, dass der Absender dieses Ereignisses "X" ist, als zu sagen, dass alles außerhalb dieses Ereignisses "X" sein muss. Es funktioniert auch nicht für mich, unabhängig davon, ob ich einen vom Browser initiierten Bildlauf identifizieren möchte, nicht vom Benutzer. Um diesen Anwendungsfall zu erstellen, müsste ich alle Mausrad-Schriftrollen verfolgen und dann versuchen, daraus abzuleiten, ob das nachfolgende Bildlaufereignis ein Benutzer war. Ich fürchte, es ist einfach nicht praktikabel.
Mrtsherman
2

Sie können die Bildlaufposition auf Bereit überprüfen. Wenn Sie das Ereignis beim Scrollen auslösen, überprüfen Sie, ob die Bildlaufposition anders ist als beim Laden der Seite. Zuletzt müssen Sie den gespeicherten Wert löschen, sobald die Seite gescrollt ist.

$(function () {
    var loadScrollTop = ($(document).scrollTop() > 0 ? $(document).scrollTop() : null);
    $(document).scroll(function (e) {
        if ( $(document).scrollTop() !== loadScrollTop) {
            // scroll code here!
        }
        loadScrollTop = null;
    });
});
Rostige Jeans
quelle
Vielen Dank für diese Idee. Es gibt einige Szenarien, die dies für mich nicht ganz abdeckt. Obwohl es eine gute Idee ist und ich es in meiner Gesäßtasche aufbewahren werde, wenn ich es jemals brauche.
Mrtsherman
Einfach und in den meisten Fällen effektiv.
Aaron
0

Bezüglich:

Insbesondere bei Verwendung der Zurück-Taste kann ein Browser zur letzten bekannten Bildlaufposition springen.

Das wird sehr bald ausgelöst, nachdem die Seite gerendert wurde. Sie können das Listening des Bildlaufereignisses nur um ca. 1 Sekunde verzögern.

Firsh - LetsWP.io
quelle
2
Dies wäre keine robuste Lösung. Die Sprungzeit hängt von der Ladezeit der Seite ab. Der Browser verzögert das Springen, bis die Seite vollständig geladen ist. Dadurch wird verhindert, dass Seitenelemente, die die Seitenhöhe laden und ändern (z. B. Bilder), nicht mehr angezeigt werden.
Mrtsherman
0

Es gibt noch eine weitere Möglichkeit, den vom Benutzer erstellten Bildlauf zu trennen: Sie können die alternativen Ereignishandler verwenden, z. B. "Mausrad", "Touchmove", "Keydown" mit den Codes 38 und 40 für das Scrollen mit Pfeilen und zum Scrollen mit Bildlaufleiste - wenn Das Ereignis 'scroll' wird gleichzeitig mit dem Ereignis 'mousedown' bis zum Ereignis 'mouseup' ausgelöst.

Alex Kuzmin
quelle
0

Es hilft möglicherweise nicht bei Ihrer Anwendung, aber ich musste ein Ereignis beim Scrollen des Benutzers auslösen, aber nicht beim programmgesteuerten Scrollen und Posten, falls es anderen hilft.

Hören Sie sich die wheelVeranstaltung statt scroll,

Es wird ausgelöst, wenn ein Benutzer das Mausrad oder das Tracker-Pad verwendet (was meiner Meinung nach die meisten Leute sowieso scrollen) und wird nicht ausgelöst, wenn Sie programmgesteuert scrollen. Ich habe es verwendet, um zwischen einer Benutzer-Bildlaufaktion und Bildlauffunktionen zu unterscheiden.

https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event


element.addEventListener('wheel', (event) => {
       //do user scroll stuff here
})

Eine Einschränkung ist, dass wheelbeim Scrollen auf Mobilgeräten nicht ausgelöst wird. Daher habe ich überprüft, ob das Gerät mobil ist, und ähnliche Funktionen verwendet

if(this.mobile){
  element.addEventListener('scroll', (event) => {
       //do mobile scroll stuff here
  })
}
TimothyBuktu
quelle
-1

Fand das sehr nützlich. Hier ist eine Coffeescript-Version für diejenigen, die so geneigt sind.

$ ->
  S.userScroll = false

  # reset flag on back/forward
  if $.history?
    $.history.init (hash) ->
      S.userScroll = false

  mouseEvent = (e)->
    S.userScroll = true

  $(document).keydown (e) ->
    importantKey =  (e.which == 33 or        # page up
      e.which == 34 or    # page dn
      e.which == 32 or    # spacebar
      e.which == 38 or    # up
      e.which == 40 or    # down
      (e.ctrlKey and e.which == 36) or    # ctrl + home
      (e.ctrlKey and e.which == 35)    # ctrl + end
    )
    if importantKey
      S.userScroll = true;

  # Detect user scroll through mouse
  # Mozilla/Webkit
  if window.addEventListener
      document.addEventListener('DOMMouseScroll', mouseEvent, false);

  # for IE/OPERA etc
  document.onmousewheel = mouseEvent;
nottombrown
quelle
-1

Wenn Sie JQuery verwenden, gibt es anscheinend eine bessere Antwort - ich probiere es nur selbst aus.

Siehe: Jquery-Ereignisauslöser vom Benutzer erkennen oder per Code aufrufen

nathan g
quelle
Danke für den Vorschlag. Dies ist jedoch eine binäre Lösung - erkennt Javascript-initiierte Bildlauf im Vergleich zu Nicht-JS-Bildlauf. Ich brauche wirklich eine ternäre Lösung. (Javascript, Benutzer, Browser). Nebenbei bemerkt, jQuery ist keine Voraussetzung für Ihre vorgeschlagene Lösung.
Mrtsherman