Kaydırma olayının kullanıcı tarafından oluşturulup oluşturulmadığını tespit edin


84

Bir kaydırma olayının tarayıcı tarafından mı yoksa kullanıcı tarafından mı yapıldığını söylemek mümkün mü? Özellikle, geri düğmesini kullanırken bir tarayıcı bilinen son kaydırma konumuna atlayabilir. Kaydırma olayına bağlanırsam, bunun kullanıcıya mı yoksa tarayıcıdan mı kaynaklandığını nasıl anlayabilirim?

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

Bir tarayıcıda kaydırmaya neden olan üç tür durum görüyorum.

  1. Kullanıcı bazı eylemler gerçekleştirir. Örneğin, fare tekerleği, ok tuşları, sayfa yukarı / aşağı tuşları, ana sayfa / son tuşlarını kullanır.
  2. Tarayıcı otomatik olarak kayar. Örneğin, tarayıcınızda geri düğmesini kullandığınızda, otomatik olarak bilinen en son kaydırma konumuna atlayacaktır.
  3. Javascript parşömenleri. Örneğin element.scrollTo(x,y),.

1
Bana geri bottonunu kullanarak atlamayı tarayıcının veya kullanıcının bir kaydırma olayını düşünüyorsanız, sorunuzdan emin değilim. Genel olarak: "Tarayıcıya göre kaydırmayı" ne düşünüyorsunuz? Komut dosyanız tarafından başlatılan kaydırmayı kastediyorsanız, yapmanız gereken tek şey, komut dosyası kaydırıldığında, olay işleyiciyi devre dışı bırakmak veya olay işleyicisinin onu yoksayacağını bilmesi için bir bayrak ayarlamaktır.
RoToRa

Geri düğmesiyle kaydırmayı bir "tarayıcı kaydırma" olarak düşündüm. Başka herhangi bir şey - fare tekerleği, yukarı / aşağı oklar, orta düğme tıklaması vb. Bir kullanıcı kaydırması olacaktır. Sanırım asıl sorum şu olabilir - bir olayın nereden geldiğini ayırt etmenin bir yolu var mı? Olay nesnesindeki özelliklere baktım ama hiçbir şey bulamadım. Hayal edebileceğim üç senaryo, tarayıcı tarafından başlatılan kaydırma, javascript tarafından başlatılan kaydırma ve kullanıcı tarafından başlatılan kaydırmadır. Umarım bu işleri daha açık hale getirir.
mrtsherman

@mrtsherman Aynı çıktıyı elde ederken bunlardan bazılarını buldum: stackoverflow.com/questions/2834667/…
AlphaMale

Yanıtlar:


30

Ne yazık ki bunu söylemenin doğrudan bir yolu yok.

Uygulamanızı bu tür akışa bağlı kalmayacak şekilde yeniden tasarlayabilirseniz , bunun için gidin.

Değilse , düşünebildiğim bir çözüm , kullanıcı tarafından başlatılan kaydırmaları takip etmek ve kaydırmanın tarayıcı tarafından mı yoksa kullanıcı tarafından mı tetiklendiğini kontrol etmek.

İşte bunu oldukça iyi yapan bir araya getirdiğim örnek (jQuery geçmişinin sorun yaşadığı tarayıcılar hariç).

Tam olarak test edebilmek için bunu yerel olarak çalıştırmanız gerekir (jsFiddle / jsbin, içeriği iFrameledikleri için uygun değildir).

İşte doğruladığım test senaryoları:

  • Sayfa yükler - userScrollolduğufalse
  • Fare / klavye kullanarak Kaydırma - userScrollolurtrue
  • Sayfanın altına atlamak için bağlantıya tıklayın - userScrollolurfalse
  • Geri / İleri'ye tıklayın - userScrollolur 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>

Notlar:

  • Bu, kullanıcı kaydırma çubuğunu fareyle sürüklediğinde kaydırmayı izlemez. Bu, sizin için alıştırma olarak bıraktığım biraz daha kodla eklenebilir.
  • event.keyCodes işletim sistemine göre değişebilir, bu nedenle bunu uygun şekilde değiştirmeniz gerekebilir.

Bu yardımcı olur umarım!


Teşekkür ederim Mrchief. Olmasını umduğum şey olmasa da en iyi yanıtın bu olduğunu düşünüyorum (Ne? event.throwerMülkiyet yok mu ? !).
mrtsherman

Kaydırmak için giriş ve bitir tuşlarını kullanırken kontrole basmanız gerekmez.
Curtis

DOMMouseScroll, Mac'teki en son Chrome'da çalışmaz. En iyisi document.addEventListener (tek bir tekerleği ayarlamak yerine 'fare tekerleği'
Curtis

9

Tüm kullanıcı olaylarını yakalamaya çalışmak yerine, tersini yapmak ve yalnızca programatik olayları işlemek ve bunları görmezden gelmek çok daha kolaydır.

Örneğin, bu tür bir kod çalışır:

// 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
});

Ardından aradığınızda setScrollTop(), kaydırma olayı yok sayılırken, kullanıcı fare, klavye veya başka bir yolla kaydırırsa, olay işlenecektir.


2
Bu, tarayıcı tarafından tetiklenen kaydırma olaylarını algılamaz / yok saymaz.
mfluehr

Eklemek zorunda benzer bir şey düşünüyordum - bu kaydırma davranışının 'pürüzsüz' olarak ayarlanabileceğini hesaba
katmıyor

8

Bildiğim kadarıyla, kaydırma olayının ne zaman "kullanıcı" tarafından veya başka yollarla yayınlandığını söylemek (herhangi bir çalışma olmadan) imkansız.

(Başkalarının da belirtildiği gibi) fare tekerleği olaylarını yakalamayı deneyebilir, ardından muhtemelen hangi öğenin odaklandığını kontrol ederken kaydırmayı tetikleyebilecek herhangi bir tuşta (oklar, boşluk vb.) Keydown olayını yakalamaya çalışabilirsiniz, çünkü örneğin giriş alanına yazarken ok tuşları. Genel olarak bu karmaşık ve dağınık bir senaryo olurdu.

Karşılaştığınız duruma bağlı olarak, "mantığı geri alabilir" ve kullanıcı tarafından verilen kaydırma olaylarını tespit etmek yerine, programatik olarak yapılmış herhangi bir parşömene bağlanın ve herhangi bir kaydırma olayını işleyin. değil , bir kullanıcı tarafından yapılan gibi kod tarafından yaptı. Dediğim gibi, bir duruma ve neyi başarmak istediğinize bağlıdır.


Teşekkürler WTK. Bu bir süre için en iyi cevaptı, ama ne yazık ki sizin için Baychief cevabınızı bazı kod örnekleriyle açıkladı, ben de ödülü ona verdim. Eğer bölüşebilseydim yapardım!
mrtsherman

4
Bu iyi. Bu ödülle ilgili değil, bilgiyi yaymak ve kazanmakla ilgili :)
WTK

3

Evet,% 100 mümkündür. Bunu şu anda IE'nin bir gereklilik olmadığı bir uygulamada kullanıyorum - yalnızca müşteriye dönük. Backbone uygulamam kaydırmanın değiştirildiği bir animasyonu başlattığında - kaydırma gerçekleşir, ancak bu olay yakalamalarını tetiklemez. Bu, en son FF, Safari ve Chrome'da test edilmiştir.

$('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)) { 
  }
}
}); 

1
Önerin için teşekkürler. Bununla birlikte, bu ikili bir çözümdür - javascript tarafından başlatılan kaydırmayı, js olmayan kaydırmayı algılar. Gerçekten üçlü bir çözüme ihtiyacım var. (javascript, kullanıcı, tarayıcı).
mrtsherman

2

Bunun yerine Mousewheel ve DOMMouseScroll olaylarını kullanmayı deneyin. Bkz. Http://www.quirksmode.org/dom/events/scroll.html


Bu öneri için teşekkür ederim. Ancak istediğim şeyin bu olduğunu sanmıyorum. Aslında tarayıcı ve kullanıcı kaydırma arasında ayrım yapmam gerekiyor. Sadece fare tekerleğini hedeflemez.
mrtsherman

Fare tekerleği olayının onscroll olayından önce tetiklendiğini düşünüyorum. Böylece, fare tekerleğinde var userscroll = true ayarlayabilir ve kaydırma sırasında algılayabilir (ve bunu false olarak sıfırlayabilirsiniz).
Gerben

3
Yine de ele alınmayan çok sayıda kullanıcı tarafından başlatılan kaydırma olayı vardır. Ok tuşları, pencere yeniden boyutlandırma vb. Bu olayı gönderenin "X" olduğunu söylemek, bu olay dışındaki her şeyin "X" olması gerektiğini söylemekten çok daha güvenlidir. Ayrıca kullanıcı tarafından değil tarayıcı tarafından başlatılan bir parşömeni tanımlamak istediğimden, benim için de çalışmıyor. Bu kullanım örneğini yapmak için, tüm fare tekerleği kaydırmalarını izlemem ve ardından sonraki kaydırma olayının buna dayalı olarak kullanıcı olup olmadığını anlamaya çalışmam gerekir. Korkarım işe yaramaz.
mrtsherman

2

Hazırda kaydırma konumunu kontrol edebilirsiniz. Kaydırma olayını başlattığınızda, kaydırma konumunun sayfanın yüklendiği zamandan farklı olduğundan emin olun. Son olarak, sayfa kaydırıldıktan sonra saklanan değeri temizlediğinizden emin olun.

$(function () {
    var loadScrollTop = ($(document).scrollTop() > 0 ? $(document).scrollTop() : null);
    $(document).scroll(function (e) {
        if ( $(document).scrollTop() !== loadScrollTop) {
            // scroll code here!
        }
        loadScrollTop = null;
    });
});

Bu fikir için teşekkür ederim. Bunun beni tam olarak kapsamadığı bazı senaryolar var. Yine de iyi bir fikir ve ihtiyacım olursa arka cebimde tutacağım.
mrtsherman

Basit ve çoğu durumda etkilidir.
Aaron

0

İle ilgili olarak:

Özellikle, geri düğmesini kullanırken bir tarayıcı bilinen son kaydırma konumuna atlayabilir.

Bu, sayfa oluşturulduktan hemen sonra tetiklenir. Kaydırma olayını dinlemeyi 1 saniye kadar geciktirebilirsiniz.


2
Bu sağlam bir çözüm olmayacaktır. Atlama süresi, sayfa yükleme süresine bağlıdır. Tarayıcı, sayfa tamamen yüklenene kadar atlamayı erteler. Bu, sayfa yüksekliğini yükleyen ve değiştiren (resimler gibi) sayfa öğelerinin görünümün dışına kaymasını önler.
mrtsherman

0

Kullanıcı tarafından oluşturulan kaydırmayı ayırmanın bir yolu daha vardır: alternatif olay işleyicileri kullanabilirsiniz, örneğin ok kaydırma için 38 ve 40 kodlu 'fare tekerleği', 'dokunma hareketi', 'tuş aşağı', kaydırma çubuğu ile kaydırma için - eğer 'scroll' olayı, 'mouseup' olayına kadar 'mousedown' ile aynı anda tetiklenir.


-1

Bunu çok faydalı buldum. İşte bu kadar eğilimli olanlar için kahvehane versiyonu.

$ ->
  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;

-1

Eğer JQuery'yi olduğundan daha iyi bir cevap kullanıyorsanız, görünüşe göre - sadece kendim deniyorum.

bkz: jquery olay tetikleyicisini kullanıcı veya kodla arama


Önerin için teşekkürler. Bununla birlikte, bu ikili bir çözümdür - javascript tarafından başlatılan kaydırmayı, js olmayan kaydırmayı algılar. Gerçekten üçlü bir çözüme ihtiyacım var. (javascript, kullanıcı, tarayıcı). Not, jQuery, yerleşik çözümünüz için bir gereklilik değildir.
mrtsherman

-1

Uygulamanıza yardımcı olmayabilir, ancak kullanıcı kaydırmasında bir etkinliği tetiklemem gerekiyordu, ancak programatik kaydırma ve yayınlama, başka birine yardımcı olursa.

wheelBunun yerine etkinliği dinleyinscroll ,

Bir kullanıcı fare tekerleğini veya izleme pedini kullandığında (ki çoğu insanın nasıl kaydırdığını hissettim) tetiklenir ve programlı olarak kaydırdığınızda tetiklenmez. Bunu, bir kullanıcı kaydırma eylemi ile kaydırma işlevleri arasında ayrım yapmak için kullandım.

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


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

Bir uyarı, wheelmobil cihazda kaydırma sırasında ateşlenmemesi, bu yüzden cihazın mobil olup olmadığını ve benzer işlevleri kullanıp kullanmadığını kontrol ettim.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.