skip to Main Content

I am working on a navbar that has several instances of dropdowns in it. We are trying to disable page scrolling whenever the dropdown is open and enable it back again when the dropdown is enabled.

Currently we have the following script, which works perfectly:

<script>
  const resourcesElem = document.getElementById("resources");
  const locationElem = document.getElementById("location");

  const observer = new MutationObserver(mutations => {
    mutations.forEach(mutation => {
      if (mutation.attributeName === "class") {
        if (resourcesElem.classList.contains("w--open") || locationElem.classList.contains("w--open")) {
          document.body.style.overflow = "hidden";
        } else {
          document.body.style.overflow = "visible";
        }
      }
    });
  });

  const config = { attributes: true, attributeFilter: ["class"] };
  observer.observe(resourcesElem, config);
  observer.observe(locationElem, config);
</script>

Basically what this script does is uses MutationObserver to check changes in both dropdowns, and if they have the class "w–open", it means that the dropdown menu is open and then it disables page scrolling. Whenever the dropdown is closed, the class "w–open" is removed and therefore, the page scroll is enabled back again.

However we would like to make this more dynamic, as we expect we would want to increase the number of dropdown in the navbar in the future and we would like to avoid updating manually the script to check all the elements.

As far as I am concerned, MutationObserver has issues checking several instances of the same element at the same time. I’ve tried making it check changes in the child elements (dropdowns) of a parent element (div) but with no luck.

I have also tried an eventListener to trigger on click of any dropdown, but this approach does not seem to work as if you click on any of the dropdown, the page scroll gets disabled but if you then click on the other dropdown, the dropdown still gets opened but the page scroll is enabled. This seem to happen because MutationObserver is checking individually both elements and one disables page scroll while the other is enabling it.

Question is: Is there a way to make the script work but making it easy to scale? For example we can instead of check for ID, check for attribute, which would make things easier if adding a new dropdown. However this approach seem to break MutationObserver as it only checks one instance.

Maybe is there another approach to solve this?

Thanks,

2

Answers


  1. It sounds like you could achieve this by listening for a click on the parent element that contains the dropdowns (which will bubble up from dropdown interactions). Instead of checking each dropdown to see if it has that class, you should be able to just use if(parent.querySelector('w--open')) to determine whether there are any instances of an open dropdown in the parent – if that returns an element, you would disable page scroll.

    Login or Signup to reply.
  2. You can assign a common class to all your dropdowns. Then you can query the document for that class. Then loop through each dropdown and apply the observer to it.

    <script>
      const dropdowns = document.querySelectorAll('.dropdown-toggle');
    
      const observer = new MutationObserver(mutations => {
        let isAnyDropdownOpen = false;
        dropdowns.forEach(dropdown => {
          if (dropdown.classList.contains("w--open")) {
            isAnyDropdownOpen = true;
          }
        });
    
        document.body.style.overflow = isAnyDropdownOpen ? "hidden" : "visible";
      });
    
      const config = { attributes: true, attributeFilter: ["class"] };
      dropdowns.forEach(dropdown => observer.observe(dropdown, config));
    </script>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search