Определите, будет ли пользователь щелкать полосу прокрутки или содержимое (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, потому что клик не запускается при нажатии на полосу прокрутки. Если это неверно, укажите рабочий пример/код
Ответы
Ответ 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