Element.offsetWidth|offsetHeight 竟然偷偷摸摸四舍五入?

682 阅读1分钟

背景

我参与的 React 项目中,对于表格组件,有大数据量展示需求(不分页)。经过多方比对,最终选用了 Audodesk 公司的开源项目 BaseTable。

github.com/Autodesk/re…

image.png

不仅如此,在项目中还使用了 BaseTable 的 Auto Resize 特性,因为表格展示在 Flex 布局容器中,需要表格组件有自动适应容器尺寸变化的能力。

image.png

但是在项目测试过程中发现,在某些情况下(例如:调整浏览器缩放比例、调整浏览器尺寸) BaseTable 所在的 Flex 容器会出现滚动条(注:容器上设置有 overflow: auto

例如:

1672983568269.png

image.png

发现

经过仔细研究,通过 F12 查看发现:Flex 容器出滚动条的那些情况,其内部表格的尺寸要比容器尺寸大,但并没有大很多,只是大了不到 1px

image.png

然后我去看了BaseTable 的 AutoResizer 的实现,发现:AutoResizer 组件动态计算的 widthheight 是通过获取其父元素的 offsetWidthoffsetHeight 得到的。

image.png

github.com/bvaughn/rea…

难道是坑在了 offsetWidth、offsetHeight 上?

验证

写了个极简的例子验证了一下:

  1. 容器 outer:宽度 300.6px,高度 200.6px,overflow auto(目的是验证四舍五入与滚动条问题)
  2. 点击按钮,将读取容器 outeroffsetWidthoffsetHeight 值,作为子组件 innerwidthheight
  3. 此时 outer 没有出现滚动条
  4. 缩放浏览器,会发现 outer 出现滚动条。
  5. 且即便回到 100% 的缩放比例,outer 滚动条也不会消失

hoho.gif

image.png

<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title></title>
</head>

<body>
	<button onclick="fn()">fn</button>

	<div id="outer" style="width: 300.6px; height: 200.6px; 
							background-color: orange; overflow: auto;">
		<div id="inner" style="background-color: rgba(0, 255, 0, 0.2)">
		</div>
	</div>

	<script type="text/javascript">
		function fn() {
			const outer = document.getElementById("outer");

			const inner = document.getElementById("inner");
			inner.style.width = outer.offsetWidth + "px";
			inner.style.height = outer.offsetHeight + "px";
		}
	</script>
</body>

</html>

官方解释

  1. offsetWidthoffsetHeight 确实会进行四舍五入
  2. 想要得到带小数的原始值,应该使用 getBoundingClientRect() 获取

image.png

解决方案

  • 修改开源组件,将 offsetWidthoffsetHeight 修改为 getBoundingClientRect().widthgetBoundingClientRect().height,并用 Math.floor 向下取整

image.png