共计 3679 个字符,预计需要花费 10 分钟才能阅读完成。
position()
作用:
返回被选元素相对于 父元素(parent)的偏移坐标
使用:
直接调用 $().position()
即可,该方法没有 arguments(参数对象)
<body> | |
<script src="jQuery.js"></script> | |
<p id="pTwo"> 这是 divTwo</p> | |
<script> | |
$("#pTwo").position() //{top: 0, left: 8} | |
</script> | |
</body> |
源码:
// 返回被选元素相对于父元素(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" )
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()
,即获取某个元素相对于视窗的位置。
注意:
① getBoundingClientRect() 计算的是目标元素的 border 的位置(左上角),是不包括 margin 的
② 如果不加上 margin 的话(代码是通过 减去
,来算上 margin 的),是不准确的,看下图
所以源码最后会:
- jQuery.css(elem, "marginTop", true) | |
- jQuery.css(elem, "marginLeft", true) |
(2)jQuery.css(elem, "width", true)
true
的作用是返回该属性的数字,而不带单位 px
(3)定位父元素存在,并且不等于目标元素,并且定位元素类型是 “ 元素类型 ” 的话
if (offsetParent && offsetParent !== elem && offsetParent.nodeType === 1)
是要减去 border
属性的值的
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:
https://github.com/AttackXiao…
(完)