Inaccuracies in absolute positioning

Discussion in 'Javascript' started by Csaba2000, Sep 1, 2003.

  1. Csaba2000

    Csaba2000 Guest

    Demo page at http://csaba.org/Demos/getRect.htm

    One question I've seen come up multiple times is:
    How can I find the abolute position of my favorite element
    on the page? The standard answer is not working for me
    on any of IE/Netscape/Opera! The above page demonstrates
    the problem. I'd love to know how to make it work correctly.

    The usual answer, which I have been using, too, is something like:
    function getRect(elem) {
    // return (Left,Top,Right,Bottom) of the element
    var myRect = Array(elem.offsetLeft, elem.offsetTop,
    elem.offsetLeft + elem.offsetWidth,
    elem.offsetTop + elem.offsetHeight)
    if (!elem.offsetParent) return myRect;
    newRect = getRect(elem.offsetParent)
    for (var i=0;i<4;i++) myRect = myRect + newRect[i % 2];
    return myRect;
    }

    This is close, but I am not doing government work, and I need something
    exact. Specifically, As I move my mouse around a table, whenever the
    mouse passes over a table border, I want to be able to identify which
    two cells it is between. Let's leave that problem alone for now, since it's
    much more difficult (when rowspan/colspan are involved) and just
    concentrate on figuring out when the mouse is over an external border.
    Now the mouse already knows which element it is over, and you can see
    this as the mouse changes form as it goes from one cell, over
    a border, then to another.

    So the plan is that whenever the table gets an onmouseover event,
    we'll compare the mouse position to the top left TD and the bottom
    right TD element to find out if we're at an external border.
    My IE 5.5, NN 6.1 and Opera 7.01 on Win 2K Pro all fail these
    comparisons on exactly two borders each, and we are talking that there
    is a two pixel difference! The observed behaviour is that when you
    approach the border from the outside, it always works but going from
    the inside to the outside of the table it fails on two borders because of
    the two pixel difference.

    Can anyone spot how to fix this problem up?
    Thanks for any tips,
    Csaba Gabor from New York

    PS. OK, the code to test for the right border cannot assume that the
    last cell of the last row is rightmost in the table, but let's not worry
    about that till after the main problem is solved.
     
    Csaba2000, Sep 1, 2003
    #1
    1. Advertising

  2. "Csaba2000" <> wrote in message
    news:bj0h4m$...
    <snip>
    >... there is a two pixel difference! The observed behaviour
    >is that when you approach the border from the outside, it
    >always works but going from the inside to the outside of the
    >table it fails on two borders because of the two pixel
    >difference.


    > Can anyone spot how to fix this problem up?
    > Thanks for any tips,

    <snip>

    Your problem is with the position of the 0,0 co-ordinate for mouse
    positions. On Opera, Mozilla and other non-IE browsers the 0,0 position
    is the top left of the HTML page within the viewport. On IE the 0,0
    position is outside the inner border (or the top left of the inner
    border). A difference of 2 pixels by default. Unfortunately, on IE,
    chaining the offsetTop/Left values produces co-ordinates with a 0,0
    position in the top-left corner of the viewport (just inside the inner
    border. That is equivalent to the situation on Mozilla, etc.

    The solution that I use is to adjust the mouse co-ordinates by the
    clientTop/Left values for the root element (body or documentElement
    depending on compatMode). I use mouse position reading scripts such as
    the following:-

    var MoveFollow = (function(){
    var theOne = null;
    var XY = {x:0,y:0};
    var readScroll = {scrollTop:0,scrollLeft:0,
    clientLeft:0,clientTop:0};
    var eventInited = false;
    var posReadX = 'pageX';
    var posReadY = 'pageY';
    function mm(e){
    e = e||event;
    XY.x = e[posReadX];
    XY.y = e[posReadY];
    };
    function initEvent(ev){
    ev = ev||event;
    if(ev){
    if(typeof ev.pageX != 'number'){
    posReadX = 'clientX';
    posReadY = 'clientY';
    if(typeof opera != 'object'){
    if((document.compatMode)&&
    (document.compatMode == 'CSS1Compat')&&
    (document.documentElement)){
    readScroll = document.documentElement;
    }else if(document.body){
    readScroll = document.body;
    }
    }
    }
    setUpOnMouse(mm);
    mm(ev);
    eventInited = true;
    }else{
    setUpOnMouse(initEvent);
    }
    };
    function setUpOnMouse(f){
    if(document.onmousemove != f){
    document.onmousemove = f;
    if((document.captureEvents)&&
    (document.layers)&&(typeof Layer == 'function')){
    document.captureEvents(Event.MOUSEMOVE);
    }
    }
    };
    return (function(ev){
    if(!eventInited)initEvent(ev);
    if(theOne)return theOne;
    this.getPageX = function(){
    return ((readScroll.scrollLeft||0) -
    (readScroll.clientLeft||0) + XY.x);
    };
    this.getPageY = function(){
    return ((readScroll.scrollTop||0) -
    (readScroll.clientTop||0) + XY.y);
    };
    this.getPageXY = function(){
    return {x:this.getPageX(),y:this.getPageY()};
    };
    this.upDate = function(ev){
    mm(ev);
    }
    theOne = this;
    });
    })(); //simultaneously define and call (one-off)!

    This code is a mouse position following object that uses the
    document.onmousemove handler to track the position of the mouse. The
    object is created with - new MoveFollow() - or new
    MoveFollow(mouseEvent) - (if a mouse event is available at construction
    time, onload,etc events are not suitable as they do not include mouse
    properties on Mozilla, for example. It is probably better not to pass an
    event to the constructor as it will compensate by setting up based on
    the first mousemove event received). The class is a singleton so no
    matter how many examples are created with the - new - keyword only one
    object is instantiated and all subsequent calls return references to
    that object. Given:-

    var readMousePosition = new MoveFollow();

    - the position of the mouse (at the time of the last mouse move event)
    can be read as:-

    var X = readMousePosition.getPageX();
    var Y = readMousePosition.getPageY();

    Part of the point of this code is that the mouse position reading
    function is very light weight, which is necessary as mousemove events
    are _very_ frequent and code that reacts to them should be quick.

    The important adjustment that corrects the 2 pixel discrepancy on IE is
    subtracting the root element clientTop/Left values, which is only done
    in the getpageX/Y functions.

    Richard.
     
    Richard Cornford, Sep 2, 2003
    #2
    1. Advertising

  3. Csaba2000

    Csaba2000 Guest

    Richard,

    Thanks very much for your explanation and functions.
    I have updated my code, and it is now working in all the
    browsers that I cited earlier.

    There is a another point that I have to confess that I
    missed. My getRect function was incorrect (oops) as
    written and that is why Opera and Netscape were messing
    up. I was forgetting to subtract one for the Right and Bottom
    values in getRect and that extra pixel made the difference.

    Now I am ready to move on to the next step of identifying
    which cells the mouse is between if it's not over a single cell.

    Thanks again for your reply, Richard,
    Csaba

    function getRect(elem) {
    // return absolute position of elem
    // (Left, Top, Right, Bottom) in page
    var i;
    if (typeof(elem.myRect)!="undefined") return elem.myRect;
    var myRect = Array(elem.offsetLeft, elem.offsetTop,
    elem.offsetLeft + elem.offsetWidth-1,
    elem.offsetTop + elem.offsetHeight-1)
    // see Richard's code for definition of readScroll
    if (!elem.offsetParent) {
    var xAdjust = (window.readScroll.clientLeft||0)-(window.readScroll.scrollLeft||0)
    var yAdjust = (window.readScroll.clientTop||0)-(window.readScroll.scrollTop||0)
    for (i=0;i<4;i++) myRect += (i%2)?yAdjust:xAdjust;
    return myRect;
    }
    newRect = getRect(elem.offsetParent)
    for (i=0;i<4;i++) myRect = myRect + newRect[i % 2];
    return myRect;
    }


    "Richard Cornford" <> wrote in message news:bj0mre$ah9$1$...
    > "Csaba2000" <> wrote in message
    > news:bj0h4m$...
    > <snip>
    > >... there is a two pixel difference! The observed behaviour
    > >is that when you approach the border from the outside, it
    > >always works but going from the inside to the outside of the
    > >table it fails on two borders because of the two pixel
    > >difference.

    >
    > > Can anyone spot how to fix this problem up?
    > > Thanks for any tips,

    > <snip>
    >
    > Your problem is with the position of the 0,0 co-ordinate for mouse
    > positions. On Opera, Mozilla and other non-IE browsers the 0,0 position
    > is the top left of the HTML page within the viewport. On IE the 0,0
    > position is outside the inner border (or the top left of the inner
    > border). A difference of 2 pixels by default. Unfortunately, on IE,
    > chaining the offsetTop/Left values produces co-ordinates with a 0,0
    > position in the top-left corner of the viewport (just inside the inner
    > border. That is equivalent to the situation on Mozilla, etc.
    >
    > The solution that I use is to adjust the mouse co-ordinates by the
    > clientTop/Left values for the root element (body or documentElement
    > depending on compatMode). I use mouse position reading scripts such as
    > the following:-
    >
    > var MoveFollow = (function(){
    > var theOne = null;
    > var XY = {x:0,y:0};
    > var readScroll = {scrollTop:0,scrollLeft:0,
    > clientLeft:0,clientTop:0};
    > var eventInited = false;
    > var posReadX = 'pageX';
    > var posReadY = 'pageY';
    > function mm(e){
    > e = e||event;
    > XY.x = e[posReadX];
    > XY.y = e[posReadY];
    > };
    > function initEvent(ev){
    > ev = ev||event;
    > if(ev){
    > if(typeof ev.pageX != 'number'){
    > posReadX = 'clientX';
    > posReadY = 'clientY';
    > if(typeof opera != 'object'){
    > if((document.compatMode)&&
    > (document.compatMode == 'CSS1Compat')&&
    > (document.documentElement)){
    > readScroll = document.documentElement;
    > }else if(document.body){
    > readScroll = document.body;
    > }
    > }
    > }
    > setUpOnMouse(mm);
    > mm(ev);
    > eventInited = true;
    > }else{
    > setUpOnMouse(initEvent);
    > }
    > };
    > function setUpOnMouse(f){
    > if(document.onmousemove != f){
    > document.onmousemove = f;
    > if((document.captureEvents)&&
    > (document.layers)&&(typeof Layer == 'function')){
    > document.captureEvents(Event.MOUSEMOVE);
    > }
    > }
    > };
    > return (function(ev){
    > if(!eventInited)initEvent(ev);
    > if(theOne)return theOne;
    > this.getPageX = function(){
    > return ((readScroll.scrollLeft||0) -
    > (readScroll.clientLeft||0) + XY.x);
    > };
    > this.getPageY = function(){
    > return ((readScroll.scrollTop||0) -
    > (readScroll.clientTop||0) + XY.y);
    > };
    > this.getPageXY = function(){
    > return {x:this.getPageX(),y:this.getPageY()};
    > };
    > this.upDate = function(ev){
    > mm(ev);
    > }
    > theOne = this;
    > });
    > })(); //simultaneously define and call (one-off)!
    >
    > This code is a mouse position following object that uses the
    > document.onmousemove handler to track the position of the mouse. The
    > object is created with - new MoveFollow() - or new
    > MoveFollow(mouseEvent) - (if a mouse event is available at construction
    > time, onload,etc events are not suitable as they do not include mouse
    > properties on Mozilla, for example. It is probably better not to pass an
    > event to the constructor as it will compensate by setting up based on
    > the first mousemove event received). The class is a singleton so no
    > matter how many examples are created with the - new - keyword only one
    > object is instantiated and all subsequent calls return references to
    > that object. Given:-
    >
    > var readMousePosition = new MoveFollow();
    >
    > - the position of the mouse (at the time of the last mouse move event)
    > can be read as:-
    >
    > var X = readMousePosition.getPageX();
    > var Y = readMousePosition.getPageY();
    >
    > Part of the point of this code is that the mouse position reading
    > function is very light weight, which is necessary as mousemove events
    > are _very_ frequent and code that reacts to them should be quick.
    >
    > The important adjustment that corrects the 2 pixel discrepancy on IE is
    > subtracting the root element clientTop/Left values, which is only done
    > in the getpageX/Y functions.
    >
    > Richard.
    >
    >
     
    Csaba2000, Sep 2, 2003
    #3
    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. John A Grandy
    Replies:
    0
    Views:
    533
    John A Grandy
    Nov 17, 2004
  2. Rob R. Ainscough
    Replies:
    2
    Views:
    1,669
    Rob R. Ainscough
    Feb 7, 2006
  3. Fred Nelson
    Replies:
    1
    Views:
    2,469
    Christopher Reed
    Feb 21, 2006
  4. cornelis van der bent

    inaccuracies in compression algorithm

    cornelis van der bent, Jul 23, 2009, in forum: C Programming
    Replies:
    18
    Views:
    551
    Keith Thompson
    Jul 24, 2009
  5. it_says_BALLS_on_your forehead

    simple regex benchmark inaccuracies

    it_says_BALLS_on_your forehead, Feb 24, 2006, in forum: Perl Misc
    Replies:
    7
    Views:
    146
Loading...

Share This Page