getBoundingClientRect()返回x值为0与sectionRef取值不一致问题
问题现象
变量sectionX的取值本应与第二条console.log的输出值一致。打印props和sectionRef.current时,获取到的是相同的DOM元素,仅sectionRef.current的输出后缀带有(flex)标识。
问题相关代码:
const horizontalScroll = (props) => { var container = document.getElementById(styles["a-text__box"]); var sectionX = props.getBoundingClientRect().x; console.log(sectionX); console.log(sectionRef.current.getBoundingClientRect().x); console.log(props); console.log(sectionRef.current); // container.scrollTo({ // top: 0, // left: sectionX, // behavior: "smooth", // }); };
控制台输出:
0 1193.28125 <div>...</div> <div>...</div>(flex)
实际异常表现:对入参props调用getBoundingClientRect()获取的元素x坐标值为0,对sectionRef.current调用相同方法获取的x坐标值为1193.28125,二者取值存在明显差异,导致后续无法正确设置容器滚动位置。
产生原因
核心是两次读取DOM布局属性的时机不一致,控制台打印的DOM内容不能作为判断依据:
- 浏览器控制台打印DOM元素是懒解析的,你在控制台看到的DOM结构是手动点开时的最终状态,不是代码执行到
console.log那一行的瞬时状态,所以两个打印看起来DOM完全一致是正常的。 - 打印结果里的
(flex)是代码执行瞬间的状态标记:说明读取props对应的DOM时,元素还没完成flex布局计算,甚至还没挂载到文档流的最终位置,此时getBoundingClientRect()只能拿到未布局时的初始坐标0;等执行到读取sectionRef.current的逻辑时,flex布局已经计算完成,元素落到了最终位置,所以能拿到正确的坐标值。 - 如果是React环境,直接把DOM作为参数在渲染相关回调里传递,拿到的很可能是布局提交前的DOM快照,不是页面上实时渲染的真实DOM,哪怕指向同一个节点,快照里的布局属性也是过时的。
修复方法
- 所有DOM布局属性读取,统一使用ref绑定的实时DOM引用,直接取
sectionRef.current.getBoundingClientRect().x作为滚动定位的参数,弃用从入参props上读取的sectionX值。 - 涉及DOM布局读取、滚动触发的逻辑,放到
useLayoutEffect钩子中执行,该钩子触发时机在浏览器完成所有DOM布局计算之后、绘制之前,能保证拿到的坐标值完全准确,不会出现初始0值的问题。 - 调试DOM属性时不要直接打印整个DOM节点,改成打印你需要的具体属性值,避免控制台懒解析带来的误判。
内容的提问来源于stack exchange,提问作者user19144150
相关产品推荐
相关产品推荐

