mouse cursor position

Discussion in 'Javascript' started by Danny@Kendal, Nov 12, 2004.

  1. Danny@Kendal

    Danny@Kendal Guest

    How can I get the coordinates of the mouse cursor in Mozilla browsers as
    well as Opera and IE6?
    I'm struggling to understand how to capture mouse movement events with
    Mozilla/Netscape/Firefox and I've Googled so much my brain hurts.

    http://www.ghpkendal.co.uk/TestPages/Test.htm

    Move your cursor over the yellow area and you should see the mouse
    coordinates echoed above.

    The following javascript function works for Opera and IE6 when called by
    onMouseMove="moveDiv()". How do I adapt it to cope with the browser versions
    mentioned above?
    The code snippet in question is listed below. I've not included any feature
    detection to keep things simple.

    divX & divY are locally declared numerical variables in the test page.
    'cpos' is the ID of a <div> element used to display the mouse cursor
    coordinates.

    function movement()
    {
    divX = event.x
    divY = event.y
    document.getElementById('cpos').innerHTML = "X=" + divX + "<br>Y=" + divY
    }
    Danny@Kendal, Nov 12, 2004
    #1
    1. Advertising

  2. On Fri, 12 Nov 2004 09:25:31 -0000, Danny@Kendal
    <> wrote:

    > How can I get the coordinates of the mouse cursor in Mozilla browsers as
    > well as Opera and IE6?


    [snip]

    > divX & divY are locally declared numerical variables in the test page.


    No, they aren't. They're globally declared.

    [snip]

    > function movement()
    > {
    > divX = event.x
    > divY = event.y


    The first problem is that you're accessing the event object as though it's
    global. Whilst this might be true in IE, and browsers that emulate it for
    compatibility (like Opera), Netscape-emulating/DOM-conforming browsers
    must use a local event object. If the event listener is added directly,
    this object is passed as an argument. In intrinsic events added through
    HTML, there is an implicit local variable called "event". Quite
    conveniently really, as this allows for:

    ... onmousemove="movement(event);" ...

    which will work with both IE and conforming browsers.

    That could change your code to:

    function movement(evt) {
    var divX = evt.x,
    divY = evt.y;

    /* [write output] */
    }

    However, there's still one more problem: x and y aren't standard
    properties of the event object. The W3C defines clientX/Y for coordinates
    within the browser viewport. You could get away with:

    var divX = evt.x || evt.clientX,
    divY = evt.y || evt.clientY;

    but something more complex may be more reliable.

    var movement = (function(e) {
    /* The default getCoordinates (gC) function. If neither the
    * Microsoft or DOM approach is deemed supported, this will be
    * used to always return (0, 0).
    */
    function gC(e) {return {x: 0, y: 0};}
    /* The DOM getCoordinates (dC) function. */
    function dC(e) {return {x: e.clientX, y: e.clientY};}
    /* The Microsoft getCoordinates (mC) function. */
    function mC(e) {return {x: e.x, y: e.x};}
    /* Tests if the given argument is a number. */
    function isN(o) {return 'number' == typeof o;}

    /* Check if the clientX and clientY event properties are
    * supported. If so, replace the default, gC, with dC.
    */
    if(isN(e.clientX) && isN(e.clientY)) {gC = dC;}
    /* If not, try again with the x and y properties and replace
    * gC with mC if successful.
    */
    else if(isN(e.x) && isN(e.x)) {gC = mC;}

    /* Now our testing is out of the way, replace the initial
    * function with a streamlined version and call it.
    */
    (movement = function(e) {
    var div = gC(e);

    /* [write output using div.x and div.y] */

    })(e);
    });

    Please be aware that if you change the name, movement, you must edit the
    line

    (movement = function(e) {

    accordingly.

    [snip]

    Tested on IE 6, Firefox 0.9.3 (must get round to install 1.0) and Opera
    7.54.

    Hope that helps,
    Mike

    --
    Michael Winter
    Replace ".invalid" with ".uk" to reply by e-mail.
    Michael Winter, Nov 12, 2004
    #2
    1. Advertising

  3. Danny@Kendal

    Danny@Kendal Guest

    "Michael Winter" <> wrote in message
    news:eek:pshcl10u4x13kvk@atlantis...
    > On Fri, 12 Nov 2004 09:25:31 -0000, Danny@Kendal
    > <> wrote:
    >
    > > How can I get the coordinates of the mouse cursor in Mozilla browsers as
    > > well as Opera and IE6?

    >
    > [snip]


    You absolute feckin' beauty! Thank you *very* much for your time.

    I've updated the test page with all the testing inlined to make it clearer
    (to me) what is happening. It seems to work in all the browsers I've tried
    so far without throwing up any javascript errors or alerts.

    I've renamed it and I'll leave it up indefinitely for reference.
    http://www.ghpkendal.co.uk/TestPages/MouseCoords.htm

    I've printed out your very helpful reply for future tinkering. I didn't know
    you could return values from a function in that way - particularly returning
    the results from one function into another so you only have one place from
    where to extract the values.

    I've tried Firefox 1.0 - very nice rendering but I prefer the Opera
    interface. I still like IE6 but it needs updating.
    Danny@Kendal, Nov 12, 2004
    #3
  4. On Fri, 12 Nov 2004 12:16:41 -0000, Danny@Kendal
    <> wrote:

    [snip]

    > You absolute feckin' beauty! Thank you *very* much for your time.


    You're welcome. :)

    > I've updated the test page with all the testing inlined to make it
    > clearer (to me) what is happening. It seems to work in all the browsers
    > I've tried so far without throwing up any javascript errors or alerts.


    It shouldn't. I still don't like the global variables.

    [snip]

    > I've printed out your very helpful reply for future tinkering. I didn't
    > know you could return values from a function in that way - particularly
    > returning the results from one function into another so you only have
    > one place from where to extract the values.


    If it helps, consider it this way...

    When you define a function using

    function identifier(/* ... */) {
    /* ... */
    }

    it's analogous to

    var identifier = function(/* ... */) {
    /* ... */
    };

    That is, you create a variable, identifier, and assign a function to it.

    Now, functions are objects and, just like with other objects, you can use
    more than one identifier to refer to the same object.

    Combining those two principles together, you can see that you could assign
    the reference to some function, A, to the identifier of some other
    function, B. Both A and B would now refer to the same function[1], so you
    can call that function with either identifier. If you make that assignment
    based on some condition, you can create a self-configuring system: if you
    determine that one function isn't appropriate for a specific action,
    replace it with another that is.

    Since Richard Cornford gave a very nice display of this idea a while ago,
    it's something I think about a lot.

    > I've tried Firefox 1.0 - very nice rendering but I prefer the Opera
    > interface.


    Me, too. I keep Mozilla (several versions) and Firefox for testing.

    > I still like IE6 [...]


    And just when I thought there was hope for you. :p

    Good luck,
    Mike


    [1] If the identifier, B, was the only reference to the second function,
    that function will be destroyed. That is:

    function A() {} // 1
    function B() {} // 2
    B = A;

    Function 2 can't be accessed any more as nothing refers to it, so it's
    deleted on the next garbage collection cycle.

    --
    Michael Winter
    Replace ".invalid" with ".uk" to reply by e-mail.
    Michael Winter, Nov 12, 2004
    #4
  5. Danny@Kendal

    DU Guest

    Michael Winter wrote:
    > On Fri, 12 Nov 2004 09:25:31 -0000, Danny@Kendal
    > <> wrote:
    >
    >> How can I get the coordinates of the mouse cursor in Mozilla browsers
    >> as well as Opera and IE6?

    >


    The answer is that you need cross-browser code since MSIE 6 does not
    support DOM 2 Events interface attributes and methods.

    >
    > [snip]
    >
    >> divX & divY are locally declared numerical variables in the test page.

    >
    >
    > No, they aren't. They're globally declared.
    >
    > [snip]
    >
    >> function movement()
    >> {
    >> divX = event.x
    >> divY = event.y

    >
    >
    > The first problem is that you're accessing the event object as though
    > it's global. Whilst this might be true in IE, and browsers that emulate
    > it for compatibility (like Opera), Netscape-emulating/DOM-conforming
    > browsers must use a local event object. If the event listener is added
    > directly, this object is passed as an argument. In intrinsic events
    > added through HTML, there is an implicit local variable called "event".
    > Quite conveniently really, as this allows for:
    >
    > ... onmousemove="movement(event);" ...
    >
    > which will work with both IE and conforming browsers.
    >
    > That could change your code to:
    >
    > function movement(evt) {
    > var divX = evt.x,
    > divY = evt.y;
    >
    > /* [write output] */
    > }
    >
    > However, there's still one more problem: x and y aren't standard
    > properties of the event object.


    x and y event properties are coordinates relative to relatively
    positioned elements in MSIE.

    Interactive demo on event.x and event.y
    http://www10.brinkster.com/doctorunclear/HTMLJavascriptCSS/PositioningEventXY.html

    I've reopened an Opera bugfile 123298 on this btw.
    http://www10.brinkster.com/doctorunclear/BrowserBugsSection/Opera7Bugs/Opera7EventXY.html


    The W3C defines clientX/Y for
    > coordinates within the browser viewport. You could get away with:
    >
    > var divX = evt.x || evt.clientX,
    > divY = evt.y || evt.clientY;
    >


    The above does not make sense. clientX and clientY are well supported
    event properties in browsers. x and y are not and are not very useful.
    Even support for x and y properties were dropped in Mozilla (they were
    supported in NS 4). And as said before, x and y are relative to
    relatively positioned elements while clientX and clientY are relative to
    browser window viewport (not to document)

    > but something more complex may be more reliable.
    >
    > var movement = (function(e) {
    > /* The default getCoordinates (gC) function. If neither the
    > * Microsoft or DOM approach is deemed supported, this will be
    > * used to always return (0, 0).
    > */
    > function gC(e) {return {x: 0, y: 0};}
    > /* The DOM getCoordinates (dC) function. */
    > function dC(e) {return {x: e.clientX, y: e.clientY};}
    > /* The Microsoft getCoordinates (mC) function. */
    > function mC(e) {return {x: e.x, y: e.x};}



    I doubt this mC function is really needed.


    > /* Tests if the given argument is a number. */
    > function isN(o) {return 'number' == typeof o;}
    >


    isN is also not needed as the function isNaN is already available and
    widely implemented in javascript browsers.

    > /* Check if the clientX and clientY event properties are
    > * supported. If so, replace the default, gC, with dC.
    > */
    > if(isN(e.clientX) && isN(e.clientY)) {gC = dC;}
    > /* If not, try again with the x and y properties and replace
    > * gC with mC if successful.
    > */
    > else if(isN(e.x) && isN(e.x)) {gC = mC;}
    >
    > /* Now our testing is out of the way, replace the initial
    > * function with a streamlined version and call it.
    > */
    > (movement = function(e) {
    > var div = gC(e);
    >
    > /* [write output using div.x and div.y] */
    >
    > })(e);
    > });
    >


    There may be (not sure; it depends on the design requirements) another
    problem which is not addressed in the above code: when the page content
    exceeds browser window viewport: the returned coordinates will not
    return the coordinates in the document scroll view.

    Interactive window properties, methods, events page
    http://www10.brinkster.com/doctorunclear/HTMLJavascriptCSS/DUWindowsMSIE6.html
    http://www10.brinkster.com/doctorunclear/HTMLJavascriptCSS/DUWindowsNS6.html
    http://www10.brinkster.com/doctorunclear/HTMLJavascriptCSS/DUWindowsOpera7.html

    DU
    --
    The site said to use Internet Explorer 5 or better... so I switched to
    Mozilla 1.7.3 :)
    DU, Nov 12, 2004
    #5
  6. On Fri, 12 Nov 2004 16:01:08 -0500, DU <> wrote:

    [snip]

    > http://www10.brinkster.com/doctorunclear/BrowserBugsSection/Opera7Bugs/Opera7EventXY.html


    From my reading of the reference, I see no bug:

    "This property returns the y-coordinate of the closest relatively
    positioned parent element of the element that fires the event. If
    the event firing element is relatively positioned, then the
    y-coordinate from the boundary of the element is returned."

    As both images are relatively positioned, the offset should be with regard
    to their borders.

    [snip]

    [MW:]
    >> var divX = evt.x || evt.clientX,
    >> divY = evt.y || evt.clientY;

    >
    > The above does not make sense.


    It makes sense, but it's unnecessary. I didn't check whether IE supported
    clientX/Y, and I just assumed it didn't. My bad.

    [snip]

    >> function mC(e) {return {x: e.x, y: e.x};}

    >
    > I doubt this mC function is really needed.


    In light of the above, no, it isn't.

    >> /* Tests if the given argument is a number. */
    >> function isN(o) {return 'number' == typeof o;}

    >
    > isN is also not needed as the function isNaN is already available and
    > widely implemented in javascript browsers.


    The function, isN (isNumber), performs a different action than isNaN. The
    former checks that the argument is a number, whilst the latter checks if
    the argument can be type-converted to a number.

    [snip]

    > [...] the returned coordinates will not return the coordinates in the
    > document scroll view.


    Could you elaborate, specifically with regards to what you're refering to
    with "document scroll view"?

    [snip]

    > http://www10.brinkster.com/doctorunclear/HTMLJavascriptCSS/DUWindowsOpera7.html


    That's a dead link, by the way. The correct URL is
    <URL:http://www10.brinkster.com/doctorunclear/HTMLJavascriptCSS/DUWindowsO7.html>.

    Mike

    --
    Michael Winter
    Replace ".invalid" with ".uk" to reply by e-mail.
    Michael Winter, Nov 12, 2004
    #6
  7. Danny@Kendal

    PDannyD Guest

    On Fri, 12 Nov 2004 21:25:30 +0000, "Michael Winter"
    <> wrote in message
    <opshde78iyx13kvk@atlantis>:

    > On Fri, 12 Nov 2004 16:01:08 -0500, DU <>
    > wrote:
    >
    > [snip]
    >
    >> http://www10.brinkster.com/doctorunclear/BrowserBugsSection/Opera7Bugs/Opera7EventXY.html

    >
    > From my reading of the reference, I see no bug:
    >
    > "This property returns the y-coordinate of the closest relatively
    > positioned parent element of the element that fires the event. If
    > the event firing element is relatively positioned, then the
    > y-coordinate from the boundary of the element is returned."
    >
    > As both images are relatively positioned, the offset should be with
    > regard to their borders.


    >> [...] the returned coordinates will not return the coordinates in the
    >> document scroll view.

    >
    > Could you elaborate, specifically with regards to what you're refering
    > to with "document scroll view"?


    (the OP posting from home)

    If the browser window is resized so that the viewport needs scrolling then
    the script I'm using doesn't return the position of the cursor in the
    viewport but the position relative to the document. I don't really need to
    fix it but I'll have a play when I'm back at work.

    It's not really an issue in my real application as the area where the
    mouse cursor position is needed is fairly small. I try, wherever possible,
    to design for a minimum of 640x480 pixels.

    Today has been a good day - I got a troublesome javascript to work and
    fixed several CSS cross-browser compatibility problems. A complete
    contrast to yesterday which was spent staring into space going 'duh'.

    --
    FZS600 - Silver/Black
    GS125 - Black/Rust
    Ford 100E Prefect - Black, naturally
    Whisky - Aberlour Cask Strength
    PDannyD, Nov 12, 2004
    #7
  8. Michael Winter wrote:
    > Danny@Kendal wrote:

    <snip>
    > Since Richard Cornford gave a very nice display of this
    > idea a while ago, it's something I think about a lot.

    <snip>

    Yep was right, he know you would not be able to resist the idea ;)

    In retrospect the object I tend to use to acquire mouse position
    information was among the first self-reconfiguring object that I wrote
    (before I recognised that as an exploitable concept in itself). It
    doesn't do much function swapping but instead re-assigns strings used as
    property names and object references to configure itself. It was
    designed to keep the mousemove handling function as fast and simple as
    possible and to exploit the fact that you don't ever need to know that
    mouse position as frequently as mousemove events happen. It also
    normalises the co-ordinates to the same relative system (offsets into
    the HTML page). I have posted it a few times before but it probably
    deserves another airing in this context:-

    var MoveFollow = (function(){
    var global = this;
    var theInterface = {
    getPageX:function(){
    return (((readScroll.scrollLeft|0) -
    (readScroll.clientLeft|0)) + x);
    },
    getPageY:function(){
    return (((readScroll.scrollTop|0) -
    (readScroll.clientTop|0)) + y);
    },
    getPageXY:function(){
    return {x:this.getPageX(),y:this.getPageY()};
    },
    getTarget:function(){
    return lastTarget;
    },
    upDate:function(ev){
    mm(ev);
    }
    };
    var x = 0, y = 0;
    var readScroll = {scrollTop:0,scrollLeft:0,
    clientLeft:0,clientTop:0};
    var lastTarget = null;
    var posReadX = 'pageX';
    var posReadY = 'pageY';
    function mm(e){
    e = e||global.event;
    x = e[posReadX];
    y = e[posReadY];
    lastTarget = e.target||e.srcElement;
    }
    function initEvent(ev){
    if(document.body){ //Not safe to configure until the
    //BODY exists in the DOM.
    ev = ev||global.event;
    if(ev){
    if(typeof ev.pageX != 'number'){ //Opera 7 has pageX
    posReadX = 'clientX';
    posReadY = 'clientY';
    if((typeof document.compatMode == 'string')&&
    (document.compatMode.indexOf('CSS') != -1)&&
    (document.documentElement)){
    readScroll = document.documentElement;
    }else if((document.body)&&(!global.opera)){
    //Not an Opera <= 6 browser (becaue its
    //clientX/Y are page-relative already).
    readScroll = document.body;
    }
    }
    setUpOnMouse(mm);
    mm(ev);
    }
    }
    }
    function setUpOnMouse(f){
    if(document.onmousemove != f){
    document.onmousemove = f;
    if((document.captureEvents)&&(global.Event)&&
    (document.layers)&&(typeof Layer == 'function')){
    //Netscape 4 *only* (or probably harmless in context)
    document.captureEvents(Event.MOUSEMOVE);
    /* captureEvents on Netscape 6+/Mozila/Gecko would
    cause all move events to be processed in the
    capturing phase in addition to when they arrived
    at the target. There is no reason to be
    processing the same event twice (and good reasons
    for avoiding doing so).
    */
    }
    }
    }
    setUpOnMouse(initEvent); // Attach initEvent as the initial
    // mousemove handler.
    return (function(){
    return theInterface;
    });
    })();

    Richard.
    Richard Cornford, Nov 13, 2004
    #8
  9. On Sat, 13 Nov 2004 00:36:50 -0000, Richard Cornford
    <> wrote:

    [snip]

    > Yep was right, he know you would not be able to resist the idea ;)


    It's always fun to have a new toy to play with. :)

    > I have posted it a few times before but it probably deserves another
    > airing in this context:-


    I can't say that I've seen it before. I'm glad you posted it, though. My
    knowledge is weak when it comes to the various dimension-related
    properties.

    [snip]

    Mike

    --
    Michael Winter
    Replace ".invalid" with ".uk" to reply by e-mail.
    Michael Winter, Nov 13, 2004
    #9
  10. On Fri, 12 Nov 2004 21:25:30 GMT, Michael Winter
    <> wrote:

    > On Fri, 12 Nov 2004 16:01:08 -0500, DU <>
    > wrote:


    [snip]

    >> [...] the returned coordinates will not return the coordinates in the
    >> document scroll view.

    >
    > Could you elaborate, specifically with regards to what you're refering
    > to with "document scroll view"?


    I think I know what you mean, now.

    If the cursor is twenty pixels in from the left-hand edge, you'd expect
    clientX to return 20. However, if the document was scrolled ten pixels to
    the right, you might expect clientX to return 30 but it should still
    return 20.

    Based on that interpretation, no my suggestion wouldn't treat a scrolled
    page specially. However, from reading through Richard's, it seems his
    would.

    Mike

    --
    Michael Winter
    Replace ".invalid" with ".uk" to reply by e-mail.
    Michael Winter, Nov 13, 2004
    #10
  11. Danny@Kendal

    DU Guest

    Michael Winter wrote:

    > On Fri, 12 Nov 2004 16:01:08 -0500, DU <> wrote:
    >
    > [snip]
    >
    >> http://www10.brinkster.com/doctorunclear/BrowserBugsSection/Opera7Bugs/Opera7EventXY.html
    >>

    >
    >
    > From my reading of the reference, I see no bug:
    >
    > "This property returns the y-coordinate of the closest relatively
    > positioned parent element of the element that fires the event. If
    > the event firing element is relatively positioned, then the
    > y-coordinate from the boundary of the element is returned."
    >
    > As both images are relatively positioned, the offset should be with
    > regard to their borders.
    >


    The offset is relative to their borders but when you mouse over these,
    the returned evt.x and evt.y in Opera 7.x are wrong.

    Regarding the OP post, the x, y-coordinates are relative to relatively
    positioned elements: if the page has relatively positioned elements,
    then the returned values won't be relative to client viewport. That is
    where your code is wrong.


    > [snip]
    >
    > [MW:]
    >
    >>> var divX = evt.x || evt.clientX,
    >>> divY = evt.y || evt.clientY;

    >>
    >>
    >> The above does not make sense.

    >
    >
    > It makes sense, but it's unnecessary. I didn't check whether IE
    > supported clientX/Y, and I just assumed it didn't. My bad.
    >


    The above is wrong when the page will have relatively positioned
    elements. When the mouse will hover over those relatively positioned
    elements, the coordinates will not return browser window viewport
    coordinates but coordinates relative to those relatively positioned
    elements. As you say, just clientX/Y is sufficient.

    DU
    --
    The site said to use Internet Explorer 5 or better... so I switched to
    Mozilla 1.7.3 :)
    DU, Nov 13, 2004
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Max
    Replies:
    7
    Views:
    9,106
  2. Jean-Benoit MORLA
    Replies:
    1
    Views:
    3,778
    Michael Borgwardt
    May 24, 2004
  3. tom arnall
    Replies:
    6
    Views:
    1,280
    Ian Shef
    Jan 18, 2007
  4. dmaziuk
    Replies:
    3
    Views:
    577
    Chris Gonnerman
    Jan 25, 2011
  5. James Black
    Replies:
    0
    Views:
    401
    James Black
    May 28, 2006
Loading...

Share This Page