jQuery 确定用户是点击滚动条还是内容(原生滚动条的onclick)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10045423/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Determine whether user clicking scrollbar or content (onclick for native scroll bar)
提问by ShadowScripter
I'm trying to create custom events in JQuery that are supposed to detect when a scrollbar is clicked1.
I know there's lots of text, but all my questions are boldfaced and there's a JSFiddle exampleyou can work on straight away.
我正在尝试在 JQuery 中创建自定义事件,这些事件应该检测何时单击滚动条1。
我知道有很多文本,但我所有的问题都是粗体的,并且有一个JSFiddle 示例,您可以立即进行处理。
Because I haven't found any built in functionality for this,
I had to create a hasScroll
function, checking if the element has a scrollbar,
因为我还没有找到任何内置的功能,所以
我必须创建一个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;
};
and an inScrollRange
function, checking if the click performed was within the scroll range.
和一个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;
}
Are all scrollbar sizes uniform? E.g in Firefox and IE it's 18px.
Assuming there are no customized scrollbars, is there any extra padding or sizes in some browsers?
所有滚动条大小都统一吗?例如在 Firefox 和 IE 中它是 18px。
假设没有自定义滚动条,在某些浏览器中是否有任何额外的填充或大小?
These functions all perform as intended (from what I can discern).
这些功能都按预期执行(据我所知)。
Making custom events was a bit trickier, but I got it to work somewhat. The only problem is that if the element clicked has a mousedown/up event attached to it, that will be triggered as well.
制作自定义事件有点棘手,但我让它有点工作。唯一的问题是,如果被点击的元素附加了一个 mousedown/up 事件,它也会被触发。
I can't seem to stop the other events from triggering while simultaneously triggering, what I call, the mousedownScroll/mouseupScroll events.
我似乎无法阻止其他事件同时触发,我称之为 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.");
});
How do I stop the other "click" events from triggering?
如何阻止其他“点击”事件触发?
While I'm asking, please feel free to optimize the code as much as possible.
当我问的时候,请尽可能地优化代码。
Here's a JSFiddle to mess around with.
The reason I'm making this is because of a bigger plugin I'm developing. It's got a custom context menu that is showing up when I right click one of the scrollers. I don't want that. So I thought I should make an event that checks for scroll clicks (mouseup/downs) and then prevent the context menu from being displayed. In order to do that though, I need the scroll click to come before the normal click, and also, if possible, stop the normal clicks from firing.
我这样做的原因是因为我正在开发一个更大的插件。它有一个自定义上下文菜单,当我右键单击其中一个滚动条时会显示该菜单。我不想要那个。所以我想我应该创建一个事件来检查滚动点击(鼠标向上/向下),然后阻止显示上下文菜单。但是,为了做到这一点,我需要在正常点击之前进行滚动点击,并且如果可能的话,停止正常点击触发。
I'm just thinking out loud here butmaybe there's a way to get all the functions that are bound to the element and then switch the order in which they were added? I know that functions are executed in the order they were added (1st added 1st called), so, if I could tap into that process, perhaps the whole "registering" of the event to JQuery could just be inserted before the click events.
我只是在这里大声思考,但也许有一种方法可以获取绑定到元素的所有函数,然后切换它们添加的顺序?我知道函数是按照添加的顺序执行的(第一个添加的第一个调用),因此,如果我可以利用该过程,也许可以在单击事件之前插入整个事件到 JQuery 的“注册”。
1can 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只能使用 mousedown/mouseup,因为单击滚动条时不会触发单击。如果这是错误的,请提供一个工作示例/代码
采纳答案by Alexander
You may probably use this hack.
你可能会使用这个 hack。
You could try hiHymaning the mousedown
and mouseup
events and avoiding them when click on a scrollbar with your custom powered function.
您可以尝试劫持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" );
};
And the inverse for mousedownScroll
and mouseupScroll
events.
和逆对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" );
};
By the way, I think the scrollbar width is an OS setting.
顺便说一句,我认为滚动条宽度是操作系统设置。
回答by Dariusz Sikorski
Solved:
解决了:
A shortest scrollbar click detection I could come up with, tested on IE, Firefox, Chrome.
我能想到的最短滚动条点击检测,在 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");
}
});
Working example: https://jsfiddle.net/s6mho19z/
回答by Samuel Rossille
I had the same problem in a previous project, and i recommend this solution. It's not very clean but it works and i doubt we can do much better with html. Here are the two steps of my solution:
我在以前的项目中遇到了同样的问题,我推荐这个解决方案。它不是很干净,但它有效,我怀疑我们可以用 html 做得更好。这是我的解决方案的两个步骤:
1. Measure the width of the scrollbar on your Desktop environment.
1. 测量桌面环境中滚动条的宽度。
In order to achieve this, at application startup, you perform the following things:
为了实现这一点,在应用程序启动时,您需要执行以下操作:
Add the following element to the body:
将以下元素添加到正文:
<div style='width: 50px; height: 50px; overflow: scroll'><div style='height: 1px;'/></div>
<div style='width: 50px; height: 50px; overflow: scroll'><div style='height: 1px;'/></div>
Measure the with of the inner div of the previously added element with jQUery's .width(), and store the width of the scrollbar somewhere (the width of the scollbar is 50 - inner div's with)
用jQUEry的.width()测量之前添加的元素的内部div的with,并将滚动条的宽度存储在某处(scollbar的宽度为50 - 内部div的with)
Remove the extra element used to measure scrollbar (now that you have the result, remove the element that you added to the body).
删除用于测量滚动条的额外元素(现在您有了结果,删除您添加到正文的元素)。
All these steps should not be visible by the user and you have the width of the scrollbar on your OS
用户不应看到所有这些步骤,并且您的操作系统上具有滚动条的宽度
For example, you can use this snippet:
例如,您可以使用以下代码段:
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.
2. 检查滚动条上是否有点击。
Now that you have the width of the scrollbar, you can with the coordinates of the event compute the coordinates of the event relative to the scrollbar's location rectangle and perfom awesome things...
现在你有了滚动条的宽度,你可以用事件的坐标计算事件相对于滚动条的位置矩形的坐标并执行很棒的事情......
If you want to filter the clicks, you can return false in the handler to prevent their propagation.
如果您想过滤点击,您可以在处理程序中返回 false 以防止其传播。
回答by jedierikb
Ensure that the content of your scollarea completely [over]fills the parent div.
确保您的 scollarea 的内容完全 [over] 填充父 div。
Then, you can differentiate between clicks on your content and clicks on your container.
然后,您可以区分对内容的点击和对容器的点击。
html:
html:
<div class='test container'><div class='test content'></div></div>
<div id="results">please click</div>
css:
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:
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" );
});
回答by Antti
Use following solution to detect if user clicked mouse over element's scrollbar. Didn't test how it works with window's scrollbar. I guess Pete's solution works better with window scrolls.
使用以下解决方案来检测用户是否在元素的滚动条上单击鼠标。没有测试它如何与窗口的滚动条一起工作。我猜皮特的解决方案更适合窗口滚动。
window.addEventListener("mousedown", onMouseDown);
function onMouseDown(e) {
if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight)
{
// mouse down over scroll element
}
}
回答by Adam Leggett
There are many answers here that involve event.clientX
, element.clientHeight
, etc. They are all wrong. Do not use them.
这里涉及有很多的答案event.clientX
,element.clientHeight
等他们都错了。不要使用它们。
- As has been discussed above, there are platforms where the
overflow: scroll
scrollbars appear as overlays, or you may have forced it withoverflow: overlay
. - Macs may switch scrollbars between persistent and overlay by plugging or unplugging a mouse. This shoots down the "measure on startup" technique.
- Vertical scrollbars appear on the left side with right to left reading order. This breaks comparing client width unless you have a bunch of special logic for right to left reading order that I bet will break.
- 如上所述,在某些平台上
overflow: scroll
滚动条显示为叠加层,或者您可能使用overflow: overlay
. - Mac 可以通过插入或拔出鼠标在持久和覆盖之间切换滚动条。这否定了“启动时的测量”技术。
- 垂直滚动条以从右到左的阅读顺序出现在左侧。除非你有一堆从右到左阅读顺序的特殊逻辑,我敢打赌,这会破坏比较客户端宽度。
You need to look at event.target
. If necessary, use an inner element that occupies all of the client area of the scroll element, and see if event.target
is that element or a descendant of it.
你需要看看event.target
。如有必要,使用一个占据滚动元素所有客户区的内部元素,看看event.target
是该元素还是它的后代。
回答by Pete Alvin
The only solution that works for me (only tested against IE11):
唯一适合我的解决方案(仅针对 IE11 进行了测试):
$(document).mousedown(function(e){
bScrollbarClicked = e.clientX > document.documentElement.clientWidth || e.clientY > document.documentElement.clientHeight;
});
});
回答by jcubic
I needed to detect scrollbar on mousedown but not on window but on div, and I've had element that fill the content, that I was using to detect size without scrollbar:
我需要在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;
}
the code for detect was similar to @DariuszSikorski answerbut including offset and using the node that was inside scrollable:
检测代码类似于@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
}
});
回答by ShadowScripter
I'll submit my own answer and accept Alexander's answer, because it made it work perfectly, and upvote Samuel's answer, because it correctly calculates the scrollbar width, which is what I needed as well.
我将提交我自己的答案并接受Alexander 的答案,因为它使它完美运行,并且支持Samuel 的答案,因为它正确计算了滚动条宽度,这也是我所需要的。
That being said, I decided to make two independent events instead of trying to overwrite/override JQuery's mousedown
event.
话虽如此,我决定制作两个独立的事件,而不是尝试覆盖/覆盖 JQuery 的mousedown
事件。
This gave me the flexibility I needed without messing with JQuery's own events, and was quite easy to do.
这给了我所需的灵活性,而不会弄乱 JQuery 自己的事件,而且很容易做到。
mousedownScroll
mousedownContent
mousedownScroll
mousedownContent
Below are the two implementations using Alexanders, and my own. Both work as I originally intended them to, but the former is probably the best.
下面是使用 Alexanders 和我自己的两个实现。两者都按照我最初的预期工作,但前者可能是最好的。
Here's a JSFiddlethat 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 JSFiddleof 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; };
这是一个实现亚历山大的答案+塞缪尔的答案的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; };
回答by Applehat
It should be pointed out that on Mac OSX 10.7+, there are not persistant scroll bars. Scroll bars appear when you scroll, and disappear when your done. They are also much smaller then 18px (they are 7px).
需要指出的是,在 Mac OSX 10.7+ 上,没有持久滚动条。滚动时滚动条出现,完成后消失。它们也比 18px 小得多(它们是 7px)。
Screenshot: http://i.stack.imgur.com/zdrUS.png