【JS温故知新】之——给力的鼠标坐标位置获取

来源:互联网 发布:虚拟服务器软件 编辑:程序博客网 时间:2024/05/02 01:04

我不是一个脑子灵光的人,所以学东西忘得快。姑且记录下来,以后方便温故知新。当然,内容中加入了我的一些理解与标注,如有不准之处,还望及时指出,一块提高。

鼠标事件的坐标位置的类型

其实无非就是以下几种

  1. 相对于客户端浏览区域的坐标(客户区坐标)
  2. 相对于整个文档中的坐标(文档绝对坐标)
  3. 相对于屏幕的坐标(屏幕坐标)
  4. 相对于触发事件的元素所在的层级关系中最近的具有position相关/绝对定位的容器的坐标

我这里讨论的是第二种,相对于网页文档中的绝对坐标,即以页面左上角为参照点,横轴水平向左,纵轴垂直向下的二维坐标的值。

文档绝对坐标是经常会用到的,不推荐只使用客户区坐标。为什么呢?这里主要是考虑到鼠标在页面上移动时浏览器窗口中文档位置是可能随时变化的,也就是说文档会滚动、会产生滚动距离滴,所以必须使用绝对坐标才能很好的满足我们不同情况下的需求。

鼠标事件的坐标属性

大致有以下几个

属性

描述

兼容性(+表示支持,-表示暂不支持)

pageX/pageY

文档绝对坐标

W3C- IE- Firefox+ Opera+ Safari+ Chrome+ (IE9+)

offsetX/offsetY

相对于“触发事件的元素”的位置

W3C- IE+ Firefox- Opera+ Safari+ Chrome+

layerX/layerY

相对于触发事件的元素所在的具有position定位的父级容器坐标(我的理解诶,很拗口,不多做解释)

W3C- IE- Firefox+ Opera- Safari+ Chrome+

x/y

同上

W3C- IE+ Firefox- Opera+ Safari+ Chrome+

clientX/clientY

客户区坐标

W3C+ IE+ Firefox+ Opera+ Safari+ Chrome+

screenX/screenY

屏幕坐标

W3C+ IE+ Firefox+ Opera+ Safari+ Chrome+

从以上表格可以看出,要想获取相对于文档的绝对坐标位置,并且兼容主流浏览器,使用以下代码可达到目的

function getMousePoint (e){
    var x = y = 0;    
    if(!e) e = window.event;
    if (e.pageX && e.pageY) {//标准浏览器下
        x = e.pageX;
        y = e.pageY;
    } else if(e.offsetX && e.offsetY) {//IE下
        x = e.offsetX;
        y = e.offsetY;
    }
    return {'x' : x,
            'y' : y }
}

此方法简单明了,通过主流浏览器测试。但是,我不推荐使用此方法,因为pageX, offsetX等不是W3C标准中的事件属性。

使用W3C支持的属性

根据上面的鼠标事件的坐标相关属性表格,W3C支持的也就clientX/clientY了。

clientX/clientY是获得客户区坐标,要想得到相对于文档的绝对坐标,还需要加上页面的滚动高度。

什么意思呢?看图理解

浏览器坐标如我图中所示(图很简单,凑活看),可得出文档绝对高度(X坐标)=滚动高度(scrollTop)+客户区高度(clientY),相应的,文档绝对X坐标=左滚距离(scrollLeft)+客户区高度(clientX)

不同渲染模式下需要注意的一点

我们知道随着W3C标准的出台,浏览器对于网页的解释有了两种模式,即使用老规范的Quirks mode(怪癖模式)和支持新标准的Standars mode(标准模式或称strict mode严格模式)。

浏览器是根据什么来判断该使用哪种渲染模式的呢?那就是DTD,既是网页的头部声明,浏览器会通过识别DTD而采用相对应的渲染模式。即对于声明了!DOCTYPE且使用HTML4以或者以上(包括未来的html5)的页面会采用Standars mode,而那些未声明DOCTYPE或者声明了DOCTYPE但是使用的是html4以下标准,则会使用Quirks mode渲染了。

这两种模式下有什么不同呢?

我们要获得鼠标的文档绝对坐标,那就需要得到页面滚动距离,在标准渲染模式中,可以使用

document.documentElement.scrollTop//滚动高度
document.documentElement.scrollLeft//左滚动距离

来获得相应的滚动距离。但是在怪癖模式中,是不存在documentElement的,所以就需要

document.body.scrollTop
document.body.scrollLeft

来获得滚动距离了。

标准模式下难道不能使用document.body.scrollTop吗

不可以,标准模式下document.body.scrollLeft和document.body.scrollTop的值都是0,只有在怪癖模式下使用这种方法才有效。

2px的误差

很多人说是IE下的误差,我实际测试发现并不是IE下,而是怪癖模式下body默认会有2px的边距,所以要想获得精确值,需要减去body的这个边距,即减去document.body.clientTop/document.body.clientLeft即可。

最后完整的代码

function getMousePoint(ev) {
    var x=y=0;if(!ev) ev=window.event;
    if (typeof window.pageYoffset!= 'undefined') {//pageYoffset是Netscape特有
        x = window.pageXOffset;
        y = window.pageYOffset;
    }else if (typeof document.compatMode != 'undefined' && document.compatMode != 'BackCompat') {//检测标准模式
        x = document.documentElement.scrollLeft;
        y = document.documentElement.scrollTop;
    }else if (typeof document.body != 'undefined') {
        x = document.body.scrollLeft - document.body.clientLeft;
        y = document.body.scrollTop - document.body.clientTop;
    }
    x += ev.clientX;
    y += ev.clientY;
    return {'x' : x, 'y' : y};
}

以上代码出自《JavaScript Dom高级程序设计》这本书中介绍的ADS库(经yun77op提醒,不是出自本书,具体哪里我已记不得来了)。当然,我修改了一些。我之所以给出这段代码,主要是为了介绍标准模式与怪癖模式的检测方法,有兴趣了解这点的同学可以仔细查看代码第6行。

其实这段代码很罗嗦,不过考虑很周全,还去兼容了网景Netscape浏览器一番。其实这点我觉得没必要了。另外对于兼容模式的判断也不用这么麻烦了。有兴趣的同学可以去参看jQuery中这部分代码,很简洁。

参考jQuery中的代码,我修改成了目前我使用的版本。

/*!
 @FileOverview 获取鼠标坐标位置
 @Author liuqiqi || imqiqiboy@g mail.com || http://www.qiqiboy.com
 @date 2010/11/19
 @param ev mouse event
 @returnType object, {'x':posX, 'y':posY} 
*/
 
function getMousePoint(ev) {
    var x = y = 0,
        doc = document.documentElement,
        body = document.body;
    if(!ev) ev=window.event;
    if (window.pageYoffset) {//pageYoffset是Netscape特有
        x = window.pageXOffset;
        y = window.pageYOffset;
    }else{
        x = (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
        y = (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
    }
    x += ev.clientX;
    y += ev.clientY;
    return {'x' : x, 'y' : y};
}

以上代码经过我多次测试,目前也正使用在我博客中,暂未发现什么错误。如果你有测试出一些问题,欢迎及时指出。如对本文中所述有疑惑或者错误之处,也欢迎指教,相互提高。

至于DEMO嘛,我就不专门给出了。我在之前的博文我的读者墙展示(附所用JS代码)读者墙效果【二】- 头像自由拖拽两篇文章中都用到了鼠标坐标获取,demo可以参看这两篇文章中的例子。其中我的读者墙展示(附所用JS代码)一文中在获取页面宽度时也考虑了标准模式和怪癖模式,有兴趣可以在那篇文章的代码中找找相关代码所在。

上面两文中的demo地址分别是DEMO1DEMO2