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