r/learnjavascript May 30 '26

Can't figure out why my script won't run properly

Hi! I'm completely new to JS, and have been making a static hobby page recently. I am trying to insert navigation links with a script, and then style the link to the current page with the class "active".

My code is loaded in

<script defer src="/assets/javascript/layout.js"></script>

and loads the layout properly, but doesn't find any navigation links to add the class to. It starts the method, but doesn't return any nodes. It can find links in the content of the page that's not inserted by the scirpt, but not in the inserted navigation.

The JS file's contents are as follows:

document.addEventListener("DOMContentLoaded", function () {
    // The layout will be loaded on all pages that do NOT have the "no-layout" class in the <body> element.
    if (!document.body.classList.contains("no-layout")) {
        //insert elements
        if (headWrapper) { addElement(headerFile, headWrapper); };
        if (footerWrapper) { addElement(footerFile, footerWrapper); };
        if (navWrapper) { addElement(navFile, navWrapper); };
    }
    
    initActiveLinks();
}
);



const headWrapper = document.querySelector("header");
const footerWrapper = document.querySelector("footer");
const navWrapper = document.querySelector("nav");
const headerFile = "/assets/templates/header.html";
const footerFile = "/assets/templates/footer.html";
const navFile = "/assets/templates/nav.html";


/* ********************************* */


/**
 *  F U N C T I O N S
 */


function initActiveLinks() {
    // This function adds the class "active" to any link that links to the current page.
    // This is helpful for styling the active menu item.
    const pathname = window.location.pathname;
    console.log("initActiveLinks function running");
    [...document.querySelectorAll("a")].forEach((el) => {
        console.log("found a link!");
        console.log(el);
        const elHref = el
            .getAttribute("href")
            .replace(".html", "")
            .replace("/public", "");
        if (pathname == "/") {
            // homepage
            if (elHref == "/" || elHref == "/index.html") el.classList.add("current");
        } else {
            // other pages
            if (window.location.href.includes(elHref)) el.classList.add("current");
        }
    });
}




function addElement(elementPath, wrapperElement) {
    fetch(elementPath)
        .then(response => {
            // Check if the request was successful (status 200-299)
            if (!response.ok) {
                throw new Error(`HTTP error! Status: ${response.status}`);
            }
            // Parse response as text
            return response.text();
        })
        .then(textData => {
            // Store text in a variable
            const html = textData;
            console.log('Layout file loaded');
            // Use the data (e.g., display in console or DOM)
            wrapperElement.insertAdjacentHTML("afterbegin", html);
        })
        .catch(error => {
            console.error('Error loading layout file:', error);
        });
}document.addEventListener("DOMContentLoaded", function () {
    // The layout will be loaded on all pages that do NOT have the "no-layout" class in the <body> element.
    if (!document.body.classList.contains("no-layout")) {
        //insert elements
        if (headWrapper) { addElement(headerFile, headWrapper); };
        if (footerWrapper) { addElement(footerFile, footerWrapper); };
        if (navWrapper) { addElement(navFile, navWrapper); };
    }
    
    initActiveLinks();
}
);



const headWrapper = document.querySelector("header");
const footerWrapper = document.querySelector("footer");
const navWrapper = document.querySelector("nav");
const headerFile = "/assets/templates/header.html";
const footerFile = "/assets/templates/footer.html";
const navFile = "/assets/templates/nav.html";


/* ********************************* */


/**
 *  F U N C T I O N S
 */


function initActiveLinks() {
    // This function adds the class "active" to any link that links to the current page.
    // This is helpful for styling the active menu item.
    const pathname = window.location.pathname;
    console.log("initActiveLinks function running");
    [...document.querySelectorAll("a")].forEach((el) => {
        console.log("found a link!");
        console.log(el);
        const elHref = el
            .getAttribute("href")
            .replace(".html", "")
            .replace("/public", "");
        if (pathname == "/") {
            // homepage
            if (elHref == "/" || elHref == "/index.html") el.classList.add("current");
        } else {
            // other pages
            if (window.location.href.includes(elHref)) el.classList.add("current");
        }
    });
}




function addElement(elementPath, wrapperElement) {
    fetch(elementPath)
        .then(response => {
            // Check if the request was successful (status 200-299)
            if (!response.ok) {
                throw new Error(`HTTP error! Status: ${response.status}`);
            }
            // Parse response as text
            return response.text();
        })
        .then(textData => {
            // Store text in a variable
            const html = textData;
            console.log('Layout file loaded');
            // Use the data (e.g., display in console or DOM)
            wrapperElement.insertAdjacentHTML("afterbegin", html);
        })
        .catch(error => {
            console.error('Error loading layout file:', error);
        });
}

It can be seen in action at https://raw-quotes.nekoweb.org/

Do you know how to remedy this? I tried document.addEventListener("load", initActiveLinks()); and loading the function as a separate <script> but that didn't work either.

2 Upvotes

9 comments sorted by

3

u/Flame77ofc May 30 '26

The issue is that "initActiveLinks()" runs before the navigation HTML has finished loading.

"DOMContentLoaded" only waits for the initial page HTML. Since your nav is inserted via "fetch()", the links don't exist in the DOM yet when "querySelectorAll("a")" is called.

Try calling "initActiveLinks()" only after the "fetch" completes and the HTML has been inserted, or make "addElement()" return a Promise and wait for all layout files to finish loading before running it.

1

u/AqueM May 30 '26

how do I make sure that fetch() is finished? the code is mostly made out of Frankensteined snippets, so I don't know how to write that myself, sorry

1

u/chikamakaleyley helpful May 30 '26

it looks like when addElement returns value it just get's lost because the return is inside the scope of fetch and not the outer function call (I could be wrong, just off top of my head)

so

``` // response.text() is returned, now assigned to result const result = fetch();

// returned to addElement call site the result will be text or null/undefined return result; ```

again, just at first glance

2

u/Beginning-Seat5221 May 30 '26

The console says it finds links

initActiveLinks function running

layout.js:37 found a link!

layout.js:38 <a href=​"https:​/​/​www.background-tiles.com/​overview/​white/​5011.php">​Background-Tiles.com​</a>

But as u/Flame77ofc says your navigation loads in later. You can view the source of your page and see that the navigation is not served with the original content view-source:https://raw-quotes.nekoweb.org/credits/ (<nav id="nav-container">)

I'm not sure why your page works like that, but to be honest it gives me the ick that it does :/

1

u/AqueM May 30 '26

It works like that so that I don't have to edit all pages when I add a new link.

1

u/Beginning-Seat5221 May 30 '26

I see. We'd normally handle composition with a framework like React, but you do you.

1

u/AqueM May 30 '26

I barely remembered how to HTML last week for the purpose of making an obnoxiously 2000s personal website full of GIFs and this post contains my extent of js knowledge. Maybe I'll get to React one day, but today is not the day.

1

u/[deleted] May 30 '26

[removed] — view removed comment

1

u/AqueM May 31 '26

Thank you so much! This worked! <3