position()
做用:
返回被選元素相對於父元素(parent)的偏移座標javascript
使用:
直接調用$().position()
便可,該方法沒有 arguments(參數對象)css
<body>
<script src="jQuery.js"></script>
<p id="pTwo">這是divTwo</p>
<script>
$("#pTwo").position() //{top: 0, left: 8}
</script>
</body>
複製代碼
源碼:html
// 返回被選元素相對於父元素(parent)的偏移座標
// 能夠理解成被選元素設置爲absolute,
// 而後設置left、top的值就是相對於父元素的偏移座標
// 源碼10571行
// position() relates an element's margin box to its offset parent's padding box
// This corresponds to the behavior of CSS absolute positioning
position: function() {
// 若是DOM元素不存在,直接返回
if ( !this[ 0 ] ) {
return;
}
var offsetParent, offset, doc,
elem = this[ 0 ],
parentOffset = { top: 0, left: 0 };
// position:fixed elements are offset from the viewport, which itself always has zero offset
// position:fixed的元素,是相對於瀏覽器窗口進行定位的,
// 因此它的偏移就是getBoundingClientRect(),即獲取某個元素相對於視窗的位置
if ( jQuery.css( elem, "position" ) === "fixed" ) {
// Assume position:fixed implies availability of getBoundingClientRect
offset = elem.getBoundingClientRect();
}
// 除去position是fixed的狀況
else {
// 獲取被選元素相對於文檔(document)的偏移座標
offset = this.offset();
// Account for the *real* offset parent, which can be the document or its root element
// when a statically positioned element is identified
doc = elem.ownerDocument;
//定位目標元素的父元素(position不爲static的元素)
offsetParent = elem.offsetParent || doc.documentElement;
// 若是父元素是<body>/<html>的話,將父元素從新定位爲它們的父元素
// body的父元素是html,html的父元素是document
while ( offsetParent &&
( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
jQuery.css( offsetParent, "position" ) === "static" ) {
offsetParent = offsetParent.parentNode;
}
// 若是定位父元素存在,而且不等於目標元素,而且定位元素類型是 "元素類型"
if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
// Incorporate borders into its offset, since they are outside its content origin
parentOffset = jQuery( offsetParent ).offset();
// 這兩行代碼的意思是父元素的offset()要從padding算起,不包括border
// 因此須要去掉border
// jQuery.css( element, "borderTopWidth", true )的 true 表示返回數字,而不帶單位 px
parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
}
}
// Subtract parent offsets and element margins
// 能夠看出,$().position()的本質是目標元素的offset()減去父元素的offset(),同時還要算上目標元素的margin,由於盒子模型(關鍵)。
//(注意:offset()的本質是getBoundingClientRect()的top、left + pageYOffset、pageXOffset)
return {
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
};
},
複製代碼
解析:
總體上看,是一個 if(...fixed) { } esle { }
語句
(1)if ( jQuery.css( elem, "position" ) === "fixed" )
java
if ( jQuery.css( elem, "position" ) === "fixed" ) {
// Assume position:fixed implies availability of getBoundingClientRect
offset = elem.getBoundingClientRect();
}
return {
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
};
複製代碼
因爲position:fixed
的元素,是相對於瀏覽器窗口進行定位的,因此它的偏移就是getBoundingClientRect()
,即獲取某個元素相對於視窗的位置。node
注意:
① getBoundingClientRect() 計算的是目標元素的border的位置(左上角),是不包括margin的
② 若是不加上margin的話(代碼是經過減去
,來算上margin的),是不許確的,看下圖git
因此源碼最後會:github
- jQuery.css( elem, "marginTop", true )
- jQuery.css( elem, "marginLeft", true )
複製代碼
(2)jQuery.css( elem, "width", true )
true
的做用是返回該屬性的數字,而不帶單位 px瀏覽器
(3)定位父元素存在,而且不等於目標元素,而且定位元素類型是 "元素類型"的話bash
if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 )
複製代碼
是要減去border
屬性的值的app
parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
複製代碼
爲啥?舉個例子:
let p=document.querySelector("#pTwo")
console.log(p.getBoundingClientRect(),'pTwo11'); //x:8,y:16
複製代碼
設置 borderLeftWidth 爲 8 像素
let p=document.querySelector("#pTwo")
p.style.borderLeftWidth='8px'
console.log(p.getBoundingClientRect(),'pTwo11'); //x:8,y:16
複製代碼
能夠看到getBoundingClientRect()
指定座標是到border
上的,這是不許確的,由於在裏面的子元素的位置也會受父元素的border
影響,因此父元素的座標須要越過border
綜上:
能夠看出,$().position()
的本質是目標元素的 offset() 減去父元素的 offset(),同時還要算上目標元素的 margin,算上父元素的border。
(注意:offset()的本質是getBoundingClientRect()的top、left + pageYOffset、pageXOffset)
Github:
github.com/AttackXiaoJ…
(完)