## What are offsets?

While building applications and working with HTML DOM elements, it's quite common to come across the need for getting an element's distance from the top of the page, its parent or the viewport.

A basic use case of this could be to attach a scroll listener to monitor an element's appearance into the viewport. For this we would surely need to know its distance from the top of the web page to determine how much scrolling will bring it into view.

In technical terminology, we call this distance as the offset of the element.

The offset of an element is its distance from a reference point.

This reference point could be the top or left of the web document; the viewport; or simply a given element.

Typically, we denote offsets in two axes - x-axis and y-axis - with distances from the left of something and with distances from the top of something respectively.

In other words, reference points for an element's offsets are usually the left and top edges of something (as discussed before, this could be the web document, the viewport, or another element).

The reason for this generalisation is that it's the left and top edges of the web document where elements originate from. The transformational axis has its origin on the top-left point, and likewise it makes sense to model distances in these two points.
There's absolutely no issue in modeling offsets from the right and bottom of something; it's just that these reference points aren't used a lot and hence would feel unreal as we work with them!

Following is an illustration to clarify this:

See how the blue box is offset 500px from the top of the document and 300px from the left of the document.

If we were to say this out concisely, we could simply say that the blue box has the offsets (300px, 500px) relative to the document.

This pair follows the normal graphical co-ordinate syntax in maths i.e `(x, y)`, except for that `x` and `y` this time are distances from the left and top of a given reference point.

Determine the left and top offsets of all the boxes shown in the snippet below, relative to the document.

You may state the offsets of each box as a pair (x, y).

The `body` element has 10px of margins around its ends.
Blue Box
width: 180px
height: 120px
Pink Box
width: 150px
height: 120px
margin-top: 30px
• Blue box: `(0, 0)`, Pink box: `(150px, 30px)`
• Blue box: `(10px, 10px)`, Pink box: `(30px, 190px)`
• Blue box: `(10px, 10px)`, Pink box: `(190px, 40px)`

## Calculating offsets in JavaScript

Now that you know what offsets are, it's time to learn how to actually calculate them in JavaScript.

We'll start by the `getBoundingClientRect()` method which we saw in the previous Bounding Box chapter.

### `getBoundingClientRect()`

Calling `getBoundingClientRect()` on an element returns an object containing useful information about its bounding box, such as its width, height - and even its offsets!

Two properties exist on this object which hold the left and top offsets of the element, relative to the viewport. They are `left` and `top`, respectively.

A positive value of `top` indicates that the element is somewhere below the top of the viewport.

A negative value indicates that the element is somewhere above the top of the viewport and therefore out of view.

Same goes for `left`.

What does a positive value for `left` indicate, as returned by the `getBoundingClientRect()` method on a given element.

• The element is to the right of the viewport's left edge.
• The element is to the left of the viewport's left edge.

Let's see a few examples...

Consider the code below:

``````<div id="blue-box">Blue box</div>
<div id="pink-box">Pink box</div>``````
``````body {
margin: 10px;
}
div {
height: 120px;
width: 180px;
float: left;
}
#blue-box {
background-color: #b2d0f8;
}
#pink-box {
background-color: #ffa9f8;
margin-top: 30px
}``````

Here we create the same two boxes as we did in the snippet above - this time, instead of doing the offset maths ourselves, we leave it to the JavaScript engine.

``````var blueBox = document.getElementById("blue-box"),
pinkBox = document.getElementById("pink-box");

// bounding rectangle for blueBox
var blueRect = blueBox.getBoundingClientRect();

// bounding rectangle for pinkBox
var pinkRect = pinkBox.getBoundingClientRect();

console.log(blueRect.left, blueRect.top);
console.log(pinkRect.left, pinkRect.top);``````
10 10
190 40

As you can confirm, the values logged to the console are synonymous with the offset values we calculated previously.

Now this was a simple use case of `left` and `top` - below we consider a slightly more complicated example to truly understand what's meant by `getBoundingClientRect()` returning offsets relative to the viewport.

We've placed a `div` element 1000px from the top of the HTML document on purpose.

``<div>Some content</div>``
``````body { margin: 0 }
div { margin-top: 1000px }``````
`body { margin: 0 }` is given to remove the predefined margins on the `<body>` element.

First initially, when the webpage loads (and is at its 0px scroll position), we call `getBoundingClientRect()` on `div` and log its `top` property. It returns the value `1000`, just as we expect it to.

One thing to remember while working with `getBoundingClientRect()` is that if we call it while our page is at its 0px scroll position, then what the method will return will, in effect, be the distance of the element from the left and top of the page as well.

Afterwards, when we scroll for let's say 350px and then call `getBoundingClientRect()` once again, its `top` property returns `650`.

See how the value changes? The element is now only 650px far away from the top edge of the viewport.

Had `top` been relative to the top of the document, instead of the viewport, its value would've always remained the same!

If we continue scrolling and cross the 1000px mark to end, let's say, at 1520px of scroll, here is what will happen.

Calling `getBoundingClientRect()` on `div` will return an object whose `top` property will be equal to `-520`, since now the element is 520px above the viewport (1000px was its initial distance).

You can visualise all this in the link below.

If you look for a hidden gem in here, you'll see that:

Adding the scroll position and the `top` property, at any point, gives us the distance of an element from the top of the document.

You can confirm this very quickly:

1. 0 + 1000px = 1000px (at the initial scroll position)
2. 350px + 650px = 1000px (once we scroll for 350px)
3. 1520px + (-520px) = 1000px

This means that if you want to get the offset of any element `ele` relative to the top of the web document, you can reliably use the following statement:

``ele.getBoundingClientRect().top + window.pageYOffset``

Obviously you'll want to save this in a some variable, or identifer so that you can reuse it in conditional checks or whatever you need it for.

The reason we add `window.pageYOffset` is to ensure that if the browser automatically scrolls to some scroll position (for example, when we visit ID links) before our calculation is made, the calculated offset accounts for the performed scroll.

### `offsetLeft` and `offsetTop`

Apart from `getBoundingClientRect()`, we've got yet another way to compute offsets of an element; and this time not just relative to the viewport.

That is using the `offsetLeft` and `offsetTop` properties, available on `Element` objects.

`offsetLeft` returns the distance of an element from the left of its nearest relative ancestor whereas `offsetTop` returns the distance of the element from the top of its nearest relative ancestor.

But what is the nearest relative ancestor?

For any element its nearest relative ancestor is defined as the first of its ancestor elements that has `position: relative` set or else the `<body>` element.

So for instance, if we have the following HTML then the nearest relative ancestor of `p` would be the `#ancestor2` element.

``````<div id="ancestor2" style="position: relative">
<div id="ancestor1">
<p>A paragraph</p>
</div>
</div>``````

This is simply because it has `position: relative` set on it, as can be seen in line 1.

Similarly, in the HTML below, since none of the elements have `position: relative` set, the nearest relative ancestor of `p` will be taken the default `document.body` object.

``````<body><!--This is the default relative ancestor-->
<div id="ancestor2">
<div id="ancestor1">
<p>A paragraph</p>
</div>
</div>
</body>``````

Now you ask: OK the term 'nearest relative ancestor' sounds interesting, but is there any way to get it in JavaScript? The answer is simply - yes!

For a given element, its `offsetParent` property holds another element which is its nearest relative ancestor. If none exists, then the property simply holds a reference to `document.body`.

Let's review the previous code above:

``````<div id="ancestor2" style="position: relative">
<div id="ancestor1">
<p>A paragraph</p>
</div>
</div>``````

Now with this in place, following we retrieve the nearest relative ancestor of `p` and log its `id` attribute:

``````var p  = document.getElementsByTagName("p")[0];

console.log(p.offsetParent.id); // "ancestor2"``````

As expected, it turns out to be `"ancestor2"`.

Similarly for the HTML below, `p.offsetParent` will return the `document.body` element.

``````<body><!--This is the default relative ancestor-->
<div id="ancestor2">
<div id="ancestor1">
<p>A paragraph</p>
</div>
</div>
</body>``````
``````var p  = document.getElementsByTagName("p")[0];

console.log(p.offsetParent === document.body); // true``````

With `offsetParent` out of the way, let's now experiment around with `offsetLeft` and `offsetTop`.

Consider the following code. We've got a `p` element sitting inside a `div` container, at a left margin of 70px and top margin of 130px. The `div` container has `position: relative` set and is therefore the nearest relative ancestor of `p`.

``````<div>
<p>A paragraph</p>
</div>``````
``````body {margin: 10px}
div {
margin: 40px 0 0 30px;
border: 1px solid grey;
position: relative; /* this part is the most important here */
}
p {
margin: 130px 0 0 70px;
background-color: #e2b600;
}``````
To understand the order of the four parameters to `margin`, please refer to CSS Margins.

A paragraph

If we log the `offsetLeft` and `offsetTop` properties of the `p` element, it should output its corresponding margin values:

``````var p = document.getElementsByTagName("p")[0];

console.log("offsetLeft:", p.offsetLeft)
console.log("offsetTop:", p.offsetTop)``````
offsetLeft: 70
offsetTop: 130

The `offsetParent` of `p` is `div`; likewise, its `offsetTop` and `offsetLeft` properties will be evaluated relative to this `div` element.

However, removing just one style from `div`, i.e `position: relative`, could produce completely different results here.

In the code above, if `div` doesn't has `position: relative` set, then what will `p`'s `offsetTop` and `offsetLeft` properties return.

Without `position: relative` on `div`, the nearest relative ancestor of `p` will be the `body` element. Consequently, its `offsetTop` and `offsetLeft` properties will be computed relative to the `body` element.

This means that `offsetLeft` and `offsetTop` will be equal to `111` (70 + 30 + 1 + 10) and `181` (130 + 40 + 1 + 10), respectively.

Remember to take into account border widths when calculating offsets on your own!

As you can clearly see, `offsetLeft` and `offsetTop` don't return an element's distance relative to the viewport, but rather to its `offsetParent`.

Moving on, the `offsetParent` of `document.body` is the `null` value.

This produces one really interesting consequence i.e the value of `offsetParent` can be used to calculate the offset of any element relative to the top of the document, in an iterative/recursive manner.

Let's see whether you can figure out how to accomplish this idea! Your skills are at a test now!

Construct a function that takes in an element object and returns its distance from the top of the document using `offsetTop` and `offsetParent`.

If the element's `offsetParent` is `null` just return `null` rightaway.

You may use the following setup:

``````function distanceTop(ele) {
// write your code here
}``````

The logic is superbly easy - walk your way up the chain of all `offsetParent`s until it becomes `null`, and sum up each one's `offsetTop` as you do so.

``````function distanceTop(ele) {
if (ele.offsetParent === null) return null

var parent = ele;
var offset = 0;

while (parent !== null) {
offset += parent.offsetTop
parent = parent.offsetParent
}

return offset;
}``````

## In conclusion

Knowing the purpose and significance of calculating offsets of elements relative to the document or the viewport on a webpage, is a crucial skill web developers must have.

Just go over the applications where offset computations are utilised and you'll understand this. We've got lazy loading, infinite scrolling, ad metrics, powering CSS features and way much more!

Likewise go through this chapter twice or thrice if the need be, as well as experiment around in the console until and unless you understand offsets to the core!. These are perhaps the best ways to learn anything the right way!