Search code examples
javascripthtmlcssscroll

on redirect scroll to id (in url)


I've been having trouble scrolling to the top of the content with a specific id when the page first loads.

I'm using scroll-margin-top which works when you click on a different id/hash, but it doesn't work when the page is first loaded and it should scroll down to the top of a specific content. What it does is scroll, so the content is in view, but not on top of the page (to elaborate, it scrolls so the content is in the middle of the screen and not on top + 170px sticky height).

The page has some pictures and I've removed the lazy-loading, so that is not the problem.

As I don't know what is causing the problem, I've tried implementing javascript to load to the top of the content on 'load' and 'DOMContentLoaded'. Neither work as expected. To be fair, it works even worse then the css scroll. CSS scroll at least puts the content into viewport, but javascript scrolls to a "random" location.

When using console.log on 'load' and 'DOMContentLoaded', and using console.log in dev tools once the content is loaded, 'getBoundingClientRect().top' return different values.

If someone has a CSS solution to this, I would much appreciated it. Otherwise if anyone has a suggestion for the javascript it will also be most welcome.

js:

//load doesn't work as expected either
window.addEventListener('DOMContentLoaded', function() {
    if (window.location.hash) {
      var target = document.querySelector(window.location.hash);
      
      if (target) {
        var offset = target.getBoundingClientRect().top + 170; //170 is sticky header height
        console.log(target.getBoundingClientRect().top);
        
        // Scroll to the target element
        window.scrollTo({
          top: offset,
          behavior: 'smooth'
        });
      }
    }
  });

Once the content is "really" loaded, using console.log(target.getBoundingClientRect().top) returns a different value then the console.log in the function above (when scroll is on top of the page).

If I can provide any additional info, let me know.


Solution

  • Edit

    New answer:

    I don't know why it didn't work before, but now the window load event listener works and scrolls to the correct section of the page.

    The new correct (and way shorter and less complicated) answer is

    
    window.addEventListener('load', () => {
        if (window.location.hash) {
            const target = document.querySelector(window.location.hash);
    
            if (target) {
                target.scrollIntoView({
                    behavior: 'smooth',
                });
            }
        }
    });
    

    Old answer:

    While it's not the most elegant solution, it does work... it keeps on scrolling until the element is at the top of the screen ( + sticky header height) and 200ms has passed (as that should be enough time for the images to load).

    window.addEventListener('DOMContentLoaded', function () {
        if (window.location.hash) {
            // Get the target element using the anchor value
            var target = document.querySelector(window.location.hash);
    
            if (target) {
    
                var posInterval = setInterval(function () {
                    areWeThereYet(target);
                }, 10);
    
                var counter = 0;
                // how long do we intend to wait before we are happy/sure the position is correct (20 * 10)
                var wait = 20;
    
                function areWeThereYet(target) {
                    counter = counter + 1;
                    if (target.getBoundingClientRect().top !== 170) {
                        var current = document.documentElement.scrollTop || document.body.scrollTop;
                        var posY = current + target.getBoundingClientRect().top - 170;
                        scrollToPosY(posY);
                    }
                    if (target.getBoundingClientRect().top === 170) {
                        if (counter > wait) {
                            clearInterval(posInterval);
                        }
                    } else if (isScrollAtPageBottom) {
                        if (counter > wait) {
                            clearInterval(posInterval);
                        }
                    }
                }
    
                function scrollToPosY(posY) {
                    window.scrollTo({
                        top: posY,
                        //behavior: 'smooth'
                        behavior: 'auto'
                    })
                }
    
                function isScrollAtPageBottom() {
                    const windowHeight = window.innerHeight;
                    const documentHeight = Math.max(
                        document.body.scrollHeight, document.documentElement.scrollHeight,
                        document.body.offsetHeight, document.documentElement.offsetHeight,
                        document.body.clientHeight, document.documentElement.clientHeight
                    );
    
                    const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
    
                    return scrollPosition >= documentHeight - windowHeight;
                }
            }
        }
    });