前一篇讲了 offset() 方法,它能拿到元素相对整个文档的坐标。但项目开发中还有一种很常见的需求:已经有一个元素了,想在它旁边再放一个元素,这两个元素得在同一个容器里。这时候用 offset() 就不太顺手了,因为文档坐标会受页面滚动和外部布局影响。jQuery 提供了另一个专门的方法——position()。
我当年写下拉菜单组件时,菜单的定位用 offset() 老是对不齐,后来换成 position() 配合父容器做参照,问题立马解决。这两个方法表面上看都是拿坐标,但参照系不同,用错场景结果不一样。
position() 的核心用途
position() 方法只做一件事,但做得很专:
获取匹配元素中第一个元素相对于其父元素(offset parent)的当前坐标。
返回一个对象,包含两个属性:
-
top:元素顶部距离父元素顶部的像素值 -
left:元素左侧距离父元素左侧的像素值
和 offset() 不同,position() 只能读取,不能设置。你没法用 position({top: 100, left: 200}) 这种写法来改变元素位置。这是很多初学者容易搞错的地方——看到 offset() 既能读又能写,就以为 position() 也一样,结果代码报错。
offset parent 是什么
position() 说的“父元素”,不是随便哪个父级标签,而是有严格定义的 offset parent(定位父元素)。浏览器找 offset parent 的规则是:
-
向上查找最近的、CSS
position属性为relative、absolute或fixed的祖先元素。 -
如果找不到,就默认用
<body>作为参照。
这意味着如果元素的父级全是默认的 position: static,position() 返回的坐标就是相对 body 的,和 offset() 的数值可能很接近甚至一样。实际项目里,为了精确控制子元素的定位范围,通常会给父容器设置 position: relative;,让它成为一个“定位锚点”。
语法
只有一种形式,非常简单:
$(selector).position()
不接收任何参数,也不能传递坐标对象。它只返回 {top: number, left: number}。如果选中了多个元素,只返回第一个的坐标。
实战示例:点击段落获取相对父级的位置
<div style="position: relative; padding: 30px; border: 1px solid #ccc;">
<p id="inner-p">这段文字在相对定位的容器里</p>
</div>
<button id="get-position">获取段落的相对坐标</button>
$(document).ready(function(){
$("#get-position").click(function(){
var pos = $("#inner-p").position();
alert("相对父容器的 Top: " + pos.top + "px\n相对父容器的 Left: " + pos.left + "px");
});
});
这里 <div> 设置了 position: relative,所以 position() 返回的是 <p> 相对于这个 <div> 的坐标。如果去掉 <div> 的 position 属性,数值就会变成相对 body,结果往往就不一样了。
实战示例:点击色块获取各自相对坐标
这个例子演示多个元素在同一父容器里,点击哪个就显示哪个相对父级的坐标:
<p>点击任意色块获取其相对父容器的坐标:</p>
<span id="left-result"></span>
<span id="top-result"></span>
<div class="box-container" style="position: relative; padding: 20px; border: 1px dashed #999; overflow: hidden;">
<div class="box" style="background-color:#ffd700; width:60px; height:60px; margin:5px; float:left;"></div>
<div class="box" style="background-color:#030303; width:60px; height:60px; margin:5px; float:left;"></div>
<div class="box" style="background-color:#473c8b; width:60px; height:60px; margin:5px; float:left;"></div>
<div class="box" style="background-color:#ee82ee; width:60px; height:60px; margin:5px; float:left;"></div>
</div>
$(document).ready(function() {
$(".box").click(function () {
var pos = $(this).position();
$("#left-result").html("left 相对位置: <span>" + pos.left + "px</span>");
$("#top-result").html("top 相对位置: <span>" + pos.top + "px</span>");
});
});
每个色块都是同一个父容器 .box-container 的子元素,父容器声明了 position: relative,所以所有色块的坐标都是相对这个框来算的。缩放窗口、改变布局,色块之间的相对关系不变,这就是用 position() 的好处。
position() 和 offset() 的核心区别
| 对比维度 | position() | offset() |
|---|---|---|
| 参照对象 | 最近的定位祖先元素(offset parent) | 整个文档(document) |
| 能否设置 | 只能读取,不能设置 | 既能读取,也能设置 |
| 受滚动影响 | 滚动父容器时一般不变(相对关系固定) | 页面滚动时数值会变(绝对位置改变) |
| 典型场景 | 同一容器内元素间的相对定位 | 全局弹窗、固定在页面某个绝对位置 |
本节课程知识要点:做元素跟随效果时(比如下拉菜单跟随按钮、气泡提示跟随图标),如果触发元素和被定位元素在同一个父容器里,优先用 position() 获取参照坐标,然后用 CSS 或 offset() 设置被定位元素的位置。这样做布局更可控,不容易受页面其他区域的影响。
个人经验:什么时候用 position(),什么时候用 offset()
经过多个项目积累,我了一条判断标准:
-
要“贴”在某个元素旁边,而且它们共享一个定位容器,就用
position()读取坐标,这样后续计算相对偏移量更直观。 -
要固定在页面某个绝对位置,比如全屏遮罩、居中弹窗,用
offset()或直接在 CSS 里写死坐标更省事。 -
如果父容器没有设置定位,
position()会自动退回到 body 参照,这时它的值和offset()可能差别不大。但依赖这种“无定位父级”的默认行为不靠谱,页面结构一变化就可能出问题。建议给所有需要做内部定位的容器显式加上position: relative;。
position() 返回的坐标不包括元素的 margin,因为 margin 在盒模型里不属于元素自身占据的可视区域。而 offset() 是把 margin 也算进去的。这个细微差别在做高精度对齐时要注意,差几个像素视觉上就很明显。