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
Use “document.getElementById” instead of “document.querySelector”
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