Вопрос по jquery, events – Определите, щелкает ли пользователь по полосе прокрутки или по содержимому (щелчок по собственной полосе прокрутки)

49

Я пытаюсь создать пользовательские события в JQuery, которые должны обнаруживать нажатие на полосу прокрутки.1.
Я знаю, что там много текста, но все мои вопросы выделены жирным шрифтом и естьПример JSFiddle Вы можете работать прямо сейчас.

Потому что я не нашел никаких встроенных функций для этого,
Я должен был создатьhasScroll функция, проверяющая, есть ли у элемента полоса прокрутки,

<code>$.fn.hasScroll = function(axis){
    var overflow = this.css("overflow"),
        overflowAxis;

    if(typeof axis == "undefined" || axis == "y") overflowAxis = this.css("overflow-y");
    else overflowAxis = this.css("overflow-x");

    var bShouldScroll = this.get(0).scrollHeight > this.innerHeight();

    var bAllowedScroll = (overflow == "auto" || overflow == "visible") ||
                         (overflowAxis == "auto" || overflowAxis == "visible");

    var bOverrideScroll = overflow == "scroll" || overflowAxis == "scroll";

    return (bShouldScroll && bAllowedScroll) || bOverrideScroll;
};
</code>

иinScrollRange функция, проверяющая, был ли выполнен клик в пределах диапазона прокрутки.

<code>var scrollSize = 18;

function inScrollRange(event){
    var x = event.pageX,
        y = event.pageY,
        e = $(event.target),
        hasY = e.hasScroll(),
        hasX = e.hasScroll("x"),
        rX = null,
        rY = null,
        bInX = false,
        bInY = false

    if(hasY){
        rY = new RECT();
        rY.top = e.offset().top;
        rY.right = e.offset().left + e.width();
        rY.bottom = rY.top +e.height();
        rY.left = rY.right - scrollSize;

        //if(hasX) rY.bottom -= scrollSize;
        bInY = inRect(rY, x, y);
    }

    if(hasX){
        rX = new RECT();
        rX.bottom = e.offset().top + e.height();
        rX.left = e.offset().left;
        rX.top = rX.bottom - scrollSize;
        rX.right = rX.left + e.width();

        //if(hasY) rX.right -= scrollSize;
        bInX = inRect(rX, x, y);
    }

    return bInX || bInY;
}
</code>

Are all scrollbar sizes uniform? E.g in Firefox and IE it's 18px.
Предполагая, что нет настраиваемых полос прокрутки, есть ли какие-либо дополнительные отступы или размеры в некоторых браузерах?

Все эти функции работают как задумано (из того, что я могу различить).

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

Кажется, я не могу остановить запуск других событий, одновременно вызывая, как я это называю, события mousedownScroll / mouseupScroll.

<code>$.fn.mousedownScroll = function(fn, data){
    if(typeof fn == "undefined" && typeof data == "undefined"){
        $(this).trigger("mousedownScroll");
        return;
    }

    $(this).on("mousedownScroll", data, fn);
};

$.fn.mouseupScroll = function(fn, data){
    if(typeof fn == "undefined" && typeof data == "undefined"){
        $(this).trigger("mouseupScroll");
        return;
    }

    $(this).on("mouseupScroll", data, fn);
};

$(document).on("mousedown", function(e){
    if(inScrollRange(e)){
        $(e.target).trigger("mousedownScroll");
    }
});

$(document).on("mouseup", function(e){
    if(inScrollRange(e)){
        $(e.target).trigger("mouseupScroll");
    }
});

$("selector").mousedown(function(e){
    console.log("Clicked content."); //Fired when clicking scroller as well
});

$("selector").mousedownScroll(function(e){
    console.log("Clicked scroller.");
});
</code>

How do I stop the other "click" events from triggering?

Пока я спрашиваю, пожалуйста, не стесняйтесь максимально оптимизировать код.

Вот JSFiddle, с которым можно возиться.

Причина, по которой я это делаю, заключается в том, что я разрабатываю плагин большего размера. У него есть пользовательское контекстное меню, которое появляется, когда я щелкаю правой кнопкой мыши по одному из скроллеров. Я этого не хочу. Поэтому я подумал, что должен создать событие, которое проверяет щелчки при прокрутке (mouseup / downs), а затем предотвращает отображение контекстного меню. Однако, чтобы сделать это, мне нужно, чтобы щелчок прокрутки предшествовал обычному щелчку, а также, если возможно, не давал срабатывать обычным щелчкам.

I'm just thinking out loud here but может быть, есть способ получить все функции, которые привязаны к элементу, и затем изменить порядок, в котором они были добавлены? Я знаю, что функции выполняются в том порядке, в котором они были добавлены (1-й добавлен, 1-й вызван), поэтому, если бы я мог подключиться к этому процессу, возможно, весь процесс "регистрации" события в JQuery можно было просто вставить до события click.

1 can only use mousedown/mouseup because click doesn't trigger when clicking on a scrollbar. If this is false, please provide a working example/code

Ах понятно. Спасибо за разъяснение этого для меня. +1 и в избранное. Надеюсь увидеть ответ (я уверен, что вы тоже!) mrtsherman
Размеры полосы прокрутки не являются одинаковыми. В WebKit вы можете настроить их довольно сильно.css-tricks.com/examples/WebKitScrollbars mrtsherman
@mrtsherman Да, но они запускаютсяmousedown а такжеmouseup, Я называю их "нажмите" События в моем посте, а затем также объяснить это в самом низу. ShadowScripter
Пожалуйста, не меняйте стандартное поведение<select multiple> управления. Это очень раздражает, если они не ведут себя так, как ожидают пользователи. Например, нажмите Shift, чтобы выбрать диапазон, Ctrl-клик, чтобы выбрать конкретные элементы. В некоторых приложениях он есть, поэтому каждый щелчок по элементу переключает его состояние - и, конечно, это не то, что ожидается. Итак: оставьте поведение по умолчаниюor использовать полностью настраиваемый виджет, который не выглядит как обычный элемент управления, как это происходит, например в родном приложении ThiefMaster♦
@ThiefMaster Я уже знаю об этом, но этоshould not влияет на поведение по умолчанию прокрутки браузеров. Может быть, вы неправильно поняли, что я пытаюсь сделать? Смысл в том, чтобы добавить дополнительные события в JQuery, чтобы я мог заставить другие мои плагины делать что-то, когда на него нажимают. Больше функциональности - & gt; больше вариантов. В моем примере я создал пользовательское контекстное меню, но я не хочу, чтобы контекстное меню вызывалось, если пользователь щелкает правой кнопкой мыши по полосе прокрутки (имитирует то же поведение, что и меню браузера). Чтобы решить эту проблему, я подумал, что могу добавить событие, которое не позволяет всплыть контекстному меню. ShadowScripter

Ваш Ответ

10   ответов
1

Александр ответ, потому что он сделал это работает отлично, и upvoteОтвет Самуилапотому что он правильно рассчитывает ширину полосы прокрутки, что мне и нужно.

При этом я решил сделать два независимых события вместо того, чтобы пытаться перезаписать / переопределить JQuery.mousedown событие.

Это дало мне необходимую гибкость, не мешая собственным событиям JQuery, и было довольно легко сделать.

mousedownScroll mousedownContent

Ниже приведены две реализации, использующие Александер и мою собственную. Оба работают так, как я хотел, но первый, вероятно, лучший.

Here's a JSFiddle that implements Alexander's answer + Samuel's answer.

$.fn.hasScroll = function(axis){
    var overflow = this.css("overflow"),
        overflowAxis;

    if(typeof axis == "undefined" || axis == "y") overflowAxis = this.css("overflow-y");
    else overflowAxis = this.css("overflow-x");

    var bShouldScroll = this.get(0).scrollHeight > this.innerHeight();

    var bAllowedScroll = (overflow == "auto" || overflow == "visible") ||
                         (overflowAxis == "auto" || overflowAxis == "visible");

    var bOverrideScroll = overflow == "scroll" || overflowAxis == "scroll";

    return (bShouldScroll && bAllowedScroll) || bOverrideScroll;
};

$.fn.mousedown = function(data, fn) {
    if ( fn == null ) {
        fn = data;
        data = null;
    }    
    var o = fn;
    fn = function(e){
        if(!inScrollRange(e)) {
            return o.apply(this, arguments);
        }
        return;
    };
    if ( arguments.length > 0 ) {
        return this.bind( "mousedown", data, fn );
    } 
    return this.trigger( "mousedown" );
};

$.fn.mouseup = function(data, fn) {
    if ( fn == null ) {
        fn = data;
        data = null;
    }    
    var o = fn;
    fn = function(e){
        if(!inScrollRange(e)) {
            return o.apply(this, arguments);
        }
        return;
    };
    if ( arguments.length > 0 ) {
        return this.bind( "mouseup", data, fn );
    } 
    return this.trigger( "mouseup" );
};

$.fn.mousedownScroll = function(data, fn) {
    if ( fn == null ) {
        fn = data;
        data = null;
    }    
    var o = fn;
    fn = function(e){
        if(inScrollRange(e)) {
            e.type = "mousedownscroll";
            return o.apply(this, arguments);
        }
        return;
    };
    if ( arguments.length > 0 ) {
        return this.bind( "mousedown", data, fn );
    } 
    return this.trigger( "mousedown" );
};

$.fn.mouseupScroll = function(data, fn) {
    if ( fn == null ) {
        fn = data;
        data = null;
    }    
    var o = fn;
    fn = function(e){
        if(inScrollRange(e)) {
            e.type = "mouseupscroll";
            return o.apply(this, arguments);
        }
        return;
    };
    if ( arguments.length > 0 ) {
        return this.bind( "mouseup", data, fn );
    } 
    return this.trigger( "mouseup" );
};

var RECT = function(){
    this.top = 0;
    this.left = 0;
    this.bottom = 0;
    this.right = 0;
}

function inRect(rect, x, y){
    return (y >= rect.top && y <= rect.bottom) &&
           (x >= rect.left && x <= rect.right)
}


var scrollSize = measureScrollWidth();

function inScrollRange(event){
    var x = event.pageX,
        y = event.pageY,
        e = $(event.target),
        hasY = e.hasScroll(),
        hasX = e.hasScroll("x"),
        rX = null,
        rY = null,
        bInX = false,
        bInY = false

    if(hasY){ 
        rY = new RECT();
        rY.top = e.offset().top;
        rY.right = e.offset().left + e.width();
        rY.bottom = rY.top +e.height();
        rY.left = rY.right - scrollSize;

        //if(hasX) rY.bottom -= scrollSize;
        bInY = inRect(rY, x, y);
    }

    if(hasX){
        rX = new RECT();
        rX.bottom = e.offset().top + e.height();
        rX.left = e.offset().left;
        rX.top = rX.bottom - scrollSize;
        rX.right = rX.left + e.width();

        //if(hasY) rX.right -= scrollSize;
        bInX = inRect(rX, x, y);
    }

    return bInX || bInY;
}

$(document).on("mousedown", function(e){
    //Determine if has scrollbar(s)
    if(inScrollRange(e)){
        $(e.target).trigger("mousedownScroll");
    }
});

$(document).on("mouseup", function(e){
    if(inScrollRange(e)){
        $(e.target).trigger("mouseupScroll");
    }
});
});

function measureScrollWidth() {
    var scrollBarMeasure = $('<div />');
    $('body').append(scrollBarMeasure);
    scrollBarMeasure.width(50).height(50)
        .css({
            overflow: 'scroll',
            visibility: 'hidden',
            position: 'absolute'
        });

    var scrollBarMeasureContent = $('<div />').height(1);
    scrollBarMeasure.append(scrollBarMeasureContent);

    var insideWidth = scrollBarMeasureContent.width();
    var outsideWitdh = scrollBarMeasure.width();
    scrollBarMeasure.remove();

    return outsideWitdh - insideWidth;
};

Here's a JSFiddle of what I decided to do instead.

$.fn.hasScroll = function(axis){
    var overflow = this.css("overflow"),
        overflowAxis,
        bShouldScroll,
        bAllowedScroll,
        bOverrideScroll;

    if(typeof axis == "undefined" || axis == "y") overflowAxis = this.css("overflow-y");
    else overflowAxis = this.css("overflow-x");

    bShouldScroll = this.get(0).scrollHeight > this.innerHeight();

    bAllowedScroll = (overflow == "auto" || overflow == "visible") ||
        (overflowAxis == "auto" || overflowAxis == "visible");

    bOverrideScroll = overflow == "scroll" || overflowAxis == "scroll";

    return (bShouldScroll && bAllowedScroll) || bOverrideScroll;
};

$.fn.mousedownScroll = function(fn, data){
    var ev_mds = function(e){
        if(inScrollRange(e)) fn.call(data, e);
    }
    $(this).on("mousedown", ev_mds);
    return ev_mds;
};

$.fn.mouseupScroll = function(fn, data){
    var ev_mus = function(e){
        if(inScrollRange(e)) fn.call(data, e);
    }
    $(this).on("mouseup", ev_mus);
    return ev_mus;
};

$.fn.mousedownContent = function(fn, data){
    var ev_mdc = function(e){
        if(!inScrollRange(e)) fn.call(data, e);
    }

    $(this).on("mousedown", ev_mdc);

    return ev_mdc;
};

$.fn.mouseupContent = function(fn, data){
    var ev_muc = function(e){
        if(!inScrollRange(e)) fn.call(data, e);
    }
    $(this).on("mouseup", ev_muc);
    return ev_muc;
};

var RECT = function(){
    this.top = 0;
    this.left = 0;
    this.bottom = 0;
    this.right = 0;
}

function inRect(rect, x, y){
    return (y >= rect.top && y <= rect.bottom) &&
        (x >= rect.left && x <= rect.right)
}

var scrollSize = measureScrollWidth();

function inScrollRange(event){
    var x = event.pageX,
        y = event.pageY,
        e = $(event.target),
        hasY = e.hasScroll(),
        hasX = e.hasScroll("x"),
        rX = null,
        rY = null,
        bInX = false,
        bInY = false

    if(hasY){ 
        rY = new RECT();
        rY.top = e.offset().top;
        rY.right = e.offset().left + e.width();
        rY.bottom = rY.top +e.height();
        rY.left = rY.right - scrollSize;

        //if(hasX) rY.bottom -= scrollSize;
        bInY = inRect(rY, x, y);
    }

    if(hasX){
        rX = new RECT();
        rX.bottom = e.offset().top + e.height();
        rX.left = e.offset().left;
        rX.top = rX.bottom - scrollSize;
        rX.right = rX.left + e.width();

        //if(hasY) rX.right -= scrollSize;
        bInX = inRect(rX, x, y);
    }

    return bInX || bInY;
}

function measureScrollWidth() {
    var scrollBarMeasure = $('<div />');
    $('body').append(scrollBarMeasure);
    scrollBarMeasure.width(50).height(50)
        .css({
            overflow: 'scroll',
            visibility: 'hidden',
            position: 'absolute'
        });

    var scrollBarMeasureContent = $('<div />').height(1);
    scrollBarMeasure.append(scrollBarMeasureContent);

    var insideWidth = scrollBarMeasureContent.width();
    var outsideWitdh = scrollBarMeasure.width();
    scrollBarMeasure.remove();

    return outsideWitdh - insideWidth;
};
@ r3wt Нанимаете меня? Нет необходимости в этом друге-о. Единственный способ научиться - это изучать код и писать его самостоятельно! Наконец-то у меня появилось свободное время иmade this, Вы должны найти основные концепции, которые вам нужны, в примере. ShadowScripter
Спасибо за слова, приятель. Я проверю это завтра. Я ценю головы и добрые слова.
Можно ли нанять вас, чтобы построить мне что-то, что использует это? Мне нужна такая функция, чтобы отменить действие ajax и функцию scrolltop, пока нажата полоса прокрутки, а затем возобновить обе функции, когда щелчок отпущен. это немного выше моего уровня квалификации, хотя.
1

которое работает для меня (проверено только на IE11):

$(document).mousedown(function(e){
    bScrollbarClicked = e.clientX > document.documentElement.clientWidth || e.clientY > document.documentElement.clientHeight;

});

23
Solved:

которое я смог придумать, протестировано на IE, Firefox, Chrome.

var clickedOnScrollbar = function(mouseX){
  if( $(window).outerWidth() <= mouseX ){
    return true;
  }
}

$(document).mousedown(function(e){
  if( clickedOnScrollbar(e.clientX) ){
    alert("clicked on scrollbar");
  }
});

Рабочий пример: https://jsfiddle.net/s6mho19z/

Идеальное решение, только работает. :-) СПАСИБО
хорошее решение: использованиеclientWidth а такжеoffsetWidth вместе это хороший способ узнать, где произошел щелчок.
Не работает на MacOS, когда полоса прокрутки появляется только при прокрутке
@ www139 Спасибо ;-)
Удивительный код! Хорошая работа.
1

но не на окне, а на div, и у меня есть элемент, который заполняет содержимое, которое я использовал для определения размера без полосы прокрутки:

.element {
    position: relative;
}
.element .fill-node {
    position: absolute;
    left: 0;
    top: -100%;
    width: 100%;
    height: 100%;
    margin: 1px 0 0;
    border: none;
    opacity: 0;
    pointer-events: none;
    box-sizing: border-box;
}

код для обнаружения был похож на@DariuszSikorski ответ но включая смещение и использование узла, который был внутри прокручиваемого:

function scrollbar_event(e, node) {
    var left = node.offset().left;
    return node.outerWidth() <= e.clientX - left;
}

var node = self.find('.fill-node');
self.on('mousedown', function(e) {
   if (!scrollbar_event(e, node)) {
      // click on content
   }
});
0

element.offsetLeft + element.scrollWidth < event.clientX подelement.onmousedown, ЭлементscrollWidth не включает ширину полосы прокрутки. Это включает только внутреннее содержание. Если вы также хотите обнаруживать с помощью горизонтальной полосы прокрутки, то же самое можно применить к этому масштабу, используя высоту и позицию Y вместо ширины и позиции X.

9

и я рекомендую это решение. Он не очень чистый, но он работает, и я сомневаюсь, что мы сможем добиться большего с HTML. Вот два шага моего решения:

1. Measure the width of the scrollbar on your Desktop environment.

Для этого при запуске приложения выполняются следующие действия:

Добавьте следующий элемент в тело:

<div style='width: 50px; height: 50px; overflow: scroll'><div style='height: 1px;'/></div>

С помощью jQUery's .width () измерьте внутреннее значение div для ранее добавленного элемента и сохраните где-нибудь ширину полосы прокрутки (ширина полосы прокрутки равна 50 - с внутренним делителем)

Удалите дополнительный элемент, используемый для измерения полосы прокрутки (теперь, когда у вас есть результат, удалите элемент, который вы добавили в тело).

Все эти шаги не должны быть видны пользователю, и у вас есть ширина полосы прокрутки в вашей ОС

Например, вы можете использовать этот фрагмент:

var measureScrollBarWidth = function() {
    var scrollBarMeasure = $('<div />');
    $('body').append(scrollBarMeasure);
    scrollBarMeasure.width(50).height(50)
        .css({
            overflow: 'scroll',
            visibility: 'hidden',
            position: 'absolute'
        });

    var scrollBarMeasureContent = $('<div />').height(1);
    scrollBarMeasure.append(scrollBarMeasureContent);

    var insideWidth = scrollBarMeasureContent.width();
    var outsideWitdh = scrollBarMeasure.width();
    scrollBarMeasure.remove();

    return outsideWitdh - insideWidth;
};

2. Check if a click is on the scrollbar.

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

Если вы хотите отфильтровать щелчки, вы можете вернуть false в обработчике, чтобы предотвратить их распространение.

Выполнение измерений для определения ширины / высоты прокрутки подвержено ошибкам - при добавлении и удалении мыши с ноутбука с сенсорной панелью на Mac происходит переполнение: полосы прокрутки динамически появляются и исчезают.
15

Вы можете попробовать угнатьmousedown а такжеmouseup события и избегая их, когда вы нажимаете на полосу прокрутки с вашей собственной активной функцией.

$.fn.mousedown = function(data, fn) {
    if ( fn == null ) {
        fn = data;
        data = null;
    }    
    var o = fn;
    fn = function(e){
        if(!inScrollRange(e)) {
            return o.apply(this, arguments);
        }
        return;
    };
    if ( arguments.length > 0 ) {
        return this.bind( "mousedown", data, fn );
    } 
    return this.trigger( "mousedown" );
};

И обратное дляmousedownScroll а такжеmouseupScroll События.

$.fn.mousedownScroll = function(data, fn) {
    if ( fn == null ) {
        fn = data;
        data = null;
    }    
    var o = fn;
    fn = function(e){
        if(inScrollRange(e)) {
            e.type = "mousedownscroll";
            return o.apply(this, arguments);
        }
        return;
    };
    if ( arguments.length > 0 ) {
        return this.bind( "mousedown", data, fn );
    } 
    return this.trigger( "mousedown" );
};

Кстати, я думаю, что ширина полосы прокрутки является настройкой ОС.

Решение с помощью jedierikb (прокрутка вниз) намного лучше, потому что оно не полагается на вычисления или хаки, а на то, какой элемент вызвал событие. И JQuery на самом деле не требуется.
@ ShadowScripter, я рад, что это было полезно;)
Спасибо Александр, этоworked perfectly, Извините за поздний прием, вы действительно должны получить больше голосов за это, это замечательно! 25 очков на вашем пути;) ShadowScripter
7

что содержимое вашей scollarea полностью [пере] заполняет родительский div.

Затем вы можете различать клики по вашему контенту и клики по вашему контейнеру.

html:

<div class='test container'><div class='test content'></div></div>
<div id="results">please click</div>

css:

#results {
  position: absolute;
  top: 110px;
  left: 10px;
}

.test {
  position: absolute;
  left: 0;
  top: 0;
  width: 100px;
  height: 100px;
  background-color: green;
}

.container {
  overflow: scroll;
}

.content {
  background-color: red;
}

js:

function log( _l ) {
  $("#results").html( _l );
}

$('.content').on( 'mousedown', function( e ) {
  log( "content-click" );
  e.stopPropagation();
});

$('.container').on( 'mousedown', function( e ) {
  var pageX = e.pageX;
  var pageY = e.pageY;
  log( "scrollbar-click" );
});

http://codepen.io/jedierikb/pen/JqaCb

Простое и очевидное решение, спасибо
Хорошее решение Вы также можете сделать это с одним событием на.container через условие как:if($(e.target).is($(this))) //scrollbar-click. jsfiddle.net/3tzj0bps
2

чтобы определить, щелкнул ли пользователь мышью по полосе прокрутки элемента. Не проверял, как это работает с полосой прокрутки окна. Полагаю, решение Пита лучше работает с прокручиванием окон.

window.addEventListener("mousedown", onMouseDown);

function onMouseDown(e) {
  if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) 
  {
      // mouse down over scroll element
   }
}
0

что в Mac OSX 10.7+ нет постоянных полос прокрутки. Полосы прокрутки появляются при прокрутке и исчезают, когда вы закончите. Они также намного меньше 18px (они 7px).

Скриншот: http://i.stack.imgur.com/zdrUS.png

Похожие вопросы