I have a page where a scroll bar containing table rows with divs in them is dynamically generated from the database. Each table row acts like a link, sort of like you\'d see
I solved this problem by using,
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
This makes the element appear in the center
after scrolling, so I don't have to calculate yOffset
.
Hope it helps...
I've taken the wonderful solution of Steve Banton and wrote a function that makes it more convenient to use. It'd be easier just to use window.scroll()
or even window.scrollBy()
, as I've tried before, but these two have some problems:
Just copy it and mess up with it how ever you want.
import smoothscroll from 'smoothscroll-polyfill';
smoothscroll.polyfill();
const prepareSmoothScroll = linkEl => {
const EXTRA_OFFSET = 0;
const destinationEl = document.getElementById(linkEl.dataset.smoothScrollTo);
const blockOption = linkEl.dataset.smoothScrollBlock || 'start';
if ((blockOption === 'start' || blockOption === 'end') && EXTRA_OFFSET) {
const anchorEl = document.createElement('div');
destinationEl.setAttribute('style', 'position: relative;');
anchorEl.setAttribute('style', `position: absolute; top: -${EXTRA_OFFSET}px; left: 0;`);
destinationEl.appendChild(anchorEl);
linkEl.addEventListener('click', () => {
anchorEl.scrollIntoView({
block: blockOption,
behavior: 'smooth',
});
});
}
if (blockOption === 'center' || !EXTRA_OFFSET) {
linkEl.addEventListener('click', () => {
destinationEl.scrollIntoView({
block: blockOption,
behavior: 'smooth',
});
});
}
};
export const activateSmoothScroll = () => {
const linkEls = [...document.querySelectorAll('[data-smooth-scroll-to]')];
linkEls.forEach(linkEl => prepareSmoothScroll(linkEl));
};
To make a link element just add the following data attribute:
data-smooth-scroll-to="element-id"
Also you can set another attribute as an addtion
data-smooth-scroll-block="center"
It represents the block
option of the scrollIntoView()
function. By default, it's start
. Read more on MDN.
Adjust the smoothScroll function to your needs.
For example, if you have some fixed header (or I call it with the word masthead
) you can do something like this:
const mastheadEl = document.querySelector(someMastheadSelector);
// and add it's height to the EXTRA_OFFSET variable
const EXTRA_OFFSET = mastheadEl.offsetHeight - 3;
If you don't have such a case, then just delete it, why not :-D.
This works for me in Chrome (With smooth scrolling and no timing hacks)
It just moves the element, initiates the scroll, then moves it back.
There is no visible "popping" if the element is already on the screen.
pos = targetEle.style.position;
top = targetEle.style.top;
targetEle.style.position = 'relative';
targetEle.style.top = '-20px';
targetEle.scrollIntoView({behavior: 'smooth', block: 'start'});
targetEle.style.top = top;
targetEle.style.position = pos;
I've got this and it works brilliantly for me:
// add a smooth scroll to element
scroll(el) {
el.scrollIntoView({
behavior: 'smooth',
block: 'start'});
setTimeout(() => {
window.scrollBy(0, -40);
}, 500);}
Hope it helps.
So, perhaps this is a bit clunky but so far so good. Im working in angular 9.
file .ts
scroll(el: HTMLElement) {
el.scrollIntoView({ block: 'start', behavior: 'smooth' });
}
file .html
<button (click)="scroll(target)"></button>
<div #target style="margin-top:-50px;padding-top: 50px;" ></div>
I adjust the offset with margin and padding top.
Saludos!
I add this css tips for those who not resolved this issue with solutions above :
#myDiv::before {
display: block;
content: " ";
margin-top: -90px; // adjust this with your header height
height: 90px; // adjust this with your header height
visibility: hidden;
}