51

The method el.getBoundingClientRect() gives a result relative to the viewport's top-left corner (0,0), not relative to an element's parent, whereas el.offsetTop, el.offsetLeft (etc.) give a result relative to the parent.

What is the best practice to have the coordinates of an element relative to its parent? el.getBoundingClientRect() modified (how?) to use parent as (0,0) coordinate, or still el.offsetTop, el.offsetLeft and so on?

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
Basj
  • 41,386
  • 99
  • 383
  • 673

1 Answers1

89

You can use getBoundingClientRect(), simply subtracting the coordinates of the parent:

const parentPos = document.getElementById('parent-id').getBoundingClientRect();
const childPos  = document.getElementById('child-id').getBoundingClientRect();
const relativePos = {};

relativePos.top    = childPos.top - parentPos.top,
relativePos.right  = childPos.right - parentPos.right,
relativePos.bottom = childPos.bottom - parentPos.bottom,
relativePos.left   = childPos.left - parentPos.left;

console.log(relativePos);
// something like: {top: 50, right: -100, bottom: -50, left: 100}

Now you have the coordinates of the child relative to its parent.

Note that if the top or left coordinates are negative, it means that the child escapes its parent in that direction. Same if the bottom or right coordinates are positive.

Working example

var parentPos = document.getElementById('parent-id').getBoundingClientRect(),
    childPos = document.getElementById('child-id').getBoundingClientRect(),
    relativePos = {};

relativePos.top = childPos.top - parentPos.top,
relativePos.right = childPos.right - parentPos.right,
relativePos.bottom = childPos.bottom - parentPos.bottom,
relativePos.left = childPos.left - parentPos.left;

console.log(relativePos);
#parent-id {
    width: 300px;
    height: 300px;
    background: grey;
}

#child-id {
    position: relative;
    width: 100px;
    height: 200px;
    background: black;
    top: 50px;
    left: 100px;
}
<div id="parent-id">
    <div id="child-id"></div>
</div>
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • 4
    This does not account for CSS transforms. – trusktr Sep 28 '18 at 19:59
  • 3
    I meant, if there are 3D transforms between parent and child, this does not work. It will be the difference in document space, but not relative to the parent in parent space. – trusktr Sep 28 '18 at 20:07
  • 2
    This worked for me in another context, but you may have to do "relativePos.right = childPos.right - parentPos.left", if you want the position relative to the top/left. Same for .left. – Stiefel Mar 19 '20 at 18:29
  • 4
    It will probably also not work if the parent has a scroll :( – chitzui Nov 14 '20 at 20:55
  • doesn't work, the parent's borders & paddings shifted the result – Heyyy Marco May 06 '21 at 06:26
  • Also won't work if parent has display: contents. You'd have to take the parent of that parent. – Simon_Weaver Oct 05 '21 at 21:44