Drop Click Content

I noticed some problems with the W3 Schools dropdowns if you use their technique for multiple drop-downs.

  • clicking on the head of a drop-down closes that drop-down, but not others.
  • if the drop content opens something like a form, you can't click in it without closing the drop-down

Try those operations. The menu-like drop-downs work, but the form closes if you try to click in any of the boxes.

drop menus

I solved it like this:

drop click

Basic Idea:

  • enclose the material that you want to stay open if it's clicked in with .dropClickContent instead of .dropContent
  • in the window click, check to see if the click is inside a .dropClickContent
  • if so, ignore the click

Also, to allow other headers to close the dropcontent, I switched from toggleClass to closeAll and then addClass

Here's what the HTML looks like for a regular drop-down (that closes on a click):

<li><button class="dropBtn" type="button">Brave</button>
    <ul class="dropContent">
        <li><button id="btnHarry" type="button">Harry</button></li>
        <li><button id="btnRon" type="button">Ron</button></li>
        <li><button id="btnHermione" type="button">Hermione</button></li>
    </ul>
</li>

Here's the HTML for the drop-down form, which doesn't close on a click. You can close the form by clicking outside it, or on its header:

<li><button class="dropBtn" type="button">Form</button>
    <div class="dropClickContent">
        <form>
            <p><label>What is your name?
                    <input type="text" name="name"></label></p>
            <p><label for="quest">What is your quest?</label></p>
            <textarea id="quest" name="quest" rows=5 cols=60></textarea>
            <p>
            <label>What is your favorite color?
                <select name="color">
                    <option>blue</option>
                    <option>yellow</option>
                </select>
            </label></p>
        </form>
    </div>
</li>

As you can see, the drop-down element is marked with dropClickContent instead of dropContent and the toggles are dropBtn.

The JS/JQ code for the drop buttons is like this.

$("nav").on('click', '.dropBtn', function (evt) {
    console.log("clicked on "+$(evt.target).text());
    var sib = $(evt.target).next().one();  // the next sibling
    var shown = sib.hasClass('show');      // whether it's shown
    closeAllDropDowns();
    // open our dropdown, if not shown
    if(!shown) {
        sib.addClass('show');
    }
});

Notice that this is a delegated handler; a single function on the nav element handles all of the .dropBtn elements. The evt.target element is the actual button that was clicked. The handler finds the next sibling of the button, sib, which is the drop-down element. The handler then finds out whether the sibling is hidden or not. The handler closes all drop-downs and then opens this one, if desired.

Here's how closeAllDropDowns works:

function closeAllDropDowns() {
    console.log("closing all dropdowns");
    $(".dropContent,.dropClickContent").removeClass('show');
}

Finally, to allow clicking anywhere outside the dropContent to close any open drop-downs:

// This closes all drop-downs if you 
// click anywhere outside the dropContent
// Same effect as W3 Schools, but 
// uses jQuery for brevity
// Also checks that we are not 
// in a dropClickContent element

$(window).click(function (evt) { 
    var dcc = $(evt.target).closest(".dropClickContent");
    var inDcc = (dcc.length === 1);
    if (!evt.target.matches('.dropBtn') &&
        !inDcc) {
       closeAllDropDowns();
    }
});