Search code examples
javascriptjqueryhtmldrop-down-menusubmenu

How can I implement `window.onlick = function(event){` to close drop down menu?


I do not think this is a duplicate question. I have tried some of the other solutions found on this website without the resolve I am looking for. How do I detect a click outside an element? for example uses event.stopPropagation(); and does not function with multiple menus correctly for me.

What I am trying to accomplish is, when a user clicks anywhere outside of a drop down menu that is open, the open menu will close.

I like the way the javascript code functions from w3schools.com, it does what I am looking for, however I am missing what I need for input into the window.onclick = function (event){ to adapt it into my code structure.

This is a sample file that I have put together to highlight what I am trying to implement.

<!DOCTYPE html>
<html>

<head>
<style>

li > ul{display:none;}
.show {display:block;}
.sub-nav ul {
    background: #efefef; 
    background: linear-gradient(top, #efefef 0%, #bbbbbb 100%);  
    background: -moz-linear-gradient(top, #efefef 0%, #bbbbbb 100%); 
    background: -webkit-linear-gradient(top, #efefef 0%,#bbbbbb 100%); 
    box-shadow: 0px 0px 9px rgba(0,0,0,0.15);
    padding: 0 20px;
    border-radius: 0px;  
    list-style: none;
    position: relative;
}
.sub-nav ul:after {content: ""; clear: both; display: block;}
li {float: left;}
.sub-nav ul li:hover {
    background: #4b545f;
    background: linear-gradient(top, #4f5964 0%, #5f6975 40%);
    background: -moz-linear-gradient(top, #4f5964 0%, #5f6975 40%);
    background: -webkit-linear-gradient(top, #4f5964 0%,#5f6975 40%);
}
.sub-nav ul li:hover a {color: #fff;}
.sub-nav ul li a {
    display: block; 
    padding: 20px 40px;
    color: #757575; 
    text-decoration: none;
}
.sub-nav ul ul {
    background: #5f6975; 
    border-radius: 0px; 
    padding: 0;
    position: absolute; 
    top: 100%;
}
.sub-nav ul ul li {
    float: none; 
    border-top: 1px solid #6b727c;
    border-bottom: 1px solid #575f6a;
    position: relative;
}
.sub-nav ul ul li a {padding: 15px 40px; color: #fff;}
.sub-nav ul ul li a:hover {background: #4b545f;}

</style>
</head>

<body>

<!-- START: nav -->
<div class="sub-nav">
                <div class="container">

                    <ul>
                        <li class="active"><a href="#">ROOT 1</a></li>
                        <li><a href="#">ROOT 2</a></li>
                        <li><a href="#">ROOT 3</a></li>
                        <li><a href="#">ROOT 4</a></li>
                        <li><a href="#">ROOT 5</a></li>

                        <li id="drop-one"><a href="#">DROP 1</a>
                            <ul>
                                <li class="has-sub"><a href="#">SUB MENU 1</a></li>
                                <li class="has-sub"><a href="#">SUB MENU 2</a></li>
                                <li class="has-sub"><a href="#">SUB MENU 3</a></li>
                                <li class="has-sub"><a href="#">SUB MENU 4</a></li>
                            </ul>
                        </li>


                        <li id="drop-two"><a>DROP 2</a>
                            <ul>
                                <li class="has-sub"><a href="#">SUB MENU LONG TITLE 1</a></li>
                                <li class="has-sub"><a href="#">SUB MENU LONG TITLE 2</a></li>
                                <li class="has-sub"><a href="#">SUB MENU LONG TITLE 3</a></li>
                            </ul>
                        </li>
                    </ul>

                </div>
</div>
<!-- END: nav -->






</body>
</html>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
    jQuery("li:has(ul)").click(function(){
        jQuery("ul",this).toggleClass("show");
    });


window.onclick = function(event) {
    if (!event.target.matches('??????')) {

        var dropdowns = document.getElementsByClassName("??????");
        var i;
        for (i=0; i<dropdowns.length; i++) {
            var openDropdown = dropdowns[i];
            if (openDropdown.classList.contains('show')) {
                openDropdown.classList.remove('show');
            }
        }
    }
}

</script>

To be as specific as I can what should I replace "??????" with to make this script work. If the html code needs to be modified to make this script work please advise, thank you!

window.onclick = function(event) {
    if (!event.target.matches('??????')) {

        var dropdowns = document.getElementsByClassName("??????");
        var i;
        for (i=0; i<dropdowns.length; i++) {
            var openDropdown = dropdowns[i];
            if (openDropdown.classList.contains('show')) {
                openDropdown.classList.remove('show');
            }
        }
    }
}



UPDATE: 12/27/2016 Just to follow up, I was able to come up with what I think are the needed modifications to achieve what was needed.

First I added li, and ul classes "dropdown" and "is-open" respectively.

<li class="dropdown"><a href="#">DROP 1</a>
    <ul class="is-open">
        <li><a href="#">SUB MENU 1</a></li>
        <li><a href="#">SUB MENU 2</a></li>
        <li><a href="#">SUB MENU 3</a></li>
        <li><a href="#">SUB MENU 4</a></li>
    </ul>
</li>


<li class="dropdown"><a href="#">DROP 2</a>
    <ul class="is-open">
        <li><a href="#">SUB MENU LONG TITLE 1</a></li>
        <li><a href="#">SUB MENU LONG TITLE 2</a></li>
        <li><a href="#">SUB MENU LONG TITLE 3</a></li>
    </ul>
</li>

Second I replaced the previous script with:

<script>


    // this is the function caller for click into drop down menu
    $(document).ready(function(){
        // this to function call targets the drop down menu by elements
        $("li:has(ul)").click(function(){
            // (IMPORTANT) code to hide existing open drop down menu before displaying new drop down menu 
            $(".is-open").hide();
            // code to toggle menu from drop down ROOT 
            $(this).find(".is-open").toggle();
        });// END: .click
    });// END: .ready

    //this script closes menu when clicked outside of drop down menu.
    $(document).on("click", function(event){
        var $triggerOn = $(".dropdown");
        if($triggerOn !== event.target && !$triggerOn.has(event.target).length){
            $(".is-open").hide();
        }// END: if statement            
    });// END: .on  



</script>

This has allowed me to implement what I desired in terms of clicking outside of the drop down menu, and the menu closes.

I now have created only one remaining issue with clicking on the "DROP x" root level. The menu will expand on click, but a second click on the root level will no longer close the drop down menu. Any advice to resolving this?

(Note: If I remove $(".is-open").hide(); from the script it restores the click to open and click to close functionality, however when clicking from "DROP 1" to "DROP 2" the drop down menus will stay open.)


Solution

  • This solution implements all the needed components and solves the jQuery scripting error that was present.

    <!DOCTYPE html>
    <html>
    
    <head>
    
    <style>
    
    li > ul{display:none;}
    .show {display:block;}
    .sub-nav ul {
        background: #efefef; 
        background: linear-gradient(top, #efefef 0%, #bbbbbb 100%);  
        background: -moz-linear-gradient(top, #efefef 0%, #bbbbbb 100%); 
        background: -webkit-linear-gradient(top, #efefef 0%,#bbbbbb 100%); 
        box-shadow: 0px 0px 9px rgba(0,0,0,0.15);
        padding: 0 20px;
        border-radius: 0px;  
        list-style: none;
        position: relative;
    }
    .sub-nav ul:after {content: ""; clear: both; display: block;}
    li {float: left;}
    .sub-nav ul li:hover {
        background: #4b545f;
        background: linear-gradient(top, #4f5964 0%, #5f6975 40%);
        background: -moz-linear-gradient(top, #4f5964 0%, #5f6975 40%);
        background: -webkit-linear-gradient(top, #4f5964 0%,#5f6975 40%);
    }
    .sub-nav ul li:hover a {color: #fff;}
    .sub-nav ul li a {
        display: block; 
        padding: 20px 40px;
        color: #757575; 
        text-decoration: none;
    }
    .sub-nav ul ul {
        background: #5f6975; 
        border-radius: 0px; 
        padding: 0;
        position: absolute; 
        top: 100%;
    }
    .sub-nav ul ul li {
        float: none; 
        border-top: 1px solid #6b727c;
        border-bottom: 1px solid #575f6a;
        position: relative;
    }
    .sub-nav ul ul li a {padding: 15px 40px; color: #fff;}
    .sub-nav ul ul li a:hover {background: #4b545f;}
    
    </style>
    
    </head>
    
    <body>
    
    <!-- START: nav -->
    <div class="sub-nav">
                    <div class="container">
    
                        <ul>
                            <li class="active"><a href="#">ROOT 1</a></li>
                            <li><a href="#">ROOT 2</a></li>
                            <li><a href="#">ROOT 3</a></li>
                            <li><a href="#">ROOT 4</a></li>
                            <li><a href="#">ROOT 5</a></li>
    
                            <li class="dropdown"><a href="#">DROP 1</a>
                                <ul class="is-open">
                                    <li><a href="#">SUB MENU 1</a></li>
                                    <li><a href="#">SUB MENU 2</a></li>
                                    <li><a href="#">SUB MENU 3</a></li>
                                    <li><a href="#">SUB MENU 4</a></li>
                                </ul>
                            </li>
    
    
                            <li class="dropdown"><a href="#">DROP 2</a>
                                <ul class="is-open">
                                    <li><a href="#">SUB MENU LONG TITLE 1</a></li>
                                    <li><a href="#">SUB MENU LONG TITLE 2</a></li>
                                    <li><a href="#">SUB MENU LONG TITLE 3</a></li>
                                </ul>
                            </li>
                        </ul>
    
                    </div>
    </div>
    <!-- END: nav -->
    
    </body>
    </html>
    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script>
        // this is the function caller for click into drop down menu
    $(document).ready(function(){
      $("li:has(ul)").click(function(){
        if ($(this).find(".is-open").is(":visible")) {
          $(".is-open").hide();
        } else {
          $(".is-open").hide();
          $(this).find(".is-open").toggle();
        }
      });
    }); 
        //this script closes menu when clicked outside of drop down menu.
        $(document).on("click", function(event){
            var $triggerOn = $(".dropdown");
            if($triggerOn !== event.target && !$triggerOn.has(event.target).length){
                $(".is-open").hide();
            }// END: if statement            
        });// END: .on  
    </script>