Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery: Any chance to detect from which side the mouse entered a div without the "Offset" Method?

Is there a way to detect from which side of a div the mouse cursor came from?

Currently i'm using this method:

jQuery(this).bind('mousemove',function(e){
    offset_pos_x = parseInt(e.offsetX);
    offset_pos_y = parseInt(e.offsetY);
    ...

Then i look for the distances the mouse went inside the div, in which direction.

The Problem is, this method is a bit buggy because i need all 4 sides, not just two, so i have to check offsetX AND offsetY.

If i move the mouse inside the div for example X:+15,Y:-3 (in Pixels) i know that the mouse came from left, because the mouse moved 15px on x-axis, but only -3px on y-axis. The buggy thing about this is, when X and Y are nearly the same and i don't know if the mouse came from left or top (for example).

Also, according to my other Question (jQuery: mouseenter/mousemove/mouseover not recognized with small div and fast mouse movement) the Event isn't fired on the first Pixel of the div's side, because of browser/os limitations. Because of that, my "entered_at" coordinate isn't that accurate - example:

If i move my mouse cursor very fast inside the div (from left), the "entered_at" coord is at x:17,y:76 for example. Now if i move my mouse to the left after stoping the mouse, for example to x:6,y:76 the difference between the starting point and offsetX is negative, so the "cursor came from right" function is triggered...

Is there another way to detect the side from which the mouse cursor came from?

Greetings, Patrick

like image 473
Patrick Avatar asked Feb 22 '13 09:02

Patrick


4 Answers

I wouldn't use the offset, but rather pageX/pageY (jQuery normalizes these). If the cursor's first mousemove event was closer to the left edge than any other edge, it came from the left. You may also consider using the hover event for this, rather than mousemove.

JSFiddle, courtesy of the Jamaican flag. http://jsfiddle.net/MJTkk/1/

function closestEdge(x,y,w,h) {
        var topEdgeDist = distMetric(x,y,w/2,0);
        var bottomEdgeDist = distMetric(x,y,w/2,h);
        var leftEdgeDist = distMetric(x,y,0,h/2);
        var rightEdgeDist = distMetric(x,y,w,h/2);
        var min = Math.min(topEdgeDist,bottomEdgeDist,leftEdgeDist,rightEdgeDist);
        switch (min) {
            case leftEdgeDist:
                return "left";
            case rightEdgeDist:
                return "right";
            case topEdgeDist:
                return "top";
            case bottomEdgeDist:
                return "bottom";
        }
}

function distMetric(x,y,x2,y2) {
    var xDiff = x - x2;
    var yDiff = y - y2;
    return (xDiff * xDiff) + (yDiff * yDiff);
}
like image 69
turiyag Avatar answered Nov 01 '22 20:11

turiyag


here's the correct/working example of the Script including the fix for absolute positioned divs. Thanks again for your help turiyag!

jsfiddle: http://jsfiddle.net/MJTkk/2/

Script:

$(function() {
    $("img").hover(function(e) {
        var el_pos = $(this).offset();
        var edge = closestEdge(e.pageX - el_pos.left, e.pageY - el_pos.top, $(this).width(), $(this).height());
        log('entered at: '+edge);
    }, function(e) {
        var el_pos = $(this).offset();
        var edge = closestEdge(e.pageX - el_pos.left, e.pageY - el_pos.top, $(this).width(), $(this).height());
        log('left at: '+edge+'<br><br>');
    });
});

function closestEdge(x,y,w,h) {
        var topEdgeDist = distMetric(x,y,w/2,0);
        var bottomEdgeDist = distMetric(x,y,w/2,h);
        var leftEdgeDist = distMetric(x,y,0,h/2);
        var rightEdgeDist = distMetric(x,y,w,h/2);

        var min = Math.min(topEdgeDist,bottomEdgeDist,leftEdgeDist,rightEdgeDist);
        switch (min) {
            case leftEdgeDist:
                return "left";
            case rightEdgeDist:
                return "right";
            case topEdgeDist:
                return "top";
            case bottomEdgeDist:
                return "bottom";
        }
}

function log(msg) {
    $("#console").append("<pre>" + msg + "</pre>");
}

function distMetric(x,y,x2,y2) {
    var xDiff = x - x2;
    var yDiff = y - y2;
    return (xDiff * xDiff) + (yDiff * yDiff);
}
like image 27
Patrick Avatar answered Nov 01 '22 20:11

Patrick


The closest edge function in the Jamaican flag fiddle isn't as good as it could be and was inaccurate for certain elements. We can use offsetX and offsetY to simiplify the function and eliminate the distMetric function:

// Pass object offsetX,offsetY,width,height
function closestEdge(distLeft,distTop,w,h){
    var distBottom = (h - distTop);
    var distRight = (w - distLeft);
    var min = Math.min(distTop, distBottom, distLeft, distRight);
    switch (min) {
        case distLeft:
            return "left";
        case distRight:
            return "right";
        case distTop:
            return "top";
        case distBottom:
            return "bottom";
    }
}

E.g.:

$('.btn').on('mouseenter',function(e){
    var edge = closestEdge(e.offsetX, e.offsetY, $(this).width(), $(this).height());
});
like image 5
chrisboustead Avatar answered Nov 01 '22 20:11

chrisboustead


I had some issues with this code, when operating on a rectangle I found that it would incorrectly identify an edge as right when it should be top. I spent some time coming up with an answer and felt I'd share:

var getDir = function( elem, e ) {       

            /** the width and height of the current div **/
            var w = elem.width();
            var h = elem.height();
            var offset = elem.offset();
            /** calculate the x and y to get an angle to the center of the div from that x and y. **/
            /** gets the x value relative to the center of the DIV and "normalize" it **/
            var x = (e.pageX - offset.left - (w/2)) * ( w > h ? (h/w) : 1 );
            var y = (e.pageY - offset.top  - (h/2)) * ( h > w ? (w/h) : 1 );

            /** the angle and the direction from where the mouse came in/went out clockwise (TRBL=0123);**/
            /** first calculate the angle of the point, 
             add 180 deg to get rid of the negative values
             divide by 90 to get the quadrant
             add 3 and do a modulo by 4  to shift the quadrants to a proper clockwise TRBL (top/right/bottom/left) **/
            var direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 ) / 90 ) + 3 )  % 4;


            /** do your animations here **/ 
            switch(direction) {
             case 0:
                return 'top';
             break;
             case 1:
                return 'right';
             break;
             case 2:
                   return 'bottom';
             break;
             case 3:
                   return 'left';
             break;
            }

}


$(this).bind('mouseenter',function(e){
    //outputs the direction to the log
    console.log(getDir($(this), e));
});

credit: jQuery animation for a hover with 'mouse direction'

like image 1
Andrew Ward Avatar answered Nov 01 '22 20:11

Andrew Ward