7

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

Community
  • 1
  • 1
Patrick
  • 413
  • 6
  • 12

6 Answers6

11

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);
}
turiyag
  • 2,757
  • 2
  • 22
  • 21
  • I've tried the pageX/pageY method before using the offset, but (correct me if i'm wrong), there is no difference in using pageX or offsetX (my calculations are different but i don't think the result is) – Patrick Feb 22 '13 at 09:17
  • Just to explain what i mean: i need a starting point, end an ending point to calculate the way in which the mouse moved. But if the starting point is recognized on +15px (x-axis) in a div because i moved my mouse too fast, the ending point CAN be negativ if i move the mouse cursor after catching the starting point to the left... – Patrick Feb 22 '13 at 09:21
  • 1
    jQuery's docs warn of using offsetX and clientX: "Properties such as .clientX, .offsetX, and .pageX are available, but support for them differs between browsers. Fortunately, jQuery normalizes the .pageX and .pageY properties so that they can be used in all browsers. " http://api.jquery.com/mousemove/ – turiyag Feb 22 '13 at 09:31
  • Well, that's working MUCH better then my version, then i was wrong and pageX/pageY is a better way to detect the mouse movement, thanks! – Patrick Feb 22 '13 at 09:48
  • I've noticed that you if you change the "out"-Function to the same for the "in"-Function its also possible to get the Side in which the mouse cursor left the div! :-) – Patrick Feb 22 '13 at 09:52
  • Ah, one more Problem: I re-position that div after page load (it's positioned absolutely), because of that it's not possible to detect the direction anymore... any advise? Here's a modified Version of your jsfiddle: http://jsfiddle.net/LfDeQ/ – Patrick Feb 22 '13 at 10:15
  • 1
    pageX and pageY give you the coordinates relative to something other than the corner of the image. jQuery's documentation on mousemove (link above) explains the difference. – turiyag Feb 24 '13 at 12:28
  • I've read the article, but i dont get it at all.. i've looked for the "EdgeDist" Vars but what do they represent? They're all over tousand pixels... I dont know what to do to calculate the side of the mouse-enter when the div is not top left positioned... please help :-) – Patrick Feb 24 '13 at 13:05
  • for the event **mouseout** or **mouseenter** on the **td** of html table it's not working... actually i want to track from which side cursor entered in a **td** – Dev Matee Sep 27 '18 at 12:06
9

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);
}
Patrick
  • 413
  • 6
  • 12
5

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());
});
chrisboustead
  • 1,573
  • 12
  • 17
1

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'

Community
  • 1
  • 1
0

Here a plugin

: https://github.com/JohnnyBeGood34/JQuery-MouseEntrance

hope help you.

JohnnyBeGoody
  • 253
  • 1
  • 3
  • 15
0

I have improved @Patrick's solution using jQuery functions to have an even more elegant and robust custom jQuery function.

Here is the jQuery getMouseSide jsFiddle:

$(function () {
    $('img').hover(function (event) {
        log('entered at: ' + $(this).getMouseSide(event))
    }, function (event) {
        log('left at: ' + $(this).getMouseSide(event) + '<br><br>')
    })

    $.fn.getMouseSide = function (event) {
        function distanceMetric(x, y, x2, y2) {
            return Math.pow(x - x2, 2) + Math.pow(y - y2, 2)
        }

        function closestEdge(x, y, w, h) {
            var edgeDistance = {
                top:        distanceMetric(x, y, w / 2  , 0)
                , bottom:   distanceMetric(x, y, w / 2  , h)
                , left:     distanceMetric(x, y, 0      , h / 2)
                , right:    distanceMetric(x, y, w      , h / 2)
            }
            , edgeDistances = $.map(edgeDistance, function (value) {
                return [value]
            })

            return Object.keys(edgeDistance)[
                $.inArray(
                    Math.min.apply(null, edgeDistances)
                    , edgeDistances
                )
            ]
        }

        var current = $(this)
        , elementOffset = current.offset()

        return closestEdge(
            event.pageX - elementOffset.left
            , event.pageY - elementOffset.top
            , current.width()
            , current.height()
        )
    }
})

function log(message) {
    $('#console').append('<pre>' + message + '</pre>')
}
Community
  • 1
  • 1
Erik Engi
  • 402
  • 3
  • 10