如何最好地将 ClientRect / DomRect 转换为普通对象
Posted
技术标签:
【中文标题】如何最好地将 ClientRect / DomRect 转换为普通对象【英文标题】:How best to convert a ClientRect / DomRect into a plain Object 【发布时间】:2016-09-09 18:19:38 【问题描述】:someElement.getBoundingClientRect()
的结果返回一个类型为ClientRect
(或DomRect
)的特殊对象
它的结构类似于top: 10, right: 20, bottom: 30, left: 10, width: 10
不幸的是,这个对象的行为与其他对象不太一样。
例如,在它上面使用Object.keys
会返回一个空数组(我认为是因为ClientRect
属性不是可枚举的
我发现了一种转换为普通对象的肮脏方式:
var obj =
for (key in rect)
obj[key] = rect[key]
我的问题是,有更好的方法吗?
【问题讨论】:
这是 soooo hacky,但Object.keys(document.body.getBoundingClientRect().__proto__)
似乎在 chrome 中工作。 :P
【参考方案1】:
我们不要把事情复杂化!
function getBoundingClientRect(element)
var rect = element.getBoundingClientRect();
return
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left,
width: rect.width,
height: rect.height,
x: rect.x,
y: rect.y
;
ES2015:
const getBoundingClientRect = element =>
const top, right, bottom, left, width, height, x, y = element.getBoundingClientRect()
return top, right, bottom, left, width, height, x, y
console.log(
getBoundingClientRect( document.body )
)
【讨论】:
这是一个 ES6 版本:const getBoundingClientRect = (element) => const top, right, bottom, left, width, height, x, y = element.getBoundingClientRect(); return top, right, bottom, left, width, height, x, y
【参考方案2】:
警告: 非标准行为(不适用于 Firefox < 62,包括 ESR 60 和可能的 Chrome 以外的其他浏览器)
var obj = el.getBoundingClientRect().toJSON();
【讨论】:
我认为你甚至不需要那里的Object.assign
。 .toJSON
返回一个合适的对象(Object.keys($0.getBoundingClientRect().toJSON())
有效)。你也每次都会得到一个新的对象! $0.getBoundingClientRect().toJSON() !== $0.getBoundingClientRect().toJSON()
请记住,toJSON
不适用于 firefox 的 rect 对象
抱歉,我不得不对答案投反对票,因为你没有提到它不支持 Firefox【参考方案3】:
这是我可以忍受的:
const persistRect = JSON.parse(JSON.stringify(someElement.getBoundingClientRect()))
【讨论】:
在 Edge 中不起作用。但是当 Edge 切换到 Chromium 时,这将得到解决。 作为一般规则,除非必要,否则应避免将数字转换为字符串(和返回)。字符串操作比处理数字要昂贵得多,因此如果必须多次执行此操作,性能会受到影响。 @MichaelJohansen,完全同意,好评论【参考方案4】:如果你使用 jQuery,你可以使用 extend 方法。
var obj = $.extend( , element.getBoundingClientRect());
【讨论】:
这可以而且确实有效,但与其他各种克隆方法相比,$.extend 的效率是出了名的低。见jsben.ch/#/bWfk9。正如 OP 所指出的,ClientRect 是一个特殊的对象,但大多数(所有?)其他技术都不能按预期工作,所以这里没有太多选择。【参考方案5】:函数式 ES6 变体:
const propValueSet = (prop) => (value) => (obj) => (...obj, [prop]: value)
const toObj = keys => obj => keys.reduce((o, k) => propValueSet(k)(obj[k])(o), )
const getBoundingClientRect = el => toObj(['top', 'right', 'bottom', 'left', 'width', 'height', 'x', 'y'])(el.getBoundingClientRect())
【讨论】:
【参考方案6】:你可以使用Object.fromEntries()
来创建你的obj。
DOMRectReadOnly
的原型
Obj vals 可以映射到 Array.from()
的第二个参数 (callbak fn)
let domRec = document.getElementById('test').getBoundingClientRect();
// Convert to plain Object
let domObj = Object.fromEntries(Array.from(Object.keys(DOMRectReadOnly.prototype).filter(k => !isNaN(domRec[k])), k => [k, domRec[k]]));
// Convert back to DOMRectReadOnly
let domRec2 = DOMRectReadOnly.fromRect(domObj);
console.log('DOMRectReadOnly', domRec);
console.log('Plain Object', domObj);
console.log('DOMRectReadOnly', domRec2);
#test
width: 200px;
height: 50px;
background-color: #f00;
<div id="test"></div>
但是,这也会将toJSON
fn 复制到新的 obj 中。因此我建议过滤非数字值:.filter(k => !isNaN(domRec[k])
。
轻量级
如果您只对x
、y
、width
、height
感兴趣,您可以使用DOMRect
而不是DOMRectReadOnly
,并跳过.filter()
部分。
您可以根据这 4 个值计算
bottom
、right
的值,而left
、top
只是x
、y
的别名。
let domRec = someElement.getBoundingClientRect();
let domObj = Object.fromEntries(Array.from(Object.keys(DOMRect.prototype), k => [k, domRec[k]]));
【讨论】:
以上是关于如何最好地将 ClientRect / DomRect 转换为普通对象的主要内容,如果未能解决你的问题,请参考以下文章