skip to Main Content

I would like to toggle the visibility of some element when its associated button is clicked. Additionally, I’d like for the visible element to close when a click occurs anywhere outside of it. Any links inside the visible element should be able to be clicked as well. This is for a Shopify theme, not sure if that makes a difference or not.

I’ve also tried using a focusout event listener on the element, but that had the same issues.

Javascript

const selectors = {
  mobileDownBtns: document.querySelectorAll('.mobile-nav__down-btn'),
  mainDownBtns: document.querySelectorAll('.main-nav-list__down-btn'),
  mainParent: document.querySelectorAll('.main-nav-parent-item'),
  spotifyBtn: document.querySelector('#spotifyBtn')
}

selectors.spotifyBtn.addEventListener('click', toggleElement);

selectors.mainDownBtns.forEach(function(btn) {
  btn.addEventListener('click', toggleElement);
});

function toggleElement(event) {
  const btn = event.currentTarget;
  const elem = btn.nextElementSibling;
  const container = btn.parentElement;

  if (elem.classList.contains('show')) {
    btn.setAttribute('aria-expanded', 'false');
    elem.classList.remove('show');
    container.classList.remove('isActive');
  } else {
    btn.setAttribute('aria-expanded', 'true');
    elem.classList.add('show');
    container.classList.add('isActive');
  }
}

window.addEventListener('mousedown', function(e) {
  const container = document.querySelector('.isActive');
  const btn = container.querySelector('button');
  const child = container.querySelector('.show');

  if (e.target != container && e.target.parentNode !== container) {
    container.classList.remove('isActive');
    btn.setAttribute('aria-expanded', 'false');
    child.classList.remove('show');
  }
});

HTML

<ul class="site-header__icon-list small--hide">
        <li class="site-header__icon-list--item">
          <button aria-label="Check out my spotify playlist" aria-expanded="false" id="spotifyBtn">
            {% include 'alt-icon-music' %}
          </button>

          <div class="site-header__spotify">
            {% include 'spotify' %}
          </div>
        </li>
      </ul>

      <nav role="navigation" class="site-header__main-nav--wrapper small--hide">
        <ul class="main-nav-list">
          {% for link in linklists[section.settings.menu].links %}
            {% if link.links != blank %}
              <li class="main-nav-list__item">
                <div class="main-nav-parent-item">  
                  <button class="main-nav-list__down-btn site-header__icon" aria-controls="navigation" aria-label="See sub navigation" aria-expanded="false">
                    <span>
                      {{ link.title }}
                    </span>

                    {% include 'alt-icon-chevron-down' %}
                  </button>

                  <div class="main-nav-list__dropdown">
                    <ul class="main-nav-list__child-list{% if link.links.size > 7 %} long{% endif %}">
                      {% for childlink in link.links %}
                        <li class="main-nav-list__item child-item">
                          <a href="{{ childlink.url }}">
                            {{ childlink.title }}
                          </a>
                        </li>
                      {% endfor %}
                    </ul>
                  </div>
                </div>
              </li>
            {% else %}
              <li class="main-nav-list__item">
                <a href="{{ link.url }}">
                  {{ link.title }}
                </a>
              </li>
            {% endif %}
          {% endfor %}
        </ul>
      </nav>

My toggle function works well on its own, but when I add the window event listener, the toggle no longer works as I’d like. The first click on the button works (shows the element) and the window listener works (hides the visible element), but any additional clicks on the button do nothing. Also, although the window listener works to close the visible element, it doesn’t allow for clicks inside the visible element before it closes.

I’m also getting an error in the console

“Uncaught TypeError: Cannot read property ‘querySelector’ of null”

but the correct variable is logged to the console when I use console.log.

I’ve been working on this problem for a couple of days now. I’m still pretty new to javascript, and I’m not really sure what’s going wrong here. Thanks in advance for any help.

2

Answers


  1. document.getElementById('#spotifyBtn')
    

    Use “document.getElementById” instead of “document.querySelector”

    Login or Signup to reply.
  2. i’ve created a similar scenario. the div itself and the items inside it are also clickable. check this code pen link https://codepen.io/anon/pen/OKMNEy

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search