jQuery 源碼解析(二十七) 樣式操做模塊 座標詳解

樣式操做模塊可用於管理DOM元素的樣式、座標和尺寸,本節講解一下座標這一塊。css

對於座標來講,jQuery提供了一個offset方法用於獲取第一個匹配元素的座標或者設置全部匹配元素的座標,還有offsetParent獲取最近的定位祖先元素,position用於獲取獲取第一個匹配元素相對於最近定位祖先元素的座標,以下:html

  • offset(options)                 ;返回匹配元素集合中的一個元素的文檔座標,或者設置每一個元素的文檔座標,;不能帶單位,默認是px,有兩種使用方法:
    • offset()                            ;返回第一個匹配元素的文檔座標
    • offset(val)                        ;設置每一個匹配的元素的文檔座標
  • offsetParent(options)      ;獲取最近的定位祖先元素
  • position()                        ;獲取第一個匹配元素相對於最近定位祖先元素的座標    ;若是是body元素則返回{ top: 0, left: 0 }。

也就是說若是不傳遞參數則獲取第一個匹配元素的文檔座標,若是傳遞了參數(含有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,返回一個相對位置,僅此而已,代碼就不貼了。

相關文章
相關標籤/搜索