Introduction
In the last chapter, we saw a good bunch of properties and methods available on window
as global identifiers or an element nodes that relate, in one way or the other, to the client, i.e. the browser window.
Now in this chapter, it's time to focus solely on element nodes and see a couple of properties and methods available on them as per the CSSOM specification. We'll start off with the five properties offsetLeft
, offsetTop
, offsetParent
, offsetWidth
and offsetHeight
, where the former three allow us to retrieve the position of an element form the document.
Then, we'll move over to consider the properties clientLeft
, clientTop
, clientWidth
and clientHeight
. Note however that unlike the usage of the word 'client', these properties don't apply to the browser window, but rather to the padding box of a given element.
Finally, we'll consider a couple of scrollling utilities as well such as scrollLeft
, scrollTop
, scrollWidth
and scrollHeight
. And we'll also consider a handful of methods including scrollIntoView()
.
Let's begin.
Getting the offsets of an element
The two properties offsetLeft
and offsetTop
allow us to retrieve the offset of an element from its nearest positioned ancestor.
But what exactly is an offset and the nearest positioned ancestor?
Starting with the former,
On web pages, an element's offset could refer to many kinds of distances, such as the distance of the element from the top edge of the document, the distance from the top edge of the viewport, or the distance from another element.
The context in which we use the word 'offset' distinguishes between each kind. Furthermore, typically, as per the co-ordinate system used on the web, whereby the origin point lies at the top-left corner of the page, distances are given from the left of something or from the top of something.
Technically, though this doesn't mean that we can't work out the distance of an element from the right of something or from the bottom of something; it's just a little bit of extra work.
Anyways, consider the following example:
<div id="d1">A simple div</div>
body {
margin: 10px;
padding: 5px;
}
#d1 {
font-size: 30px;
background-color: lightgrey
}
The <body>
has a margin of 10px and a padding of 5px. Given this info, what do you think is the offset of the <div>
element?
Well, it's really simple. First, we gotta appreciate the fact that the offset is customarily given in two different directions, i.e. the horizontal distance from the left edge of something and the vertical distance from the top edge of something.
Following this notion, we see that the offset of the <div>
element from the top edge of the document is 15px and so is the case for its offset from the left edge of the document, i.e. its also 15px.
Not really that difficult, was it?
Coming back to the properties offsetLeft
and offsetTop
, they both return the offset of an element from the left and top edge of its nearest positioned element, respectively.
And now it's time to talk about the term 'nearest positioned ancestor'.
For a given element, its nearest positioned ancestor is simply the ancestor element that is closest to it, in terms of the whole tree structure, and that meets either of the following criteria:
- Isn't statically positioned, i.e doesn't have the default
position: static
CSS style applied. - Is statically positioned, but is a
<td>
,<th>
or a<table>
element. - Is the
<body>
element.
From this, note that the <body>
element or the root <html>
element, both don't have a nearest positioned element. Also, if an element has display: none
set on it or position: fixed
, it doesn't have a nearest positioned element either.
The good news for us in this regard is that for any given element, we can get to know of its nearest positioned element via the property offsetParent
.
As stated before, offsetParent
isn't bound to return the actual parent of a given DOM element node. Rather, it's bound to return the ancestor of the given element that meets the criteria of the nearest positioned ancestor as mentioned above.
offsetParent
would've been offsetAncestor
. What do you think?As per the CSSOM View Module specification, the computation of offsetParent
for a given element can be found right at the start of section 7: Extensions to the HTMLElement
Interface.
If an element doesn't have a nearest positoned ancestor, offsetParent
returns back null
.
Let's take the help of a decent amount of examples to make intuition of all this lengthy, and somewhat boring, discussion and make it a little bit more interesting.
First, let's get the hang of offsetParent
and, obviously, of the nearest positioned ancestor with it.
Consider the following example that we saw above:
<div id="d1">A simple div</div>
body {
margin: 10px;
padding: 5px;
}
#d1 {
font-size: 30px;
background-color: lightgrey
}
Let's inspect the offsetParent
of the <div>
element:
document.querySelector('#d1').offsetParent.nodeName
'BODY'
And, it's the <body>
element. The very first ancestor of <div>
here is <body>
, and so without further ado, offsetParent
becomes <body>
. This one was very very simple.
Let's consider another example.
Everything below is the same as before, just this time we've encapsulated the <div>
element around a <section>
:
<section>
<div id="d1">A simple div</div>
</section>
body {
margin: 10px;
padding: 5px;
}
section {
border: 5px solid red;
padding: 10px
}
#d1 {
font-size: 30px;
background-color: lightgrey
}
Can you figure out the offsetParent
of the <div>
element here?
document.querySelector('#d1').offsetParent.nodeName
'BODY'
Once again, the offsetParent
is <body>
. The first ancestor of <div>
here is <section>
, but it's statically positioned (and isn't either of the elements <td>
, <th>
or <table>
). Hence, we consider the next ancestor up the chain and see that it's <body>
. Likewise, offsetParent
becomes <body>
.
Let's look at yet another example. The HTML below is the exact same as the last example above, with the exception of the fact that the <section>
element is now made relatively positoned:
<section style="position: relative">
<div id="d1">A simple div</div>
</section>
Now, if we inspect the offsetParent
of <div>
, we expect to see the <section>
element returned. Let's try it out:
document.querySelector('#d1').offsetParent.nodeName
'SECTION'
As expected, the offsetParent
is the <section>
element by virtue of the fact that <section>
has position: relative
set on it.
In the code below, we quickly inspect the offsetParent
of a couple of elements on the document:
<div id="d1">
<div id="d2">A simple div</div>
</div>
body {
margin: 10px;
padding: 5px;
}
#d1 {
position: fixed;
width: 80%;
background-color: yellow;
}
#d2 {
font-size: 35px;
border: 5px solid red;
margin: 15px
}
Following is the console snippet where we inspect the offsetParent
property on each element up the chain starting with the innermost <#d2>
element:
document.querySelector('#d2').offsetParent.id
'd2'
document.querySelector('#d1').offsetParent
null
document.body.offsetParent
null
document.documentElement.offsetParent
null
The return value of each statement is quite simple to understand:
- The first ancestor of
#d2
, that is#d1
, hasposition: fixed
set on it, hence theoffsetParent
of#d1
becomes the#d2
element. - Since
#d2
hasposition: fixed
set on it, it doesn't have a nearest positioned ancestor, i.e. itsoffsetParent
isnull
. - As stated above, both
<body>
and<html>
don't have a nearest positioned ancestor and likewise theiroffsetParent
isnull
.
Hopefully, at this stage, you are comfortable with the idea of offsetParent
and so we can finally move over to consider the properties offsetLeft
and offsetTop
.
To state it once again, and this time more precisely, offsetTop
returns back the distance of an element's border box from the top edge of its offsetParent
's padding box. A similar reasoning applies to offsetLeft
.
However, keep in mind, that if a given element's offsetParent
is null
, this doesn't necessarily mean that its offsetTop
or offsetLeft
would be 0
.
As specified in the algorithm for offsetTop
(and a similar one for offsetLeft
), if the offsetParent
is null
, further computation is done before calculating the final value of offsetTop
for a given element.
This is detailed as follows:
- If the element is
<body>
or hasdisplay: none
set on it (both of whoseoffsetParent
isnull
),offsetTop
is0
. - Otherwise, if the
offsetParent
of the element isnull
because of some other reason (for e.g.position: fixed
), return the element's distance from the top edge of the web page.
Let's consider a quick example:
In the code below, we inspect the offsetTop
of the <div>
element:
<section>
<div id="d1">A simple div</div>
</section>
body {
margin: 10px;
padding: 5px;
}
section {
border: 5px solid red;
padding: 30px;
position: relative
}
#d1 {
font-size: 30px;
background-color: lightgrey
}
As we can clearly see, the <section>
element has a large padding of 30px while a border of 5px on all sides. Our job is to get the value of offsetTop
of the <div>
element. What do you think it would be equal to?
Well, let's see it:
document.querySelector('#d1').offsetTop
30
Just as expected, offsetTop
is 30
as the padding around the <section>
element, which is the offsetParent
of the <div>
, is 30px.
The figure below illustrates what dimension does offsetTop
exactly target in the example above:
offsetTop
targets in the example above.Simple?
Let's consider removing the position: relative
declaration from the <section>
element above, and then re-inspect the offsetTop
of the <div>
:
document.querySelector('section').style.position = 'static'
'static'
document.querySelector('#d1').offsetTop
50
So this time the value of offsetTop
gets promoted to 50
. But how?
Well, first let's determine the offsetParent
of the <div>
, which is extremely simple. It's just the <body>
element. So the <div>
's offsetTop
is just the distance between the top edge of its border box and the top edge of <body>
's padding box. This should technically be equal to 40, however the value above is 50
.
Where does the extra 10px come from?
Well, this is an edge case.
offsetParent
is <body>
and it's statically positioned, as in the example above, its margin box is considered to be its padding box. And so this means that the padding box of <body>
above includes its 10px margin as well.The figure below illustrates the exact dimension that offsetTop
targets in this example:
offsetTop
targets in this example.If we apply position: relative
to <body>
, its padding box would work as usual, i.e. it won't include its margins as well.
In the following snippet, we do this and then re-inspect the offsetTop
of the <div>
:
document.querySelector('section').style.position = 'static'
'static'
document.querySelector('#d1').offsetTop
50
Apparently, now the extra 10px from the margins around <body>
doesn't get counted, all thanks to the application of position: relative
to it.
The figure below illustrates offsetTop
in this very example:
offsetTop
targets in this example.Dimensions of the border box
To retrieve the width of the border box of an element, we can use its offsetWidth
property. Similarly, to retrieve its height, we can use the offsetHeight
property.
To recap it, the border box of an element is the smallest box around it that includes all of its borders. For instance, consider the figure below:
The element has 10px of border on all but the bottom side, a padding of 20px, an absolute width of 500px, and an absolute height of 100px. Based on the dimensions given above, it's quite easy to calculate the width and height of the border box:
Width: 500 + 20 + 20 + 10 + 10 = 560px
Height: 100 + 20 + 20 + 10 = 150px
Note that offsetWidth
and offsetHeight
, just like offsetLeft
and offsetTop
, don't consider any CSS transformations applied to the underlying element.
Let's now consider an example.
In the code below, we have a <div>
element that tries to replicate the figure shown above:
<div id="d1">A div</div>
#d1 {
border: 10px solid #777;
border-bottom: none;
padding: 20px;
width: 500px;
height: 100px;
}
As we calculated the width and height of the border box above, they were 560px and 150px, respectively. Let's see what does offsetWidth
and offsetHeight
have to offer us:
var divElement = document.querySelector('#d1')
divElement.offsetWidth
560
divElement.offsetHeight
150
As expected, both the properties yield the same values that we calculated manually.
Let's consider this same example, just this time with a CSS transformation applied to it additionally:
<div id="d1">A div</div>
#d1 {
border: 10px solid #777;
border-bottom: none;
padding: 20px;
width: 500px;
height: 100px;
transform: scale(0.5);
}
As mentioned before, offsetWidth
and offsetHeight
don't take CSS transformations into account, hence both properties should expectedly return back the same values as they did before.
divElement.offsetWidth
560
divElement.offsetHeight
150
And yes they do!
Recall that to obtain the exact dimensions of the border box of an element after applying CSS transformations we can use the getBoundingClientRect()
method. This can be seen as follows:
divElement.getBoundingClientRect().width
280
divElement.getBoundingClientRect().height
75
As can be seen, each dimension is half its corresponding value when inspected via offsetWidth
and offsetHeight
, respectively.
The edge case of the <html>
element
The border box of an element always includes any scrollbars associated with it. This doesn't however apply to the <html>
element. When we apply borders to the <html>
element, they don't encapsulate the scrollbar of the document. Hence, the border box of <html>
doesn't include its scrollbar.
Remember that the scrollbar applied to a web page by default is applied to the <html>
element, not to the <body>
element.
Now what this means for us is that the offsetWidth
of <body>
excludes the width of the scrollbar of the web page, simply because the scroll bar isn't part of <body>
. (Note that if we somehow change the styles of the web page such that the scrollbar is applied over the <body>
element, then this might change.)
Moreover, this also means that the offsetWidth
of <html>
excludes the width of the scrollbar of the web page as well.
The clientLeft
and clientTop
properties
Named akin to offsetLeft
and offsetTop
, we have the properties clientLeft
and clientTop
available on element nodes.
The question is: what's their purpose?
Well, clientTop
returns the distance of the padding box from the top edge of the border box of an element. In other words, this is just the width of the top border plus the height of the top scrollbar (if there is any scrollbar).
The same goes for the clientLeft
. Stating it precisely, clientLeft
is simply the width of the left border of the underlying element plus the width of its left scrollbar (if there is any scrollbar).
Beware of the word 'client' here!
As we mentioned at the very start of this chapter, the word 'client' here is misleading.
In other parts of the CSSOM API, such as in the method getBoundingClientRect()
, 'client' refers to the browser window. However, in clientTop
, 'client' refers to the padding box of the element under inspection.
The figure below illustrates the clientTop
property.
clientLeft
and clientTop
properties.Let's consider an example:
<div id="d1">A div</div>
#d1 {
border: 20px solid #bbb;
padding: 20px;
}
var divElement = document.querySelector('#d1')
divElement.clientLeft
20
divElement.clientTop
20
As is evident, the width of the left and the top borders of the <div>
element is 20px, and likewise clientLeft
and clientTop
both yield 20
.
Dimensions of the padding box
Where offsetWidth
and offsetHeight
apply to the dimensions of the border box of an element, the clientWidth
and clientHeight
properties apply to its border box.
In particular, clientWidth
returns back the width of the calling element's padding box excluding any rendered scrollbars, and obviously also ignoring any applied CSS transformations. The same goes for clientHeight
, albeit for the height of the element's padding box.
To recap it, the padding box of an element is the smallest box around it that includes all of its padding. This excludes any rendered scrollbars. Consider the figure below.
Let's try to calculate the width and height of the padding box here:
Width: 500 + 20 + 20 = 540px
Height: 100 + 20 + 20 = 140px
Easy, isn't this?
As we did with offsetWidth
above, let's try to replicate this figure using an actual HTML element and then retrieve its clientWidth
and clientHeight
.
<div id="d1">A div</div>
#d1 {
border: 10px solid #bbb;
padding: 20px;
}
var divElement = document.querySelector('#d1')
divElement.clientWidth
540
divElement.clientHeight
140
The scrollLeft
and scrollTop
properties
Recall from the CSSOM Viewport chapter, the global scrollX
and scrollY
properties tell us about the scroll position of the HTML document in the given viewport
A similar kind of information can be obtained for an element as well in the CSSOM API, thanks to the scrollLeft
and scrollTop
properties of the Element
interface, respectively.
The scrollLeft
property represents the horizontal scroll position of a given element, supposing that it has an associated scrollbar.
Unlike the global scrollX
property, however, it's both capable of being read from and written to. As you can probably guess, when scrollLeft
is set to a particular value, the underlying element is horizontally scrolled to that very position.
On the same lines, the scrollTop
property, which is arguably more commonly used than scrollLeft
due to the natural flow of content on most web pages, represents the vertical scroll position of a given element.
Now here are certain things to note regarding setting scrollLeft
and scrollTop
:
- If the element doesn't have a scrollbar, setting either
scrollLeft
orscrollTop
has no effect. - Setting either property to a value greater than the maximum possible scroll position in the respective direction gets it to be set to that maximum value instead.
- Setting either property to a negative value gets it to be set to
0
instead.
As always, examples are the best teachers, ain't they? Likewise, let's consider a couple of them to better understand the behavior of scrollTop
.
Consider the following code where we have a <section>
element with a lot of content inside it and with the overflow: scroll
style set so that it's scrollable:
<section id="s1">
This is a section with a lot of content. The purpose of so much content is just so that the element is scrollable.
<br><br>
<div id="d1">A small div</div>
<br><br>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Provident vero magnam, ipsa itaque at omnis amet nisi cumque error harum maiores possimus quasi officia ea architecto obcaecati incidunt consequuntur ex!
</section>
<br>
<button>Get scrollTop</button>
#s1 {
overflow: scroll;
padding: 20px;
border: 5px solid #777;
height: 100px
}
#d1 {
height: 200px;
width: 50%;
background-color: yellow;
}
Lorem ipsum dolor sit amet consectetur adipisicing elit. Provident vero magnam, ipsa itaque at omnis amet nisi cumque error harum maiores possimus quasi officia ea architecto obcaecati incidunt consequuntur ex!
Our aim is to alert the current vertical scroll position of the <section>
element when the given button is clicked. This is accomplished below:
var buttonElement = document.querySelector('button');
var sectionElement = document.querySelector('#s1');
buttonElement.onclick = function() {
alert(`scrollTop: ${sectionElement.scrollTop}px`);
}
This example demonstrated the scrollTop
property in the read context. Now, let's see it in the write context.
Consider the following code where we have the same HTML and CSS setup as before, just this time the button's click handler is configured to vertically scroll the <section>
by a further 10px:
var buttonElement = document.querySelector('button');
var sectionElement = document.querySelector('#s1');
buttonElement.onclick = function() {
// Vertically scroll the section by a further 10px.
sectionElement.scrollTop += 10;
}
sectionElement.scrollTop += 10
is effectively the same as the statement sectionElement.scrollBy(0, 10)
.So this was simple, what do you think?
Moving on, there is an important thing to take note of while working with scrollLeft
or scrollTop
(or any other mechanism of scrolling an element, such as scroll()
, scrollBy()
as we shall see later on).
That is, changing the scroll position of an element works regardless of the fact if the element has a scrollbar on it or not. But keep in mind that this assumes that the element has content overflowing it, which might either be hidden (via overflow: hidden
) or made accessible by means of scrolling (via overflow: auto
or overflow: scroll
).
We've already witnessed above that when a given element has a scrollbar rendered alongside it, changing its scroll position by means of setting scrollLeft
and/or scrollTop
does have an effect.
However, in the following example, the exact same as before, we also demonstrate this for the case when the element doesn't have a scrollbar rendered alongside it (but still has overflowing content) by virtue of the style overflow: hidden
:
#s1 {
overflow: hidden;
}
Open the link above and try clicking the button again and again. As you do so, you'll notice it producing the exact same effect as in the last example above.
Dimensions of the content
Before we talk about the scrollWidth
and scrollHeight
properties, let's first quickly recap offsetWidth
/offsetHeight
and clientWidth
/clientHeight
.
offsetWidth
/offsetHeight
target the width and height of the border box of the underlying element, respectively.clientWidth
/clientHeight
target the width and height of the padding box of the underlying element, respectively.
Alright, so now what exactly is scrollWidth
and scrollHeight
?
Well, in simple terms, scrollWidth
is the width of the content of a given element.
Note that this might be different from the width of the content box of the element — the content might overflow out of the content box and thus have a different dimension than that of the content box.
Consider the HTML + CSS below:
<section id="s1">
<div id="d1">A simple div. Lorem ipsum dolor sit amet consectetur adipisicing elit. Ab tempora quia doloribus esse laborum quis voluptatum excepturi nobis et ullam, eaque repellendus suscipit, consequuntur maxime quisquam deserunt saepe. Nam, ea.<div>
</section>
#s1 {
overflow: auto;
height: 100px;
width: 300px;
padding: 20px;
background-color: #eee
}
#d1 {
/* The following width and height make #d1 to overflow #s1 */
height: 200px;
width: 600px;
background-color: yellow;
}
Here's the output this produces:
The <section>
element has a <div>
that's larger both in width and height than it and thus has a scrollbar rendered alongside it, courtesy of the overflow: auto
style.
Based on the code above, the figure below illustrates scrollWidth
of <section>
:
scrollWidth
.As can be seen, the scrollWidth
of the <section>
element is the width of the bounding box around it that surrounds all of its content, including the one that overflows it.
Keep in mind that, just like clientWidth
:
scrollWidth
doesn't consider the width of the borders on either side of the given element.scrollWidth
doesn't consider the width of the scrollbar (this is obvious because it measures the width of the content behind the scrollbar).scrollWidth
does consider the padding on both sides of the given element.
The same concepts can be applied to the scrollHeight
property, but for the vertical dimension of the element rather than its horizontal dimension.
Now, following from this discussion, there is an extremely ingenious approach that we could take to determine whether there is content overflowing a given element or not. This is detailed as follows:
Determining if an element has overflowing content
If the scrollWidth
/scrollHeight
of a given element is larger than its clientWidth
/clientHeight
, this simply means that the element has overflowing content.
Note that by no means does this tell us that the element has a rendered scrollbar — this can only be confirmed by inspecting the CSS styles of the element which requires us to invoke the global getComputedStyle()
function, passing the element as an argument.
We'll see how to work with getComputedStyle()
later in the CSSOM Computed Styles chapter.
Coming back to the example above, let's actually inspect the value of the scrollWidth
property of the <section>
element:
var sectionElement = document.querySelector('#s1');
console.log('scrollWidth', sectionElement.scrollWidth);
console.log('scrollHeight', sectionElement.scrollHeight);
Each of the values here makes perfect sense:
scrollWidth
is640
because the width of the<div>
is 600px and the padding on both ends of<section>
is 20px each.scrollHeight
is240
because the height of the<div>
is 200px and the padding on both ends of<section>
is 20px each.
The scrollIntoView()
method
Recall the example from the CSSOM Viewport chapter where we used a button and a click handler on it to scroll the viewport such that a given element's top edge aligns with the viewport's top edge.
However, in that chapter, we manually calculated and hardcoded the distance of the element from the top edge of the document, which was passed in to the scroll()
global method.
Here's that example reviewed:
<button>Bring heading into view</button>
<h1>This is a heading</h1>
body {
margin: 0;
height: 2000px;
}
button {
position: absolute;
top: 0;
padding: 15px;
}
h1 {
margin-top: 1000px;
background-color: yellow;
height: 100px;
}
var buttonElement = document.querySelector('button');
buttonElement.onclick = function() {
// Bring the <h1> element into view by scrolling the viewport
// 1000px vertically.
scroll(0, 1000);
}
A slightly better option would be to programmatically compute the distance of the element from the top of the page, in JavaScript, and then scroll the viewport to that particular position. Everything else remains the same.
We accomplish this in the following code:
var buttonElement = document.querySelector('button');
var h1Element = document.querySelector('h1');
buttonElement.onclick = function() {
// The distance of <h1> from the top edge of the document.
var topOffset = h1Element.getBoundingClientRect().top + scrollY;
scroll(0, topOffset);
}
The variable topOffset
represents the distance of <h1>
from the top of the document. It's computation is quite trivial:
- The
getBoundingClientRect()
method returns its distance from the top edge of the viewport. scrollY
gives the current vertical scroll position of the document. This is added to the previous value just in case the viewport was automatically scrolled by the browser before the execution ofgetBoundingClientRect()
.
scrollY
(or its alias pageXOffset
) is safe play. Sometimes, browsers automatically scroll a document to a given position and if that happens, then solely relying on getBoundingClientRect().top
would only end up with the wrong distance — we'd need to add scrollY
to it as well.As you might agree, this second approach is definitely better and more flexible than the last one above. We can position the <h1>
element anywhere and be rest assured that upon the button's click, the viewport gets scrolled such that the element comes into view, just as desired.
However, there still is an even more flexible and better option and that is to use the Element
interface's scrollIntoView()
method to accomplish essentially the same thing, although scrollIntoView()
is much more sophisticated and complex in its inner workings.
Talking about the syntax of scrollIntoView()
, it has three different (overloaded) forms:
The first one shown below takes no arguments and simply scrolls the viewport to align the element's top edge with the viewport's top edge:
scrollIntoView()
The second one takes a single Boolean argument as follows:
scrollIntoView(alignToTop)
alignToTop
specifies whether we want the element's top edge to align with the top edge of the viewport or not. If it's true
, the top edges are aligned together. Otherwise, the bottom edges of the element and the viewport are aligned together.
The third one takes a single object literal as argument, allowing us to specify a number of various configurations:
scrollIntoView(options)
options
can take the following properties:
block
specifies the vertical position on the viewport where the element must show up. Can be either'start'
,'center'
,'end'
or'nearest'
. The default is'start'
.inline
— specifies the horizontal position on the viewport where the element must show up. Can be either'start'
,'center'
,'end'
or'nearest'
. The default is'nearest'
.behavior
— same as thebehavior
property of the object passed into the globalscroll()
,scrollTo()
andscrollBy()
methods. The default value is'auto'
.
The block
property configures scrolling in the vertical (i.e. block) direction and works when the given element has a vertical scrollbar. Similarly, the inline
property configures scolling in the horizontal (i.e. inline) direction and works when the element has a horizontal scrollbar.
As for the values of both of these properties, they're also really simple to understand.
'start'
gets the element's starting edge to coincide with the starting edge of the viewport. The 'starting' edge might be different depending on the property we're talking about:
- If we are considering the
block
property,'start'
relates to the vertical axis and thus, gets the element's top edge (which is the starting edge vertically) to coincide with the viewport's top edge. - If we are considering the
inline
property,'start'
similarly relates to the horizontal axis and thus, gets the element's left edge (which is the starting edge horizontally) to coincide with the viewport's left edge.
A similar reasoning can be applied to the values 'center'
and 'end'
.
'nearest'
is a special case. It is somewhere in between 'start'
and 'end'
.
In particular, if the element (on which scrollIntoView()
is invoked) is closer to the 'start'
position, 'nearest'
is equivalent to 'start'
. On the same lines, if the element is closer to the 'end'
position, 'nearest'
is equivalent to 'end'
.
'nearest'
value for the properties block
and inline
isn't supported on many browsers at the time of this writing.Simple?
Alright, it's time for some examples.
In the following code, we use scrollIntoView()
to bring the <h1>
element, shown in the first example above, into view:
var buttonElement = document.querySelector('button');
var h1Element = document.querySelector('h1');
buttonElement.onclick = function() {
h1Element.scrollIntoView();
}
To demonstrate the other forms of scrollIntoView()
, extending this same example, below we pass in false
as an argument in order to get the bottom edges of the <h1>
element and the viewport to coincide with each other:
var buttonElement = document.querySelector('button');
var h1Element = document.querySelector('h1');
buttonElement.onclick = function() {
h1Element.scrollIntoView(false);
}
And now, let's test the third form of the method:
var buttonElement = document.querySelector('button');
var h1Element = document.querySelector('h1');
buttonElement.onclick = function() {
// Smoothly bring the element into view and centralize
// it on the viewport.
h1Element.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
Note that sometimes, based on the dimensions of the document or other ancestor elements, it's not entirely possible to align a particular edge of an element with the same edge on the viewport.
For instance, in the following example, the <body>
element has no absolute height and, therefore, no scrollbar while the <h1>
element is just 100px away from the top edge of the document:
<button>Bring heading into view</button>
<h1>This is a heading</h1>
body {
margin: 0;
}
button {
position: absolute;
top: 0;
padding: 15px;
}
h1 {
margin-top: 100px;
background-color: yellow;
height: 100px;
}
var buttonElement = document.querySelector('button');
var h1Element = document.querySelector('h1');
buttonElement.onclick = function() {
h1Element.scrollIntoView();
}
Now, if we try to click the button, the scrollIntoView()
method would still be invoked but there would be no way for the document to be scrolled even by 1px and therefore no way to get the top edges of the <h1>
and the viewport to be aligned with one another.