ShadowScripter ShadowScripter - 3 months ago 18
jQuery Question

Determine whether user clicking scrollbar or content (onclick for native scroll bar)

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 example you can work on straight away.

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,

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

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?

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.

I can't seem to stop the other events from triggering while simultaneously triggering, what I call, the mousedownScroll/mouseupScroll events.

$.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 but maybe 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.




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

Answer

You may probably use this hack.

You could try hijacking the mousedown and mouseup events and avoiding them when click on a scrollbar with your custom powered function.

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

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