59

我正在尝试在 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.");
});

如何阻止其他“点击”事件触发?

当我问的时候,请尽可能优化代码。

这是一个可以解决的 JSFiddle。

我做这个的原因是因为我正在开发一个更大的插件。当我右键单击其中一个滚动条时,它会显示一个自定义上下文菜单。我不想要那个。所以我想我应该做一个检查滚动点击(鼠标上/下)的事件,然后阻止显示上下文菜单。不过,为了做到这一点,我需要滚动点击在正常点击之前出现,并且如果可能的话,停止正常点击。

我只是在这里大声思考,但也许有一种方法可以获取绑定到元素的所有功能,然后切换添加它们的顺序?我知道函数是按照它们添加的顺序执行的(第一次添加第一次调用),所以,如果我可以利用该过程,也许可以在点击事件之前插入事件到 JQuery 的整个“注册”。


1只能使用 mousedown/mouseup 因为单击滚动条时不会触发单击。如果这是错误的,请提供一个工作示例/代码

4

12 回答 12

28

解决了:

我能想到的最短的滚动条点击检测,在 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/

于 2015-11-19T09:00:05.423 回答
18

你可能会使用这个 hack。

您可以尝试劫持mousedownmouseup事件并在使用自定义驱动功能单击滚动条时避免它们。

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

mousedownScrollmouseupScroll事件相反。

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

顺便说一句,我认为滚动条宽度是操作系统设置。

于 2012-04-07T12:14:24.160 回答
17

使用以下解决方案来检测用户是否在元素的滚动条上单击鼠标。没有测试它如何与窗口的滚动条一起工作。我猜 Pete 的解决方案更适用于窗口滚动。

window.addEventListener("mousedown", onMouseDown);

function onMouseDown(e) {
  if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) 
  {
      // mouse down over scroll element
   }
}
于 2018-01-03T11:37:33.283 回答
8

我在之前的项目中遇到了同样的问题,我推荐这个解决方案。它不是很干净,但它可以工作,我怀疑我们可以用 html 做得更好。这是我的解决方案的两个步骤:

1. 测量桌面环境中滚动条的宽度。

为了实现这一点,在应用程序启动时,您执行以下操作:

将以下元素添加到正文:

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

用 jQUery 的 .width() 测量先前添加元素的内部 div 的宽度,并将滚动条的宽度存储在某处(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 以防止其传播。

于 2012-05-23T16:13:15.730 回答
8

确保 scollarea 的内容完全 [over] 填充父 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

于 2014-06-23T15:01:50.017 回答
3

这里有很多答案涉及event.clientX,element.clientHeight等。它们都是错误的。不要使用它们。

  • 正如上面所讨论的,在某些平台上overflow: scroll滚动条显示为覆盖,或者您可能已经使用overflow: overlay.
  • Mac 可以通过插入或拔出鼠标在持久和覆盖之间切换滚动条。这击倒了“启动时测量”技术。
  • 垂直滚动条出现在左侧,阅读顺序从右到左。这会破坏比较客户端宽度,除非您有一堆从右到左阅读顺序的特殊逻辑,我打赌这会破坏,因为您可能没有始终如一地测试 RTL。

你需要看看event.target。如有必要,使用一个占据滚动元素所有客户区域的内部元素,并查看 event.target该元素是该元素还是它的后代。

于 2020-04-26T15:44:29.337 回答
0

我将提交我自己的答案并接受Alexander 的答案,因为它使它完美运行,并支持Samuel 的答案,因为它正确计算了滚动条宽度,这也是我所需要的。


话虽如此,我决定制作两个独立的事件,而不是尝试覆盖/覆盖 JQuery 的mousedown事件。

这给了我所需的灵活性,而不会弄乱 JQuery 自己的事件,而且很容易做到。

  • mousedownScroll
  • mousedownContent

下面是使用 Alexanders 和我自己的两个实现。两者都按照我最初的意图工作,但前者可能是最好的。


  • 这是一个实现 Alexander 的答案 + Samuel 的答案的 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;
    };
    
于 2013-03-15T12:18:17.807 回答
0

需要指出的是,在 Mac OSX 10.7+ 上,没有持久滚动条。滚动条会在您滚动时出现,并在您完成后消失。它们也比 18px 小得多(它们是 7px)。

截图:http: //i.stack.imgur.com/zdrUS.png

于 2012-05-08T17:15:02.853 回答
0

唯一适合我的解决方案(仅针对 IE11 进行了测试):

$(document).mousedown(function(e){
    bScrollbarClicked = e.clientX > document.documentElement.clientWidth || e.clientY > document.documentElement.clientHeight;
});
于 2016-09-08T21:00:59.820 回答
0

在 ubuntu 21.10 中测试并在 chrome 和 firefox 中工作。

const isScrollClick =
  e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight;
于 2022-02-24T13:04:25.263 回答
0

我需要在 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 答案,但包括偏移量并使用可滚动内部的节点:

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
   }
});
于 2017-11-08T17:06:29.103 回答
-1
clickOnScrollbar = event.clientX > event.target.clientWidth || event.clientY > event.target.clientHeight;

在 Chrome / Mac OS 上测试

于 2019-10-15T13:46:40.997 回答