鼠标位置(跨浏览器)

处理鼠标事件的时候,会遇到各种位置属性,这里做一下简单的总结:

鼠标事件属性

clientX, clientY

Standard: W3C Recommendation

相对于可见视点(visual viewport)的鼠标位置,例如点击客户端的左上角时 clientX 和 clientY 都为0,即使用户拖动了水平或垂直滚动条

screenX, screenY

Standard: W3C Recommendation

相对于用户屏幕的鼠标位置

offsetX, offsetY

Standard: W3C Working Draft

相对于事件target的鼠标位置,这个属性各个浏览器的实现不一致

pageX, pageY

Standard: W3C Working Draft

相对于html文档,即布局视点(layout viewport)的鼠标位置

x, y

Standard: W3C Working Draft

与clientX, clientY等同,但是有些浏览器不支持

layerX, layerY

No Standard

相对于最近的被 positioned 的祖先元素,如果没有被 positioned 的祖先元素,就相对于html文档(类似于pageX, pageY)

标准化

计算pageX, pageY

IE8 以下不支持这两个属性。如果你使用jQuery时,它会自动为你标准化这两个属性。如果你使用的不是jQuery的标准事件,而是原生事件对象,你可以使用jQuery.event.fix函数来标准事件对象。例如:

document.body.onclick = function(e) {  
    e = e || window.event;
    e = jQuery.event.fix(e);
    console.log([e.pageX, e.pageY]);
};

在没有jQuery的情况下,可以通过将 pageX 和 pageY 与 scrollLeft 和 scrollTop 相加来计算 clientX 和 clientY

document.body.onclick = function(e) {  
    e = e || window.event;

    var pageX = e.pageX;
    var pageY = e.pageY;
    if (pageX === undefined) {
        pageX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
        pageY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }

    console.log([pageX, pageY]);
};

计算offsetX, offsetY

根据W3C工作草案,offsetX 和 offsetY 应该相对于事件的 target 元素的内边距边缘。只用 IE 符合此标准,Webkit 相对的是边框边缘,Opera 相对的是内容边缘,而 FireFox 不支持该属性。

通过 Element.getBoundingClientRect 函数,我们能够很容易的实现 Webkit 的标准-相对于边框边缘:

document.body.onclick = function(e) {  
    e = e || window.event;

    var target = e.target || e.srcElement,
        rect = target.getBoundingClientRect(),
        offsetX = e.clientX - rect.left,
        offsetY = e.clientY - rect.top;

    console.log([offsetX, offsetY]);
};

如果要实现W3C草案,只需要减去边框目标元素的边框大小即可:

document.body.onclick = function(e) {  
    e = e || window.event;

    var target = e.target || e.srcElement,
        style = target.currentStyle || window.getComputedStyle(target, null),
        borderLeftWidth = parseInt(style['borderLeftWidth'], 10),
        borderTopWidth = parseInt(style['borderTopWidth'], 10),
        rect = target.getBoundingClientRect(),
        offsetX = e.clientX - borderLeftWidth - rect.left,
        offsetY = e.clientY - borderTopWidth - rect.top;

    console.log([offsetX, offsetY]);
};
Author image
关于 superlin
Beijing, CN 主页
The reason why a great man is great is that he resolves to be a great man.
默认颜色 边栏居左 边栏居右