技術(shù)分享 如何獲取Dom元素的X/Y坐標(biāo)
當(dāng)今已有很多的JS框架封裝了獲取Dom元素的坐標(biāo)的方法,我們可以直接使用,而這里我更多的是希望透過這些方法,看到原始的獲取坐標(biāo)方式以及如何處理跨瀏覽器問題。
獲取Dom元素的X/Y坐標(biāo)
現(xiàn)在Web頁面的交互方式越來越多樣化,其中拖放頁面元素也是一種很常見的操作。在這類操作當(dāng)中有兩個(gè)主要問題需要解決,一個(gè)是事件的注冊(cè)方式,一般處理拖放元素的事件順序是:捕獲鼠標(biāo)正鍵按下——注冊(cè)鼠標(biāo)移動(dòng)事件——捕獲鼠標(biāo)正鍵抬起——注銷鼠標(biāo)移動(dòng)事件;另一個(gè)問題就是拖放元素的位置,即X/Y坐標(biāo)。這里我主要來講述后面的問題:如何獲取一個(gè)Dom元素的坐標(biāo)。
當(dāng)今已有很多的JS框架封裝了獲取Dom元素的坐標(biāo)的方法,我們可以直接使用,而這里我更多的是希望透過這些方法,看到原始的獲取坐標(biāo)方式以及如何處理跨瀏覽器問題。
首先認(rèn)識(shí)一下getBoundingClientRect方法,標(biāo)準(zhǔn)語法為:
- oRect=object.getBoundingClientRect();
Dom節(jié)點(diǎn)調(diào)用該方法可返回一個(gè)ClientRect類型的對(duì)象,該對(duì)象有四個(gè)屬性值:top、left、right、bottom,表示了該節(jié)點(diǎn)相對(duì)于可視瀏覽器可視區(qū)域的左上角位置的坐標(biāo),看圖就能好理解:
坐標(biāo)說明圖1
補(bǔ)充一下,如果當(dāng)前的元素已經(jīng)超出可視區(qū)域,依然按照可視區(qū)域的左上角位置的坐標(biāo)來計(jì)算,如圖2
坐標(biāo)說明圖2
這樣就可以簡單的獲取Dom元素在可視區(qū)里的X/Y坐標(biāo)(通過left和top屬性)。最后在計(jì)算上頁面滾動(dòng)條的偏移量就可以計(jì)算出元素在整個(gè)頁面中的X/Y坐標(biāo)了。頁面滾動(dòng)偏移量的計(jì)算在不同瀏覽器下有所不同,不過我們可以借鑒YUI里面方法,設(shè)計(jì)一個(gè)通用的方式:
- scrollLeft=Math.max(document.documentElement.scrollLeft,
- document.body.scrollLeft);
- scrollTop=Math.max(document.documentElement.scrollTop,
- document.body.scrollTop);
在W3C標(biāo)準(zhǔn)下document.body.scrollTop和document.body.scrollLeft都為0,所以采用了上面兼容的方式獲取頁面滾動(dòng)條的偏移量。#p#
◆將ClientRect對(duì)象的left、top屬性分別加上scrollLeft和scrollTop,就能獲取Dom元素的X/Y坐標(biāo)了。但對(duì)于IE8之前的IE版本在很多情況,下面要對(duì)這個(gè)值進(jìn)行一些調(diào)整,有三種情況,下面來分別看一下:
1.IE6的標(biāo)準(zhǔn)模式下不需要調(diào)整;
2.所有怪異模式下為取document的當(dāng)前borderLeftWidth和borderTopWidth值做調(diào)整,分別加在X/Y坐標(biāo)值上;
3.其他的情況都分別在X/Y坐標(biāo)值上加上2;
這樣就可以獲取最后準(zhǔn)確的X/Y坐標(biāo)了。
如果所有的瀏覽器都能如此就好了,可惜有些瀏覽器(FF2、Safari)不支持getBoundingClientRect方法。需要通過一級(jí)級(jí)查找和計(jì)算offsetParent來獲取X/Y坐標(biāo)值。這里首先介紹什么是元素的offsetParent屬性。
◆offsetParent屬性是距離調(diào)用offsetParent的元素最近的(在包含層次中最靠近的),并且是已進(jìn)行過CSS定位的與容器元素。首先說明一下CSS定位,是指對(duì)元素設(shè)置position屬性為absolute、relative或fixed(IE6除外),還有一個(gè)問題是元素在table元素中時(shí)會(huì)有不同的情況。下面是我的一些歸納,不全之處望大家指出:
1.元素不在table元素中,且元素及其所有上級(jí)元素都未進(jìn)行CSS定位時(shí),這個(gè)元素的offsetParent屬性為根元素(Body);
2.元素本身沒進(jìn)行CSS定位,而出現(xiàn)在table中或有上級(jí)元素進(jìn)行了CSS定位,那么當(dāng)向上先達(dá)到TD元素時(shí)該元素的offsetParent屬性為TD元素;當(dāng)向上先達(dá)到進(jìn)行了CSS定位的上級(jí)元素時(shí)該元素的offsetParent屬性為該上級(jí)元素;
3.無論元素在不在table中,只要元素本身進(jìn)行了CSS定位,有上級(jí)元素進(jìn)行了CSS定位的則元素的offsetParent屬性為該上級(jí)元素,沒有上級(jí)素進(jìn)行了CSS定位的則元素的offsetParent屬性為根元素;
知道了offsetParent屬性的含義,就可以通過offsetParent屬性來一級(jí)級(jí)的計(jì)算X/Y坐標(biāo)了。一種比較簡單的while循環(huán):
- varnode;/*求坐標(biāo)的元素*/
- varxy=[];/*保存XY坐標(biāo)*/
- while((nodenode=node.offsetParent)){
- xy[0]+=node.offsetLeft;
- xy[1]+=node.offsetTop];
- }
◆通過這一個(gè)循環(huán)就能累計(jì)元素每級(jí)offsetParent屬性元素的偏移量,但這個(gè)偏移量在累加的過程中沒有計(jì)算每級(jí)父元素有滾動(dòng)條的情況,最后還要同getBoundingClientRect方法一樣加上頁面滾動(dòng)值(這里scrollLeft和scrollTop)。現(xiàn)在先來累計(jì)計(jì)算元素每上級(jí)元素的滾動(dòng)條情況,首先判斷元素本身是不是設(shè)置了position為fixed:
1.設(shè)置了則不用計(jì)算每上級(jí)元素的滾動(dòng)條情況,但需要對(duì)Opera和其他瀏覽器做區(qū)分,Opera瀏覽器減去scrollLeft和scrollTopxy[0]-=scrollLeft;xy[1]-=scrollTop;,其他情況是加上scrollLeft和scrollTop。
- xy[0]+=scrollLeft;xy[1]+=scrollTop;
2.未設(shè)置時(shí)就需要累計(jì)計(jì)算元素每上級(jí)元素的滾動(dòng)條,通過一個(gè)循環(huán)里累加:
- while((nodenode=node.parentNode)&&node.tagName){
- scrollTop=node.scrollTop;
- scrollLeft=node.scrllLeft;
- if(scrollTop||scrollLeft){
- xy[0]-=scrollLeft;
- xy[1]-=scrollTop;
- }
- }
最后機(jī)上頁面滾動(dòng)值
- xy[0]+=scrollLeft;xy[1]+=scrollTop;
這樣最后就可以在不支持getBoundingClientRect方法的瀏覽器下獲取元素的X/Y坐標(biāo)了。
總結(jié):
如果瀏覽器支持getBoundingClientRect方法,通過該方法再加上頁面滾動(dòng)條的偏移就能獲取元素的X/Y了(不同瀏覽器需要微調(diào)),如果不支持getBoundingClientRect方法,則需要通過循環(huán)該元素的每級(jí)offsetParent屬性來累計(jì)偏移量,再通過每個(gè)父級(jí)元素的滾動(dòng)條來調(diào)整,最后再加上頁面滾動(dòng)條的偏移來獲取元素的X/Y坐標(biāo)。獲取X/Y坐標(biāo)的方式還有很多,可能不盡相同,我這里主要是基于YUI里面的思想和方法。
【編輯推薦】
- JQuery創(chuàng)建DOM元素方法解析
- W3C DOM模型用法詳解
- JavaScript獲取HTML DOM節(jié)點(diǎn)元素詳解
- 深入了解JavaScript HTML DOM對(duì)象
- 解析HTML DOM Checkbox對(duì)象的屬性和方法