正确设置绝对定位的dom元素的位置

By | August 20, 2009

先前在做ASTreeView右键菜单时遇到一个问题,是这样的:

因为页面的布局中,放置ASTreeView控件的容器叫container,当用户右键点击树节点时,edit, delete节点的菜单出现。由于container的position是relative的,右键菜单的div的position是absolute的,所以,设置菜单div的left, top属性时,会根据container的位置去定位,如图所示:

 

image

当用户点击右键,我们可以获取当前鼠标的坐标,这时,如果直接将鼠标的x,y坐标赋值给菜单的left和top就会出问题。因为鼠标坐标的原点是窗口的左上角,而菜单的原点是container的左上角。

那如何让菜单显示在鼠标点击的位置呢?接下来的代码将一步一步地实现:

首先是获取鼠标坐标的代码:

function getMousePos(e) {

        e = e || event;

        var posx = 0;
        var posy = 0;

        if ( e.pageX && e.pageY )     {
            posx = e.pageX;
            posy = e.pageY;
        }
        else if (e.clientX && e.clientY )     {
            posx = e.clientX + document.body.scrollLeft
                + document.documentElement.scrollLeft;

            posy = e.clientY + document.body.scrollTop
                + document.documentElement.scrollTop;
        }

        return {
            'x' : posx,
            'y' : posy
        }
    }

接着我们将要得到container相对于窗口左上角的距离,当然,这要考虑container的父节点,也就是要一直递归到根节点:

function getTotalOffset( elem ){
        var offsetTotalX = 0;
        var offsetTotalY = 0;
        var pOffsetElem = elem.offsetParent;

        while( pOffsetElem ){
            offsetTotalX += pOffsetElem.offsetLeft;
            offsetTotalY += pOffsetElem.offsetTop;

            pOffsetElem = pOffsetElem.offsetParent;
        }

        return {
            'x' : offsetTotalX,
            'y' : offsetTotalY
        }
    }

最后在设置菜单位置的时候,将鼠标的位置,减去container的offset:

var pos = getMousePos(e);
var offset = getTotalOffset(elem);

menu.style.left = ( pos.x - offset.x ) + 'px';
menu.style.top = ( pos.y - offset.y ) + 'px';

这样,菜单就能显示在鼠标点击的位置啦!

完整示例下载:

点击下载