樣式操做模塊可用於管理DOM元素的樣式、座標和尺寸,本節講解一下座標這一塊。css
對於座標來講,jQuery提供了一個offset方法用於獲取第一個匹配元素的座標或者設置全部匹配元素的座標,還有offsetParent獲取最近的定位祖先元素,position用於獲取獲取第一個匹配元素相對於最近定位祖先元素的座標,以下:html
也就是說若是不傳遞參數則獲取第一個匹配元素的文檔座標,若是傳遞了參數(含有left、top的對象),則設置每一個匹配元素的座標.node
舉個栗子:jquery
writer by:大沙漠 QQ:22969969瀏覽器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script> <style> *{margin:0;padding:0;} div{margin:20px;width: 200px;height: 180px;position: relative;padding-top: 20px;background: #c38;} h1{margin:10px;color: #333;} </style> </head> <body> <div> <h1>123</h1> </div> <button id="b1">獲取h1元素的文檔座標</button><br/> <button id="b2">獲取h1元素最近的定位祖先元素</button><br/> <button id="b3">獲取h1元素離最近定位祖先元素的座標</button><br/> <button id="b4">設置h1元素的文檔座標</button> <p></p> <script> $('#b1').click(()=>{ //獲取h1元素的文檔座標 console.log( $('h1').offset() ) }) $('#b2').click(()=>{ //獲取h1元素最近的定位祖先元素,也就是div元素 console.log( $('h1').offsetParent() ) }) $('#b3').click(()=>{ //獲取h1元素離最近定位祖先元素的座標,也就是相對div元素的座標 console.log( $('h1').position() ) }) $('#b4').click(()=>{ //設置h1元素的文檔座標,相對於整個文檔的 $('h1').offset({top:'10',left:'10'}) }) </script> </body> </html>
咱們添加了一個div和一個h1,另外,div設置了relation屬性,div內的h1相對於div設置了margin屬性,另外定義了四個按鈕,分別用於獲取h1的文檔座標、獲取h1最近的定位組件元素、獲取h1元素離最近定位祖先元素的座標和修改h1元素的座標,效果以下:函數
點擊按鈕1獲取的文檔座標是相對於整個文檔的,按鈕2獲取的定位元素也就是div元素,按鈕3獲取的是相對於div的偏移座標,按鈕4設置h1的文檔座標,此時h1元素上會新增一個position:relative;屬性,jQuery通過計算在h1上設置對應的偏移地址,以下:源碼分析
源碼分析this
offset的實現是經過getBoundingClientRect整個原生API來實現的,以下:spa
if ( "getBoundingClientRect" in document.documentElement ) { //原生方法getBoundingClientRect()返回元素的窗口座標,返回值含有4個整型屬性:top、left、right、bottom jQuery.fn.offset = function( options ) { var elem = this[0], box; //elem指向第一個匹配元素 if ( options ) { //若是傳入了options參數 return this.each(function( i ) { //則遍歷匹配元素集合 jQuery.offset.setOffset( this, options, i ); //並在每一個元素上調用jQuery.offset.setOffset(elem,options,i)設置文檔座標。 }); } if ( !elem || !elem.ownerDocument ) { //若是沒有匹配元素或匹配元素不在文檔中, return null; //則不作任何處理,當即返回null } if ( elem === elem.ownerDocument.body ) { //若是elem是body元素 return jQuery.offset.bodyOffset( elem ); //則調用jQuery.offset.bodyOffset(body)返回body元素的文檔座標 } try { box = elem.getBoundingClientRect(); //調用原生方法getBoundingClientRect()返回元素的窗口座標,用try-catch語句來'吞掉'IE可能拋出的異常。 } catch(e) {} var doc = elem.ownerDocument, //指向document對象 docElem = doc.documentElement; //指向html元素 // Make sure we're not dealing with a disconnected DOM node if ( !box || !jQuery.contains( docElem, elem ) ) { return box ? { top: box.top, left: box.left } : { top: 0, left: 0 }; } var body = doc.body, win = getWindow(doc), //調用getwindow(doc)獲取window對象 clientTop = docElem.clientTop || body.clientTop || 0, //clientTop是html或body元素的上邊框厚度 clientLeft = docElem.clientLeft || body.clientLeft || 0, //clientLeft是html或body元素的左邊框厚度 scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, //滾動條的垂直偏移 scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, //滾動條的水平偏移 top = box.top + scrollTop - clientTop, //第一個元素的文檔上座標 left = box.left + scrollLeft - clientLeft; //第一個元素的文檔左座標 return { top: top, left: left }; //返回第一個元素的文檔座標 }; } else { //不支持原生方法getBoundingClientRect()時,如今大多數瀏覽器已經支持了,因此這裏不討論。 }
上面是獲取文檔座標的,對於設置文檔座標是經過jQuery.offset.setOffset()來實現的,也就是上面標紅的地方,jQuery.offset.setOffset的實現以下:3d
jQuery.offset = { setOffset: function( elem, options, i ) { //設置單個元素的文檔座標。 var position = jQuery.css( elem, "position" ); // set position first, in-case top/left are set even on static elem if ( position === "static" ) { //若是該元素的position屬性等於static的 elem.style.position = "relative"; //則修正爲relative,使得設置的樣式left、top可以生效。 } var curElem = jQuery( elem ), //當前元素的jQuery對象 curOffset = curElem.offset(), //當前元素的文檔座標 curCSSTop = jQuery.css( elem, "top" ), //獲取 當前元素的計算樣式top,帶有單位 curCSSLeft = jQuery.css( elem, "left" ), //獲取 當前元素的計算樣式left,帶有單位 calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, //若是當前屬性樣式position是absolute或fixed,而且樣式left或top是auto,則設置calculatePosition爲true。 props = {}, curPosition = {}, curTop, curLeft; // need to be able to calculate position if eit her top or left is auto and position is either absolute or fixed if ( calculatePosition ) { //若是calculatePosition爲true,修正curTop和curLeft座標。 curPosition = curElem.position(); //經過.position()獲取當前元素相對於最近定位祖先元素或body元素的座標 curTop = curPosition.top; //獲取組件元素的top curLeft = curPosition.left; //獲取組件的left } else { //將curCSSTop、curCSSLeft解析爲數值,以便參與計算。 curTop = parseFloat( curCSSTop ) || 0; curLeft = parseFloat( curCSSLeft ) || 0; } if ( jQuery.isFunction( options ) ) { //若是options是函數 options = options.call( elem, i, curOffset ); //則執行該函數,取其返回值做爲要設置的文檔座標。 } if ( options.top != null ) { //計算最終的內聯樣式top props.top = ( options.top - curOffset.top ) + curTop; } if ( options.left != null ) { //計算最終的內聯樣式left props.left = ( options.left - curOffset.left ) + curLeft; } if ( "using" in options ) { //若是參數options中有回調函數using,則調用 options.using.call( elem, props ); } else { curElem.css( props ); //不然調用.css(name,value)方法設置最終的內聯樣式top、left。 } } };
從源碼裏能夠看到,若是元素有設置了absolute則會獲取祖先元素的的偏移,而後通過一些運算獲取最後的值,最後經過css()修改樣式來實現最後的定位。
對於offsetParent來講,它的實現以下:
jQuery.fn.extend({ offsetParent: function() { //獲取最近的定位祖先元素,就是CSS position屬性被設置爲relative、absolute 或 fixed 的元素,返回一個jQuery對象,包含全部祖先元素。 return this.map(function() { var offsetParent = this.offsetParent || document.body; //offsetParent是最近的定位祖先元素;若是沒有找到,則返回body元素。 while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { //若是找到的祖先元素的樣式position是static,則繼續沿着樹向上找,直到遇到body元素或html元素爲止。 offsetParent = offsetParent.offsetParent; } return offsetParent; //返回的定位組選元素將被添加到新構造的jQuery對象上。 }); } })
offsetParent是原生的DOM操做的API,用於獲取第一個祖定位元素,全部瀏覽器都支持的,這裏就是把它進行了一個封裝而已。
position()比較簡單,經過offsetParent()獲取第一個祖先節點的文檔座標,而後用組件節點的top減去當前節點的top,組件節點的left減去當前節點的left,返回一個相對位置,僅此而已,代碼就不貼了。