4
\$\begingroup\$

Is there a way to simplify this JavaScript function any further? Having to input 4 variables just for a drop-down menu seems a bit ridiculous, and it is hard to keep track of the variables when I want to have more than 1 button.

<!DOCTYPE html>
<html>
<head>
<style>

.dropbtn:hover, .dropbtn:focus {
    background-color: #3e8e41;
}

.dropdown-content {
    display: none;
    position: absolute;
    background-color: #f9f9f9;
}

.dropdown-content a {
    padding: 12px 16px;
    text-decoration: none;
    display: block;
}

.dropdown a:hover {background-color: #f1f1f1}
.show {display:block;}

</style>
</head>
<body>

<h2>Clickable Dropdown</h2>
<p>Click on the button to open the dropdown menu.</p>

<div class="dropdown">
<button onclick="myFunction('dropdown-content','myDropdown','show','.dropbtn')" class="dropbtn">Dropdown</button>
  <div id="myDropdown" class="dropdown-content">
    <a href="#home">Home</a>
    <a href="#about">About</a>
    <a href="#contact">Contact</a>
  </div>
</div>

<script>
/* When the user clicks on the button, 
toggle between hiding and showing the dropdown content */
function myFunction(dropDownClass,dropDownId,show,dropbtnClass) {
    var dropdowns = document.getElementsByClassName(dropDownClass);
    var i;     
    var openDropdown = dropdowns[i];
document.getElementById(dropDownId).classList.toggle(show);

// Close the dropdown if the user clicks outside of it
window.onclick = function(event) {
  if (!event.target.matches(dropbtnClass)) {
    for (i = 0; i < dropdowns.length; i++) {
      if (openDropdown.classList.contains(show)) {
        openDropdown.classList.remove(show);
      }
    }
  }
}

}
</script>

</body>
</html>
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

First, your code will be easier to read if you separate you JS and your HTML completely. You can add your onclick event on your element this way:

var dropDowns = document.getElementsByClassName('dropdown');
for(var i = 0; i < dropDowns.length; i++){
    dropDowns[i].addEventListener('click', function(e){
        // e contains your click data
    });
}

You might also want to isolate your code by wrapping it in an anonymous function to avoid leaking informations in the global object.

Then, you can deduce everything you're currently passing as parameter from your context.

.dropbtn: this is the button you clicked, which will be sent in your event data as e.target

dropdown-content: as far as I can tell, this will always be the name of your class, so I'd put it in a variable inside your function

dropDowns[i].addEventListener('click', function(e){
   var dropdownClass = 'dropdown-content';
});

myDropdown: this represents the div inside the dropdown element containing the button clicked by the user, which can be written as

e.target.nextElementSibling

since the div is written just after your button

show: again, a simple var in your function is enough

This gives us this:

// namespace variables
(function(){
    // classes used in your HTML
    var dropDownClass = 'dropdown-content';
    var showClass = 'show';
    var dropDowns = document.getElementsByClassName('dropdown');

    // this function will be called on click
    var open = function(e){
        var openDropDown = e.target.nextElementSibling;
        var dropdowns = document.getElementsByClassName(dropDownClass);

        openDropDown.classList.toggle(showClass);

        var closeDropDown = function(event) {
            if (event.target === e.target) {
                return;
            }
            if (openDropDown.classList.contains(showClass)) {
                openDropDown.classList.remove(showClass);
            }
            window.removeEventListener('click', closeDropDown);
        };
        window.addEventListener('click', closeDropDown);
    }

    for(var i = 0; i < dropDowns.length; i++){
        dropDowns[i].children[0].addEventListener('click', open);
    }
}());

Now, you can remove the onclick event and the id attribute in your html and add more dropdowns.

Here is the corresponding Plunker: https://plnkr.co/edit/3mdULmI2QEcWmpDCzXQb?p=preview

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.