Reputation: 93
The dropdown menu should open when the button is clicked.
And if you click outside the dropdown it should close again.
I tried this by adding an event listener to the body when the dropdown menu is active and then checking if the click happened outside the dropdown.
How can I write the following code without timeout function and make the dropdown close when clicked outside and not inside?
<div class="dropdown dropdown__open">
Open Dropdown
<div class="dropdown__window">
<a class="dropdown__link">Link Text</a>
<a class="dropdown__link">Link Text</a>
<a class="dropdown__link">Link Text</a>
<a class="dropdown__link">Link Text</a>
</div>
</div>
.dropdown {
position: relative;
cursor: pointer;
user-select: none;
&__window {
position: absolute;
background: $background-alt;
width: 100%;
max-width: 260px;
top: 45px;
z-index: 12;
overflow: hidden;
display: none;
visibility: hidden;
&--active {
display: block;
visibility: visible;
}
> * {
width: 100%;
display: block;
color: $white;
white-space: nowrap;
&:hover {
background: $background;
color: currentColor;
}
}
}
}
let dropdownBtn = document.querySelectorAll('.dropdown__open')
dropdownBtn.forEach((el) => {
let dropdownWindow = el.querySelector('.dropdown__window')
el.addEventListener('click', function () {
dropdownWindow.classList.toggle('dropdown__window--active')
window.setTimeout(function () {
if (dropdownWindow.classList.contains('dropdown__window--active')) {
let clickListener = document.body.addEventListener(
'click',
function (event) {
if (event.target.classList != 'dropdown__link') {
dropdownWindow.classList.remove('dropdown__window--active')
// Buggy clickListener.removeEventListener()
}
}
)
}
}, 1000)
})
})
OR https://codepen.io/Salala2/pen/RwxjOzW
Upvotes: 4
Views: 27181
Reputation: 35
The @Pedam answer work for me but the new problem is when we want access to an element inside the menu, menu is close anyway. So there is this solution from @Arbejdsglæde to add at your code.
Upvotes: 0
Reputation: 31
I was searching for a solution for this problem and got to this post.
I have found a solution that might be usefull for someone else as well.
In my case, i am using JSX where I only render the dropdown if a boolean is true, "opened" and my input field has a reference called inputRef.
The solution:
window.addEventListener("click", (evt)=>{
const eventPath = evt.composedPath();
this.opened = eventPath.includes(this.inputRef);
});
The event composed path will return the array list of elements that are targets of the click event, if my input field is part of that list, I keep the menu open, if not, it will not be rendered at all.
Keep in mind that if you are using vanilla JS you will need to query select your input element and create its instance like in the comments above.
More about event composed path at https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath
Upvotes: 3
Reputation: 416
You can use the window.onclick
. And if you don't want your dropdown to close when each of the dropdown items is clicked, you can specify that as a condition too.
const dropDownButton = document.querySelector('.dropdown__open')
const dropDownWindow = document.querySelector('.dropdown__window')
dropDownButton.addEventListener('click',
()=> dropDownWindow.classList.toggle('dropdown__window--active'))
window.onclick = function(e){
if (!e.target.matches('.dropdown__open') &&
dropDownWindow.classList.contains('dropdown__window--active')){
dropDownWindow.classList.remove('dropdown__window--active')
}
}
Upvotes: 0
Reputation: 216
You can use onBlur()
. But in order to do that you need to add tabindex="0"
to the invoking element i.e the element which is invoking dropdown to display.
const dropdown = document.querySelector('.dropdown');
const dropdownWindow = document.querySelector('.dropdown__window')
dropdown.addEventListener('click', (event) => {
dropdownWindow.classList.toggle('dropdown__window--active');
});
dropdown.addEventListener('blur', (event) => {
dropdownWindow.classList.remove('dropdown__window--active');
});
.dropdown {
position: relative;
cursor: pointer;
user-select: none;
max-width: 260px;
}
.dropdown__window {
position: absolute;
background: #ccc;
box-shadow: 0 10px 40px #1d2021;
width: 100%;
max-width: 260px;
top: 45px;
z-index: 12;
overflow: hidden;
display: none;
visibility: hidden;
}
.dropdown__window--active {
display: block;
visibility: visible;
}
.dropdown__window>* {
font-size: 14px;
cursor: pointer;
display: block;
padding: 1rem 0 1rem 1rem;
font-weight: 400;
text-align: left;
line-height: 1.42857143;
color: #fff;
white-space: nowrap;
}
<div class="dropdown dropdown__open" tabindex="0">
Open Dropdown
<div class="dropdown__window">
<a class="dropdown__link">Link Text</a>
<a class="dropdown__link">Link Text</a>
<a class="dropdown__link">Link Text</a>
<a class="dropdown__link">Link Text</a>
</div>
</div>
Upvotes: 11