Home  >  Q&A  >  body text

Generating content using Ajax - scrolling to Id doesn't work

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.

P粉208469050P粉208469050178 days ago408

reply all(1)I'll reply

  • P粉978551081

    P粉9785510812024-04-05 09:06:23

    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.

    function generateContent(data){
       // code
    
        if (window.location.href.indexOf("#") !== -1) {
            const id = window.location.href.split("#")[1];
            const element = document.getElementById(id);
            if (element) {
                scrollToLoop(id);
            }
        }
    }
    generateContent();
    
    let isUserScrolling = false;
    window.addEventListener("wheel", function () {
        isUserScrolling = true;
    });
    window.addEventListener("touchmove", function () {
        isUserScrolling = true;
    });
    function scrollToLoop(id, scrl) {
        let scroll = scrl;
        const element = document.getElementById(id);
    
        if (element) {
            if (!isInViewport(id) && scroll && !isUserScrolling) {
                scrollToElement(id);
                setTimeout(() => {
                    if (!isInViewport(id)) {
                        scrollToLoop(id, true);
                    }
                }, 500);
            } else {
                setTimeout(() => {
                    if (!isInViewport(id) && scroll && !isUserScrolling) {
                        scrollToLoop(id, true);
                    }
                }, 500);
            }
        }
    }

    reply
    0
  • Cancelreply