如何最好地将 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。

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;
&lt;div id="test"&gt;&lt;/div&gt;

但是,这也会将toJSON fn 复制到新的 obj 中。因此我建议过滤非数字值:.filter(k =&gt; !isNaN(domRec[k])

轻量级

如果您只对xywidthheight 感兴趣,您可以使用DOMRect 而不是DOMRectReadOnly,并跳过.filter() 部分。

您可以根据这 4 个值计算 bottomright 的值,而 lefttop 只是 xy 的别名。

let domRec = someElement.getBoundingClientRect();
let domObj = Object.fromEntries(Array.from(Object.keys(DOMRect.prototype), k => [k, domRec[k]]));

【讨论】:

以上是关于如何最好地将 ClientRect / DomRect 转换为普通对象的主要内容,如果未能解决你的问题,请参考以下文章

如何最好地将字符串表示形式转换为 DbType?

如何最好地将 CSS3 过渡设置为无?

如何最好地将 mysql 数据实现为一行 javascript 代码?

如何最好地将方法传递给同一类的方法

如何在 Swift 中最好地将对象组织成月份和年份?

如何最好地将多个文本文件导入 SQLite FTS 虚拟表?