计算元素在滚动容器中的可见比例时,应优先使用 getBoundingClientRect(),而非 offsetLeft,因为后者受 offsetParent 影响,参照基准不固定。
问题:offsetLeft 的 offsetParent 陷阱
element.offsetLeft 返回元素相对于其 offsetParent 的距离。offsetParent 是”最近的有 position 属性的祖先元素”,而不是滚动容器本身。
<div class="page-layout"> ← position: relative → offsetParent!
<div class="carousel-wrapper"> ← overflow: hidden,无 position
<div class="carousel-track"> ← overflow-x: auto,无 position
<a class="card"> ← offsetLeft 相对 .page-layout,而非 track
若 track 没有 position: relative,card 的 offsetLeft 会包含 page-layout 的 padding/margin,导致可见比例计算错误。
解法:getBoundingClientRect 在视口坐标系计算
getBoundingClientRect() 返回元素相对于视口的坐标,container 和 child 在同一坐标系内,直接相减即可得到准确的重叠区域:
function getVisibleRatio(container, child) {
var cr = container.getBoundingClientRect();
var er = child.getBoundingClientRect();
var visibleLeft = Math.max(cr.left, er.left);
var visibleRight = Math.min(cr.right, er.right);
var visible = Math.max(0, visibleRight - visibleLeft);
return er.width > 0 ? visible / er.width : 0;
}
修复方案(offsetLeft 场景)
若必须用 offsetLeft,需给滚动容器加 position: relative,使其成为 offsetParent:
.carousel-track {
position: relative; /* 让子元素的 offsetParent 指向 track */
overflow-x: auto;
}
参考
- HAT-217 SlipBox 轮播圆点指示器,首次用
offsetLeft时点显示错位,改用getBoundingClientRect修复