skip to Main Content

I created a dropdown menu, which is activated and closed by setting display to block or none, depending on the state of a checkbox.

Everything is working as it is supposed to, but I want the menu to close when I click on the checkbox, which works by default or when I click anywhere else, which also works with my script.

BUT: I am not able to accomplish both. My script works fine, but then I am unable to close the menu by clicking on the checkboxes label.

window.onclick = function(unchceck) {
  let button = document.getElementById("toggle_button");

  if (unchceck.target != button) {
    button.checked = false;
  }
}
/* highly simplified version of the CSS, just to show the function */

.expM {
  display: none;
  }
#toggle_button {
  display: none
  }
#toggle_button:checked~.expM {
  display: block; 
  }
li {
  background-color: rgb(70, 70, 70);
  }
<!-- my checkbox with a label, the checkbox is set to display: none -->
<input type="checkbox" name="test" id="toggle_button">
<label for="toggle_button">
  <img id="menuBurger" src="https://picsum.photos/20"/>
</label>


<!-- simplyfied version of my menu -->
<ul class="expM">
  <li><a class="menuLink" href="index.html">Home</a></li>
  <li><a class="menuLink" href="pages/GetStarted.html">Get Started</a></li>
</ul>

I tried multiple solutions, but none them worked. They resulted in me not being able to check the checkbox anymore or in the original premisse, that I was only able to achieve one of the two ways to uncheck the box.

Some things I tried were setting a cooldown/ timeout for the checkbox, which also did not work. I also tried to stop the function as soon, as checked = false, which worked neither.

You would help me a lot with a clean solution, making it possible to close the menu by clicking on the checkbox and by clicking anywhere on the screen.

3

Answers


  1. i think the problem is:

    u click the checkbox lable -> the checkbox deactivates itself and the script activates it again because its checking if only the checkbox isnt clicked.

    so use this to fix it:

    window.onclick = function(e) {
      let button = document.getElementById("toggle_button");
      let lable_img = document.getElementById("YOUR IMG ID HERE");
    
      if (e.target != lable_img && e.target != button) {
        button.checked = false;
      }
    }
    

    (add an id to your img and replace "YOUR IMG ID HERE" with the img id)

    Login or Signup to reply.
  2. Code for closing your "menu" for any click outside it, without disturbing the checkBox click event :

    PS: your code was close but you neglected to deal with bubbles events

    window.addEventListener('click', ({target}) =>
      {
      // check for all possible elements
      if (!target.matches('#toggle_button, label[for="toggle_button"], #menuBurger'))
        document.querySelector('#toggle_button').checked = false;
      })
    #toggle_button,
    #toggle_button:not(:checked) ~ .expM { 
      display: none; 
      }
    li { 
      background-color: #464646; 
      }
    <!-- my checkbox with a label, the checkbox is set to display: none -->
    <input type="checkbox" name="test" id="toggle_button">
    <label for="toggle_button">
      <img id="menuBurger" src="https://picsum.photos/20"/>
    </label>
    
    
    <!-- simplyfied version of my menu -->
    <ul class="expM">
      <li><a class="menuLink" href="index.html">Home</a></li>
      <li><a class="menuLink" href="pages/GetStarted.html">Get Started</a></li>
    </ul>
    Login or Signup to reply.
  3. If I understood correctly, the OP needs the menu to close/open according to the associated checkbox status (unchecked/checked), and in addition should the user click outside of the menu, then the default behavior is to uncheck the checkbox. The solution provided works for one or more menus, details are commented in the example.

    // Define a boolean flag.
    let ignore = false;
    
    // Collect all elements with the className of ".excluded" into a NodeList.
    const excluded = document.querySelectorAll(".excluded");
    
    /* 
    For each ".excluded" element, bind each to the "mouseover" and "mouseout" 
    events. Whenever the user's cursor is inside an ".excluded" element, the 
    "ignore" flag is true, otherwise, if the cursor is outside of all "excluded" 
    elements, the "ignore" flag is false. 
    */
    excluded.forEach(exc => {
      exc.addEventListener("mouseover", function(event) {
        ignore = true;
      });
    });
    
    excluded.forEach(exc => {
      exc.addEventListener("mouseout", function(event) {
        ignore = false;
      });
    });
    
    /* 
    Bind the "click" event to the entire Document Object. Whenever the user 
    clicks anything that isn't ".excluded", all checkboxes (.cbox) are 
    collected into a NodeList and then unchecked. 
    */
    document.addEventListener("click", function(event) {
      if (!ignore) {
        document.querySelectorAll(".cbox").forEach(c => c.checked = false);
      }
    });
    /* 
    Optional. 
    IMO a checkbox doesn't need to be visible* when the label serves as the
    interface.
    * Unless accessibility is a concern.
    */
    
    .cbox {
      display: none
    }
    
    .icon {
      display: block;
      margin: 0;
      padding: 0;
      cursor: pointer
    }
    
    
    /* 
    "display: none" is still a viable option.
    "visibility: hidden" is used instead so any surrounding elements will 
    not shift. 
    */
    
    .list {
      margin: 0;
      padding-top: 0.5rem;
      padding-bottom: 0.5rem;
      padding-right: 1rem;
      visibility: hidden;
      background: #999
    }
    
    
    /* 
    Pseudo-class ":checked" indicates when a checkbox or radio button is 
    checked.
    Adjacent combinator: "+" indicates what must occur immediately after the
    selector that is to the left of a "+".
    Summary: 
      - If a checkbox (.cbox) is checked (:checked)
      - and it is followed (+) by a label (.icon)
      - make the menu (.list) that follows (+) the label (.icon) visible 
    */
    
    .cbox:checked+.icon+.list {
      visibility: visible;
    }
    
    
    /* 
    This is the class given to any element that you need to exclude from 
    being considered outside the menu and labels. 
    An outline is added for demo purposes. 
    */
    
    .excluded {
      width: max-content;
      max-height: min-content;
      outline: 3px red dashed;
    }
    <!-- 
    Match a checkbox "id" with a label "for" and they will be associated
    with each other. If the label is clicked, the associated checkbox will
    be checked or unchecked. 
    It's important to keep in mind that each "id" MUST BE UNIQUE. But it is 
    perfectly valid to have multiple labels with identical "for".
    -->
    
    <input id="c1" class="cbox" type="checkbox">
    <label for="c1" class="icon excluded">
        <img src="https://picsum.photos/20/">
      </label>
    <menu class="list excluded">
      <li>
        <a href="https://example.com">Example</a>
      </li>
      <li>
        <a href="https://stackoverflow.com">Stackoverflow</a>
      </li>
    </menu>
    
    <input id="c2" class="cbox" type="checkbox">
    <label for="c2" class="icon excluded">
        <img src="https://picsum.photos/20/">
      </label>
    <menu class="list excluded">
      <li>
        <a href="https://example.com">Example</a>
      </li>
      <li>
        <a href="https://stackoverflow.com">Stackoverflow</a>
      </li>
    </menu>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search