I generate page content based on data obtained via ajax. The problem I'm having is that when I want to scroll to a certain ID, the scrolling either doesn't happen or it scrolls to the wrong location.
I've been looking at the SO Q&A but haven't found any good solutions. A lot of answers are for Angular or React, but nothing really solid for plain js.
For example, suppose the user clicks a link "About Us -> Project", "About Us" is the page, and the project is located in the id at the end of the page, which contains some content.
The problem occurs when I click on a link from a different page.
Assuming we are on the homepage, we click on the link to the About Us page, Projects section (id project). This is when the scroll doesn't work as expected. If we click on the linked item when we go to the About Us page this problem does not occur.
Since the page data is rendered in javascript, I cannot use the event listeners DOMContentLoaded
or load
since they fire before the content is generated (I think).
Below is my solution that doesn't work. It should check if the id is in the viewport, if not it should scroll.
I don't want to use setTimeout
because it may not always work (slow internet speed, more content (images), etc.)
Any help would be greatly appreciated.
function generate(data){ // code let idx = 0 //just in case, so we don't have an infinite loop if (window.location.href.indexOf("#") !== -1) { const id = window.location.href.split("#")[1]; const element = document.getElementById(id); document.addEventListener('DOMContentLoaded', function () { console.log("loaded"); if (element) { while (!isInViewport(id)) { idx = idx + 1; console.log(isInViewport(id)) scrollToElement(id); if (idx === 10000){ break; } }; } }); } } generateContent(); function scrollToElement(id) { const element = document.getElementById(id); if (element) { element.scrollIntoView({ behavior: 'smooth' }); } } function isInViewport(id) { const element = document.getElementById(id); if (element) { const rect = element.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } }
If I can provide any additional data, please let me know.
Although it's not the most elegant solution so far.
If anyone can find a better way, please let me know.
EDIT: Changed the code slightly to eliminate the issue of programmatic scrolling even after the user scrolls.