26

The result of someElement.getBoundingClientRect() returns a special object of type ClientRect (or DomRect apparently)

It is structured like {top: 10, right: 20, bottom: 30, left: 10, width: 10}

Unfortunately, this object does not behave quite like other objects.

For example, using Object.keys on it returns an empty array (I think because ClientRect properties are not enumerable

I found something of a dirty way to convert to a plain object:

var obj = {}
for (key in rect) {
  obj[key] = rect[key]
}

My question is, is there a better way?

Zach Lysobey
  • 14,959
  • 20
  • 95
  • 149

6 Answers6

32

Let's not overcomplicate things!

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 )
)
Community
  • 1
  • 1
Bartosz Gościński
  • 1,468
  • 1
  • 16
  • 28
  • 10
    Here's an ES6 version: ```const getBoundingClientRect = (element) => { const {top, right, bottom, left, width, height, x, y} = element.getBoundingClientRect(); return {top, right, bottom, left, width, height, x, y} } ``` – aboutaaron Jul 14 '17 at 15:28
6

Warning: non-standard behavior (doesn't work in Firefox < 62, including ESR 60 and possibly other browsers other than Chrome)

var obj = el.getBoundingClientRect().toJSON();
icl7126
  • 5,740
  • 4
  • 53
  • 51
jsdiamond
  • 165
  • 2
  • 2
  • I think you don't even need the `Object.assign` there. `.toJSON` retuns a proper object (`Object.keys($0.getBoundingClientRect().toJSON()) ` works). You get a fresh object every time too! `$0.getBoundingClientRect().toJSON() !== $0.getBoundingClientRect().toJSON()` – Zach Lysobey Feb 08 '18 at 23:48
  • 1
    keep in mind, `toJSON` is not working for rect objects at firefox folks – mkg Mar 15 '18 at 15:30
  • 2
    Sorry, but I have to downvote the answer since you didn't mention it doesn't support with Firefox – Yami Odymel May 07 '18 at 12:57
5

This is something that I can live with:

const persistRect = JSON.parse(JSON.stringify(someElement.getBoundingClientRect()))
Skyrocker
  • 970
  • 8
  • 15
4

You could use the extend method if you are using jQuery.

var obj = $.extend( {}, element.getBoundingClientRect());
Syed Waqas Bukhary
  • 5,130
  • 5
  • 47
  • 59
Vince
  • 49
  • 1
  • 2
  • 2
    This can and does work, but $.extend is notoriously inefficient compared to various other cloning methods. See http://jsben.ch/#/bWfk9. As OP notes ClientRect is a special object though, and most (all?) other techniques do not work as expected, so not a lot of choices here. – kamelkev Feb 07 '17 at 20:51
3

You could use Object.fromEntries() to create your obj.

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>

However this would also copy the toJSON fn to the new obj. Therefore I suggest to filter non-numeric values: .filter(k => !isNaN(domRec[k]).

Lightweight

If you are just interested in x, y, width, height, you could use DOMRect instead of DOMRectReadOnly and skip the .filter() part.

You can calc values for bottom, right from these 4 values and left, top are just aliases for x, y.

let domRec = someElement.getBoundingClientRect();
let domObj = Object.fromEntries(Array.from(Object.keys(DOMRect.prototype), k => [k, domRec[k]]));
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Exodus 4D
  • 2,207
  • 2
  • 16
  • 19
2

Functional ES6 variant:

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())
pkfm
  • 451
  • 3
  • 7