In the past when I needed something like a dropdown menu, it was really easy to toggle the appropriate CSS classes with jQuery.
Here is my first attempt to do the same thing without any dependencies:
// create object containing all .dropdown elements
var dropdown = document.getElementsByClassName("dropdown");
// loop through "dropdown"
for (var d = 0; d < dropdown.length; d++) {
// send each element to the "dropdownListen" function to listen for click
dropdownListen(dropdown[d]);
}
function dropdownListen(elem) {
elem.onclick = function(e) {
// go no farther if inside the dropdown was clicked
if (!e.target.matches('.dropdown-menu-item, .dropdown-body, p')) {
// If the current dropdown is not already open, check all of the others and close any that are found
if (!this.classList.contains('shown')) {
// create object containing all .dropdown elements, again
var dropdowns = document.getElementsByClassName("dropdown");
// loop through "dropdowns"
for (var d = 0; d < dropdowns.length; d++) {
var openDropdown = dropdowns[d];
// remove class "shown" if any open dropdown is found
if (openDropdown.classList.contains('shown')) {
openDropdown.classList.remove('shown');
}
}
}
// toggle the class of the dropdown that was clicked
this.classList.toggle('shown');
}
};
}
document.onclick = function(e) {
// close any open dropdowns when clicking anywhere else on the document
if (!e.target.matches('.dropdown-toggle, .dropdown-menu-item, .dropdown-body, p')) {
// create object containing all .dropdown elements, again
var dropdowns = document.getElementsByClassName("dropdown");
// loop through "dropdowns"
for (var d = 0; d < dropdowns.length; d++) {
var openDropdown = dropdowns[d];
// remove class "shown" if any open dropdown is found
if (openDropdown.classList.contains('shown')) {
openDropdown.classList.remove('shown');
}
}
}
}
.button-group {
display: inline-block;
}
.button {
background: lightblue;
border: 1px solid darkblue;
padding: 5px 10px;
display: inline-block;
cursor: default;
}
.dropdown-body {
position: absolute;
border: 1px solid #ddd;
min-width: 100px;
box-shadow: 1px 1px 15px 0px #f0f0f0;
border-radius: 3px;
padding: 3px 0px;
background-color: #fff;
left: auto;
right: 0;
/* Align to Right by default */
}
.dropdown-body .dropdown-menu-item {
padding: 5px;
cursor: default;
display: block;
text-decoration: none;
color: #333;
}
.dropdown-body .dropdown-menu-item:hover {
background-color: #08c;
color: #fff;
}
.dropdown {
position: relative;
}
.dropdown.dropdown-left .dropdown-body {
right: auto;
left: 0;
}
.dropdown .dropdown-body {
display: none;
}
.dropdown.shown .dropdown-body {
display: block;
}
<div class="button-group dropdown">
<div class="dropdown-toggle button">Default Dropdown</div>
<div class="dropdown-body">
<div class="dropdown-menu-item">Item 1</div>
<div class="dropdown-menu-item">Item 2</div>
<div class="dropdown-menu-item">Item 3</div>
<div class="dropdown-menu-item">Item 4</div>
<div class="dropdown-menu-item">Item 5</div>
</div>
</div>
<div class="button-group dropdown dropdown-left">
<div class="dropdown-toggle button">Left Dropdown</div>
<div class="dropdown-body">
<a href="http://google.com" class="dropdown-menu-item" target="_blank">Google.com</a>
<div class="dropdown-menu-item">Item 2</div>
<div class="dropdown-menu-item">Item 3</div>
<div class="dropdown-menu-item">Item 4</div>
<div class="dropdown-menu-item">Item 5</div>
</div>
</div>
<div class="button-group dropdown dropdown-left">
<div class="dropdown-toggle button">Some other content</div>
<div class="dropdown-body">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis, dolore.</p>
</div>
</div>
Features:
- Only one dropdown can be open at a time
- Clicking inside does not close it
- Clicking outside closes any open dropdown
Questions/Concerns:
- Possible unnecessary duplication of code
- You have to individually name every element that could be inside a dropdown to prevent it from closing when clicking inside.
- This is my first attempt at writing JavaScript without a library or framework. Am I on the right track? Am I following common best practices?
shown
class. As for the markup, that is an easy change (I just used divs for this example for some reason). \$\endgroup\$