Подтвердить что ты не робот

Определить, было ли создано событие прокрутки пользователем

Можно ли определить, было ли событие прокрутки выполнено браузером или пользователем? В частности, при использовании кнопки "Назад" браузер может перейти в последнюю известную позицию прокрутки. Если я привяжу к событию прокрутки, как я могу узнать, вызвано ли это пользователем или браузером?

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

Я вижу три типа ситуаций, которые вызывают прокрутку в браузере.

  • Пользователь выполняет некоторые действия. Например, использует колесико мыши, клавиши со стрелками, клавиши вверх/вниз страницы, ключи дома/конца.
  • Браузер автоматически прокручивается. Например, при использовании кнопки "Назад" в вашем браузере он автоматически перейдет в последнюю известную позицию прокрутки.
  • Прокрутка Javascript. Например, element.scrollTo(x,y).
4b9b3361

Ответ 1

К сожалению, нет прямого способа сказать это.

Я бы сказал, если вы можете перепроектировать ваше приложение, чтобы оно не зависело от этого типа потока, пойдите для этого.

Если нет, обходным решением, о котором я могу думать, является отслеживание прокрученных пользователем прокруток и проверка этого, чтобы увидеть, была ли прокрутка запущена браузером или пользователем.

Вот пример, который я собрал, что делает это довольно хорошо (за исключением браузеров, с которыми имеет проблемы с jQuery history).

Вам нужно запустить это локально, чтобы иметь возможность полностью протестировать его (jsFiddle/jsbin не подходят для iFrame содержимого).

Здесь тестовые примеры, которые я проверил:

  • Загрузка страниц - userScroll false
  • Прокрутка с помощью мыши/клавиатуры - userScroll становится true
  • Нажмите на ссылку, чтобы перейти к нижней части страницы - userScroll станет false
  • Нажмите Back/Forward - userScroll станет 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>

Примечания:

  • Это не отслеживает прокрутку, когда пользователь перетаскивает полосу прокрутки с помощью мыши. Это может быть добавлено с помощью некоторого кода, который я оставил для вас как упражнение.
  • event.keyCodes может отличаться от ОС, поэтому вам может потребоваться соответствующее изменение.

Надеюсь, это поможет!

Ответ 2

Насколько я знаю, невозможно (без какой-либо работы) сказать, когда событие прокрутки выдается "пользователем" или другими способами.

Вы можете попробовать (как упоминалось в других публикациях) уловить мышиные события, а затем, вероятно, попытаться поймать событие keydown на любых клавишах, которые могут запускать прокрутку (стрелки, пробел и т.д.), проверяя, какой элемент в данный момент сосредоточен, поскольку вы, например, t с помощью клавиш со стрелками при вводе в поле ввода. В общем, это было бы сложно и беспорядочно script.

В зависимости от ситуации, с которой вы имеете дело, вы можете предположить, что "верните логику", и вместо того, чтобы обнаруживать выпущенные пользователем события прокрутки, просто вставляйте их в любые прокрутки, сделанные программно, и обрабатывайте любые события прокрутки не сделанный вашим кодом как сделанный пользователем. Как я сказал, это зависит от ситуации и того, что вы пытаетесь достичь.

Ответ 3

Да, это на 100% возможно. В настоящее время я использую это в приложении, где IE не является требованием - клиент сталкивается только. Когда мое приложение Backbone инициирует анимацию, в которой изменяется прокрутка, прокрутка происходит, но не вызывает эти захваты событий. Это проверено в последних версиях FF, Safari и Chrome.

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

Ответ 5

Вместо того, чтобы пытаться поймать все пользовательские события, гораздо проще сделать обратное и обрабатывать только программные события - и игнорировать их.

Например, такой код будет работать:

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

Затем, когда вы вызываете setScrollTop(), событие прокрутки будет проигнорировано, а если пользователь прокручивает с помощью мыши, клавиатуры или любым другим способом, событие будет обработано.

Ответ 6

Вы можете проверить положение прокрутки в режиме готовности. Когда вы запускаете проверку события прокрутки, чтобы убедиться, что положение прокрутки отличается от того, что было при загрузке страницы. Наконец, обязательно очистите сохраненное значение после прокрутки страницы.

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

Ответ 7

Нашел это очень полезным. Здесь версия для кофиса для тех, кто так склонен.

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

Ответ 9

Относительно:

В частности, при использовании кнопки "Назад" браузер может перейти в последнюю известную позицию прокрутки.

Это срабатывает очень скоро после отображения страницы. Вы можете просто отложить прослушивание события прокрутки на 1 секунду или около того.

Ответ 10

Есть еще один способ отделить созданную пользователем прокрутку: вы можете использовать альтернативные обработчики событий, например, 'mousewheel', 'touchmove', 'keydown' с кодами 38 и 40 для прокрутки стрелок, для прокрутки с помощью полосы прокрутки - если Событие scroll вызывается одновременно с mousedown до события mouseup.

Ответ 11

У меня есть очень грязное решение, которое довольно специфично в его сценарии использования, но я решил поделиться им здесь в надежде, что кто-то может извлечь из него пользу или понять.

Контекст:

У меня есть страница с "постоянными ссылками", кнопки, которые ставятся рядом с каждым основным разделом, чтобы дать пользователю удобную ссылку на этот конкретный раздел, например, <a href="#introduction">. Я всегда ненавидел то, как, когда вы нажимаете, чтобы перейти к разделу, а затем прокручивать где-то еще в документе, # хеш в URL остается. Таким образом, если вы обновите страницу, вы unintuitively вернуться к любой последней # секции вы в последний раз щелкнули, вместо страницы освежающей "на месте", как браузеры обычно делают.

Я хотел, чтобы хеш в URL исчез, когда пользователь прокручивал страницу, поэтому у меня было что-то вроде следующего:

window.onscroll = function() {
    if (window.location.hash)
        history.pushState(null, null, " ");
}

Проблема в том, что он обнаруживает прокрутку всех окон. Когда пользователь нажимает на ссылку, чтобы перейти к разделу, страница прокручивается до этого раздела. Технически это тоже прокрутка страницы, поэтому приведенный выше код захватывает ее и удаляет хеш.

То, что я хочу, это чтобы ушел #, когда пользователь прокручивает, но ничего больше. Поскольку у нас нет четкого разделения всех различных причин 1) the scroll was caused by clicking a link, OR when the page loads and navigates to a section, мне пришлось разделить его на следующее: 1) the scroll was caused by clicking a link, OR when the page loads and navigates to a section и 2) every other cause of a scroll.

Поэтому, когда страница загружается, я устанавливаю глобальный флаг (вот так):

window.anchorScroll = true

Для случаев, когда пользователь сначала переходит на страницу, с # в URL.

Затем я также устанавливаю флаг при нажатии на ссылку/ссылку:

window.anchorScroll = true

Затем, когда страница прокручивается, я делаю это:

if (window.anchorScroll)
    window.anchorScroll = false; // unset the flag so the next time the page is scrolled, it assumed to be because of the user
else
    if (window.location.hash) // if there is a hash
        history.pushState(null, null, " "); // remove the hash without refreshing page

Это работает достаточно хорошо для моего случая, хотя это грязно. Следует также отметить, что это предполагает, что событие onclick мыши всегда запускается перед событием onscroll окна. Кажется, что это так, и было бы разумно, чтобы это всегда было в таком порядке, но я не могу найти какую-либо документацию/спецификацию, которая говорит, что это определенно должно быть так.