How can I access elements when I use *csi.min.js* in JavaScript?

Viewed 88

I want to animate my navbar using JavaScript. I have created separate navbar.html and included it using csi.min.js. When I try to getElementById my navbar, show and hide button. it returns null and when try it on dev console it works.

navbar.html:

<nav>  
    <div class="site-mobile-menu collapse navbar-collapse show" id="main-navbar">
        navbar content
    </div>
<nav>

index.html:

<div data-include="/src/navbar.html"></div>


<script src="/src/js/navbar.js"></script>

navbar.js:

window.addEventListener("load", function() {
    // debugger;

    var mobNav = document.getElementById("main-navbar");
    var showNavBtn = document.querySelector("#show-nav");
    var hideNavBtn = document.getElementById("hide-nav");
    console.log(mobNav + " " + showNavBtn + " " + hideNavBtn);

    if (!mobNav == null || !showNavBtn == null || !hideNavBtn == null) {
        showNavBtn.onclick = function() {
            console.log("clicked");
            mobNav.classList.add("nav-shown");
        }
    } else {
        console.log("Error Opening Mobile Navbar");
    }
}, false);
1 Answers

When the window load, the elements such as #main-navbar are not in the DOM yet, csi adds them later.

Ideally, csi would give an option to notified when a [data-include] element is loaded but it doesn't. I can suggest 2 ways:

Re-implementing csi

// this is csi re-implementation
function fetchTemplates() {
  return Promise.all(
      [...document.querySelectorAll('[data-include]')].map(async el => {
        const response = await fetch(el.getAttribute('data-include'));
        const html = await response.text();
        el.outerHTML = html;
    }));
}

// now you can call fetchTemplates and wait for it to done
(async () => {
  await fetchTemplates()
  console.log('templates are loaded. you can now find #main-navbar');
  console.log(document.querySelector('#main-navbar'));
})();

https://codesandbox.io/s/zealous-jepsen-12ihl?file=/re-implement-csi.html

Use MutationObserver to catch when csi replace the element with the fetched content.

const div = document.querySelector('[data-include="/src/navbar.html"]');
const observer = new MutationObserver(function (mutationsList, observer) {
  const templates = document.querySelectorAll('[data-include]');
  if (templates.length === 0) {
    console.log('all templates loaded, now you can find #main-navbar');
    console.log(document.querySelector('#main-navbar'));
  }
  // we no longer should observe the div
  observer.disconnect();
});
observer.observe(div.parentElement, {
  childList: true,
});

https://codesandbox.io/s/zealous-jepsen-12ihl?file=/index.html

Personally I recommend the first option because

  • It's a very old package implemented with very old API (XMLHttpRequest).
  • If there are a lot of html elements in your site's DOM it might hurt performance.
Related