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

Определите, будет ли пользователь щелкать полосу прокрутки или содержимое (onclick для собственной полосы прокрутки)

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

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

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

и inScrollRange, проверяя, был ли нажатый клик в диапазоне прокрутки.

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

Являются ли все полосы прокрутки одинаковыми? Например, в Firefox и IE это 18px.
Предполагая, что нет настроенных полос прокрутки, есть ли дополнительные дополнения или размеры в некоторых браузерах?

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

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

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

$.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.");
});

Как остановить другие события "click" от запуска?

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

Здесь JSFiddle, чтобы возиться с.

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

Я просто вслушиваюсь вслух здесь, но может быть, есть способ получить все функции, привязанные к элементу, а затем переключить порядок, в котором они были добавлены? Я знаю, что функции выполняются в том порядке, в котором они были добавлены (1-й добавлен 1-й вызов), поэтому, если бы я мог использовать этот процесс, возможно, вся "регистрация" события в JQuery могла бы быть вставлена ​​перед событиями щелчка.


1 может использовать только mousedown/mouseup, потому что клик не запускается при нажатии на полосу прокрутки. Если это неверно, укажите рабочий пример/код

4b9b3361

Ответ 1

Возможно, вы можете использовать этот хак.

Вы можете попытаться захватить события 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" );
};

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

Ответ 2

Решено:

Самое короткое обнаружение щелчка полосы прокрутки, которое я мог придумать, протестировал на 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/

Ответ 3

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

1. Измерьте ширину полосы прокрутки в рабочей среде рабочего стола.

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

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

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

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

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

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

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

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. Проверьте, находится ли клик на полосе прокрутки.

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

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

Ответ 4

Убедитесь, что содержимое вашей 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

Ответ 5

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

window.addEventListener("mousedown", onMouseDown);

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

Ответ 6

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


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

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

  • mousedownScroll
  • mousedownContent

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


  • Здесь JSFiddle, который реализует ответ Александра + Самуил.

    $.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;
    };
    
  • Здесь JSFiddle того, что я решил сделать вместо этого.

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

Ответ 7

Единственное решение, которое работает для меня (тестируется только против IE11):

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

});

Ответ 8

Мне нужно было обнаружить полосу прокрутки на mousedown, но не на окне, а на 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 ответ, но включая смещение и использование node, которое было внутри прокручиваемого:

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

Ответ 9

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

Снимок экрана: http://i.stack.imgur.com/zdrUS.png

Ответ 10

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

Ответ 11

clickOnScrollbar = event.clientX > event.target.clientWidth || event.clientY > event.target.clientHeight;

протестировано на Chrome/Mac OS

Ответ 12

Я не пробовал, и я знаю, что это будет долгий путь, но вы можете создать свою собственную полосу прокрутки с помощью javascript, html и css и сделать это, когда узнаете, когда кто-то щелкнет в вашем div (или полосе прокрутки). здесь вы можете увидеть, как создать полосу прокрутки с помощью CSS: http://www.apptools.com/examples/scroller.php