I have this code for my website:
function clickMe() {
var element = document.getElementById('about');
element.scrollIntoView({
block: 'start',
behavior: 'smooth',
});
}
This works pretty nice but I have a fixed header so when the code scrolls to the element the header is in the way.
Is there a way to have an offset and make it scroll smoothly?
To use JavaScript scrollIntoView to do smooth scroll with offset, we can call scrollTo with an object that has the top and behavior properties. const element = document.
Native method: scrollIntoView The usage is simple, just like this: function scroll(e) {e. scrollIntoView({behavior: "smooth", block: "start"});}scroll(document. getElementById(YOUR_DIV_ID));
scrollIntoView() method scrolls the current element into the visible area of the browser window. Parameters can be provided to set the position inside the visible area as well as whether scrolling should be instant or smooth.
Is there a way to have an offset and make it scroll smoothly?
#Yes, but not with scrollIntoView()
The scrollIntoViewOptions of Element.scrollIntoView() do not allow you to use an offset. It is solely useful when you want to scroll to the exact position of the element.
You can however use Window.scrollTo() with options to both scroll to an offset position and to do so smoothly.
If you have a header with a height of 30px
for example you might do the following:
function scrollToTargetAdjusted(){
var element = document.getElementById('targetElement');
var headerOffset = 45;
var elementPosition = element.getBoundingClientRect().top;
var offsetPosition = elementPosition + window.pageYOffset - headerOffset;
window.scrollTo({
top: offsetPosition,
behavior: "smooth"
});
}
This will smoothly scroll to your element just so that it is not blocked from view by your header.
Note: You substract the offset because you want to stop before you scroll your header over your element.
#See it in action
You can compare both options in the snippet below.
<script type="text/javascript">
function scrollToTarget() {
var element = document.getElementById('targetElement');
element.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
function scrollToTargetAdjusted() {
var element = document.getElementById('targetElement');
var headerOffset = 45;
var elementPosition = element.getBoundingClientRect().top;
var offsetPosition = elementPosition + window.pageYOffset - headerOffset;
window.scrollTo({
top: offsetPosition,
behavior: "smooth"
});
}
function backToTop() {
window.scrollTo(0, 0);
}
</script>
<div id="header" style="height:30px; width:100%; position:fixed; background-color:lightblue; text-align:center;"> <b>Fixed Header</b></div>
<div id="mainContent" style="padding:30px 0px;">
<button type="button" onclick="scrollToTarget();">element.scrollIntoView() smooth, header blocks view</button>
<button type="button" onclick="scrollToTargetAdjusted();">window.scrollTo() smooth, with offset</button>
<div style="height:1000px;"></div>
<div id="targetElement" style="background-color:red;">Target</div>
<br/>
<button type="button" onclick="backToTop();">Back to top</button>
<div style="height:1000px;"></div>
</div>
window.pageYOffset
have being added, to fix the problem related to @coreyward comments
Søren D. Ptæus's answer got me on the right track but I had issues with getBoundingClientRect()
when not at the top of the window
.
My solution adds a bit more to his to get getBoundingClientRect()
working a bit more consistently with more versatility. I used the approach outlined here and implemented it to get this working as intended.
const element = document.getElementById('targetElement');
const offset = 45;
const bodyRect = document.body.getBoundingClientRect().top;
const elementRect = element.getBoundingClientRect().top;
const elementPosition = elementRect - bodyRect;
const offsetPosition = elementPosition - offset;
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
Codepen Example
Remember to include the polyfill when implementing this!
Simple but elegant solution if the element has a small height (shorter than the viewport):
element.scrollIntoView({ behavior: 'auto' /*or smooth*/, block: 'center' });
The block: center
will scroll the element so the center of the element is at the vertical center of the viewport, so the top header will not cover it.
EDIT 8.5.22: behavior: instant
was used in the past, but removed from browsers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With