Phát hiện xem sự kiện cuộn có được tạo bởi người dùng hay không


82

Có thể biết liệu một sự kiện cuộn được thực hiện bởi trình duyệt hay bởi người dùng không? Cụ thể, khi sử dụng nút quay lại, trình duyệt có thể chuyển đến vị trí cuộn cuối cùng đã biết. Nếu tôi liên kết với sự kiện cuộn, làm thế nào tôi có thể biết liệu điều này là do người dùng hay trình duyệt gây ra?

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

Tôi thấy ba loại tình huống gây ra cuộn trong trình duyệt.

  1. Người dùng thực hiện một số hành động. Ví dụ, sử dụng con lăn chuột, phím mũi tên, phím lên / xuống trang, phím home / end.
  2. Trình duyệt sẽ tự động cuộn. Ví dụ: khi sử dụng nút quay lại trong trình duyệt của bạn, nút này sẽ tự động chuyển đến vị trí cuộn cuối cùng đã biết.
  3. Javascript cuộn. Ví dụ element.scrollTo(x,y),.

1
Tôi không chắc về câu hỏi của bạn, nếu bạn coi bước nhảy bằng cách sử dụng đáy quay lại với tôi là một sự kiện cuộn của trình duyệt hoặc người dùng. Nói chung: Bạn coi "cuộn bằng trình duyệt" là gì? Nếu ý của bạn là quá trình cuộn được khởi tạo bởi tập lệnh của bạn, thì tất cả những gì bạn cần làm là khi tập lệnh của bạn cuộn, để hủy kích hoạt trình xử lý sự kiện hoặc đặt cờ để trình xử lý sự kiện biết bỏ qua nó.
RoToRa

Tôi coi cuộn qua nút quay lại là "cuộn trình duyệt". Bất cứ điều gì khác - con lăn chuột, mũi tên lên / xuống, nhấp vào nút trung tâm, v.v. sẽ là cuộn người dùng. Tôi đoán câu hỏi thực sự của tôi có thể là - có cách nào để phân biệt một sự kiện đến từ đâu không? Tôi đã xem xét các thuộc tính trên đối tượng sự kiện, nhưng không thể tìm thấy bất kỳ thứ gì. Ba trường hợp tôi có thể tưởng tượng là cuộn do trình duyệt khởi tạo, cuộn do javascript khởi tạo và cuộn do người dùng khởi tạo. Hy vọng điều đó làm cho mọi thứ rõ ràng hơn.
mrtsherman

@mrtsherman tôi thấy một số trong những trong khi đạt được kết quả tương tự: stackoverflow.com/questions/2834667/...
AlphaMale

Câu trả lời:


30

Thật không may, không có cách nào trực tiếp để nói điều đó.

Tôi sẽ nói nếu bạn có thể thiết kế lại ứng dụng của mình để nó không phụ thuộc vào loại quy trình này, hãy làm điều đó.

Nếu không, một cách giải quyết mà tôi có thể nghĩ đến là theo dõi các lần cuộn do người dùng khởi tạo và kiểm tra xem liệu cuộn được kích hoạt bởi trình duyệt hay bởi người dùng.

Đây là một ví dụ mà tôi đã tổng hợp lại để thực hiện điều này khá tốt (ngoại trừ các trình duyệt mà lịch sử jQuery có vấn đề).

Bạn cần chạy cục bộ này để có thể kiểm tra toàn bộ (jsFiddle / jsbin không phù hợp vì chúng iFrame nội dung).

Đây là các trường hợp thử nghiệm mà tôi đã xác thực:

  • Số lượt tải trang - userScrollfalse
  • Cuộn bằng chuột / bàn phím - userScrolltrở thànhtrue
  • Nhấp vào liên kết để chuyển đến cuối trang - userScrolltrở thànhfalse
  • Nhấp vào Quay lại / Chuyển tiếp - userScrolltrở thành 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>

Ghi chú:

  • Điều này không theo dõi quá trình cuộn khi người dùng kéo thanh cuộn bằng chuột. Điều này có thể được thêm vào với một số mã khác, mà tôi đã để lại như một bài tập cho bạn.
  • event.keyCodes có thể thay đổi theo hệ điều hành, vì vậy bạn có thể phải thay đổi phù hợp.

Hi vọng điêu nay co ich!


Cảm ơn Mrchief. Tôi nghĩ rằng đây là câu trả lời tốt nhất mặc dù nó không phải là những gì tôi hy vọng nó sẽ là (Cái gì? Không có event.throwertài sản?!).
mrtsherman

Bạn không cần phải nhấn điều khiển khi sử dụng phím home và end để cuộn.
Curtis

DOMMouseScroll không hoạt động trên Chrome mới nhất trên máy Mac. Tốt nhất nên sử dụng document.addEventListener ('mousewheel' thay vì đặt onmousewheel
Curtis

8

Theo như tôi biết thì không thể (không có bất kỳ công việc nào) để biết bất cứ khi nào sự kiện cuộn được phát hành bởi "người dùng" hoặc bằng các phương tiện khác.

Bạn có thể thử (như những người khác đã đề cập) bắt các sự kiện con lăn chuột, sau đó có thể cố gắng bắt sự kiện phím xuống trên bất kỳ phím nào có thể kích hoạt cuộn (mũi tên, dấu cách, v.v.) trong khi kiểm tra xem phần tử nào hiện đang được lấy tiêu điểm, vì ví dụ: bạn không thể cuộn bằng các phím mũi tên trong khi nhập vào trường nhập. Nói chung đó sẽ là kịch bản phức tạp và lộn xộn.

Tùy thuộc vào tình huống bạn đang xử lý, tôi có thể đoán "hoàn nguyên logic" và thay vì phát hiện các sự kiện cuộn do người dùng cấp, chỉ cần kết nối vào bất kỳ cuộn nào được tạo theo chương trình và coi mọi sự kiện cuộn không do mã của bạn thực hiện như do người dùng tạo. Như tôi đã nói, nó phụ thuộc vào tình huống và những gì bạn đang cố gắng đạt được.


Cảm ơn WTK. Đây là câu trả lời hay nhất trong một thời gian, nhưng không may cho bạn, Mrchief giải thích câu trả lời của bạn bằng một số ví dụ mã nên tôi đã trao tiền thưởng cho anh ta. Nếu tôi có thể tách nó ra, tôi sẽ có!
mrtsherman

4
Vậy là được rồi. Nó không phải là về tiền thưởng mà là về việc truyền bá và thu thập kiến ​​thức :)
WTK

8

Thay vì cố gắng bắt tất cả các sự kiện của người dùng, việc làm ngược lại và chỉ xử lý các sự kiện theo chương trình sẽ dễ dàng hơn nhiều - và bỏ qua những sự kiện đó.

Ví dụ, loại mã này sẽ hoạt động:

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

Sau đó, khi bạn gọi setScrollTop(), sự kiện cuộn sẽ bị bỏ qua, trong khi nếu người dùng cuộn bằng chuột, bàn phím hoặc bất kỳ cách nào khác, sự kiện sẽ được xử lý.


2
Điều này không phát hiện / bỏ qua các sự kiện cuộn do trình duyệt kích hoạt.
mfluehr

Tôi đã nghĩ về một cái gì đó tương tự phải thêm vào - điều này không tính đến việc hành vi cuộn có thể được đặt thành 'mượt mà'
Kamil Bęben

3

Vâng, nó là 100% có thể. Tôi hiện đang sử dụng điều này trong một ứng dụng mà IE không phải là một yêu cầu - chỉ đối mặt với khách hàng. Khi ứng dụng Backbone của tôi bắt đầu hoạt ảnh nơi cuộn được thay đổi - cuộn xảy ra nhưng không kích hoạt các lần chụp sự kiện này. Điều này được thử nghiệm trong FF, Safari và Chrome mới nhất.

$('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
Cám ơn vì sự gợi ý. Tuy nhiên, đây là một giải pháp nhị phân - phát hiện cuộn bắt đầu bằng javascript so với cuộn không phải js. Tôi thực sự cần một giải pháp bậc ba. (javascript, người dùng, trình duyệt).
mrtsherman

2

Thay vào đó, hãy thử sử dụng sự kiện Mousewheel và DOMMouseScroll. Xem http://www.quirksmode.org/dom/events/scroll.html


Cảm ơn bạn đã gợi ý này. Tuy nhiên tôi không nghĩ rằng điều này có những gì tôi muốn. Tôi thực sự cần phân biệt giữa cuộn trình duyệt và người dùng. Không chỉ nhắm mục tiêu con lăn chuột.
mrtsherman

Tôi nghĩ rằng sự kiện con lăn chuột được kích hoạt trước sự kiện trên cuộn. Vì vậy, bạn có thể đặt var userscroll = true trên mousewheel và phát hiện nó trong onscroll (và đặt lại thành false).
Gerben

3
Mặc dù vậy, có một số lượng lớn các sự kiện cuộn do người dùng khởi tạo sẽ không được đề cập. Phím mũi tên, thay đổi kích thước cửa sổ, v.v. Sẽ an toàn hơn nhiều nếu nói người gửi của sự kiện này là "X" hơn là nói mọi thứ bên ngoài sự kiện này phải là "X." Nó cũng không hoạt động đối với tôi bất kể vì tôi muốn xác định một cuộn do trình duyệt khởi tạo, không phải bởi người dùng. Vì vậy, để thực hiện trường hợp sử dụng này, tôi sẽ phải theo dõi tất cả các lần cuộn con lăn chuột và sau đó cố gắng suy luận xem sự kiện cuộn tiếp theo có phải là người dùng dựa trên đó hay không. Tôi sợ rằng nó chỉ là không thể hoạt động.
mrtsherman

2

Bạn có thể kiểm tra vị trí cuộn đã sẵn sàng. Khi bạn kích hoạt kiểm tra sự kiện khi cuộn để đảm bảo vị trí cuộn khác với vị trí khi trang được tải. Cuối cùng, hãy chắc chắn xóa giá trị được lưu trữ khi trang được cuộn.

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

Cảm ơn bạn vì ý tưởng này. Có một số tình huống mà điều này không phù hợp với tôi. Mặc dù đó là một ý kiến ​​hay và tôi sẽ giữ nó trong túi sau nếu tôi cần.
mrtsherman

Đơn giản và hiệu quả trong hầu hết các trường hợp.
Aaron

0

Liên quan đến:

Cụ thể, khi sử dụng nút quay lại, trình duyệt có thể chuyển đến vị trí cuộn cuối cùng đã biết.

Điều đó xảy ra rất sớm, sau khi trang được hiển thị. Bạn chỉ có thể trì hoãn việc đưa vào sự kiện cuộn 1 giây hoặc lâu hơn.


2
Đây sẽ không phải là một giải pháp mạnh mẽ. Thời gian để nhảy phụ thuộc vào thời gian tải trang. Trình duyệt trì hoãn nhảy cho đến khi trang được tải hoàn toàn. Điều này ngăn không cho các phần tử trang tải và thay đổi chiều cao trang (như hình ảnh) cuộn ra khỏi chế độ xem.
mrtsherman

0

Có một cách khác để tách cuộn do người dùng tạo: bạn có thể sử dụng các trình xử lý sự kiện thay thế, ví dụ: 'mousewheel', 'touchmove', 'keydown' với mã 38 và 40 cho cuộn mũi tên, để cuộn với thanh cuộn - nếu sự kiện "scroll" được kích hoạt đồng thời với "mousedown" cho đến sự kiện "mouseup".


-1

Tìm thấy điều này rất hữu ích. Đây là một phiên bản coffeescript cho những người có khuynh hướng như vậy.

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

nếu bạn đang sử dụng JQuery thì rõ ràng là có một câu trả lời tốt hơn - tôi chỉ đang tự mình thử.

xem: Phát hiện kích hoạt sự kiện jquery bởi người dùng hoặc cuộc gọi bằng mã


Cám ơn vì sự gợi ý. Tuy nhiên, đây là một giải pháp nhị phân - phát hiện cuộn bắt đầu bằng javascript so với cuộn không phải js. Tôi thực sự cần một giải pháp bậc ba. (javascript, người dùng, trình duyệt). Sidenote, jQuery không phải là yêu cầu cho giải pháp đạo cụ của bạn.
mrtsherman

-1

Nó có thể không hữu ích với ứng dụng của bạn, nhưng tôi cần kích hoạt một sự kiện trên cuộn của người dùng chứ không phải cuộn theo chương trình và đăng trong trường hợp nó giúp ích cho bất kỳ ai khác.

Hãy lắng nghe wheelsự kiện thay vì scroll,

Nó được kích hoạt bất cứ khi nào người dùng sử dụng con lăn chuột hoặc tracker pad (tôi cảm thấy rằng hầu hết mọi người vẫn cuộn như thế nào) và không được kích hoạt khi bạn cuộn theo chương trình. Tôi đã sử dụng nó để phân biệt giữa hành động cuộn của người dùng và các chức năng cuộn.

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


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

Một lưu ý là wheelnó không kích hoạt khi cuộn trên thiết bị di động, vì vậy tôi đã kiểm tra xem thiết bị có phải là thiết bị di động hay không và sử dụng các chức năng tương tự

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.