0

What I've tried:

I'm almost expecting this to be flagged as a duplicate, but the first ten possible duplicate solutions did not work for me. I've tried unbind(), event.stopPropagation() and event.stopImmediatePropagation() (however, my understanding of the function isn't the best), but my .click() function fires three times in the overlayClicked() function.

What I have:

I have three <div>s that make up the body so far: the navigation drawer, the menu handle, and the overlay.

The handle is place relatively to the the drawer so that when the drawer opens and closes, it moves with it. Only when the handle is clicked, using a <label> with a hidden <input type="checkbox">. When the handle is clicked, (and this part works fine) it calls handleClicked(cb) which then either opens or closes the drawer, and sets the third <div>, the overlay, from display: none to display: block and fades it in.

The part that doesn't work is I also have another function, overlayClicked(), that obviously, when the overlay is clicked, I want it to click the checkbox from above, close the drawer, and again hide the overlay. (Hopefully this all makes sense so far).

function handleClicked(cb){
    if (cb.checked){
        $(".navbar").css("left", "0px");
        $(".overlay").fadeToggle(500);
    }else{
        $(".navbar").css("left", "-202.77px");
        $(".overlay").fadeToggle(500);
    }
    console.log("handle");
}

function overlayClicked(){
    $("#handle").click();
}
:root {
   --light-green-rgb: 142, 167, 128;
   --light-green-hex: #8EA780;
   --green-rgb: 91, 110, 82;
   --green-hex: #5B6E52;
   --dark-green-rgb: 37, 52, 29;
   --dark-green-hex: #25341D;
}

.overlay {
   position   : fixed;
   top        : 0;
   left       : 0;
   width      : 100%;
   height     : 100%;
   background : #000;
   opacity    : 0.6;
   filter     : alpha(opacity=60);
   z-index    : 998;
   display    : none;
   }

.navbar {
    position: fixed;
    left: -202.77px;
    z-index: 999;
    width: 200px;
    height: 100%;
    background-color: rgba(91, 110, 82, 0.9);
    border-right: 2.77px solid var(--dark-green-hex);
    transition: left .5s;
}

label.navbar-handle {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    float: right;
    cursor: pointer;
    display: block;
}

.navbar-handle-div {
    position: relative;
    right: -209.77px;
    top: 7px;
    border: 3px double var(--dark-green-hex);
    background-color: var(--green-hex);
    width: 45px;
    height: 45px;
}

.navbar-handle-line {
    margin-left: auto;
    margin-right: auto;
    display: block;
    width: 80%;
    height: 6.28%;
    border: 2px solid var(--dark-green-hex);
    cursor: pointer;
}

.navbar-handle-space {
    margin-left: auto;
    margin-right: auto;
    display: block;
    width: 80%;
    height: 14.28%;
    cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="overlay" onclick="overlayClicked()"></div>
        
        <div class="navbar">
            
            <div class="navbar-handle-div">
                <label for="handle" class="navbar-handle">
                    <div class="navbar-handle-space"></div>
                    <div class="navbar-handle-line"></div>
                    <div class="navbar-handle-space"></div>
                    <div class="navbar-handle-line"></div>
                    <div class="navbar-handle-space"></div>
                    <div class="navbar-handle-line"></div>
                    <div class="navbar-handle-space"></div>
                </label>
                <input style="display: none;" id="handle" type="checkbox" onclick="handleClicked(this)"/>
            </div>
            
        </div>

Expected behavior:

Obviously, I want to click the overlay, and have it call overlayClicked(), with it simply unchecking the checkbox, closing the drawer, and fading the overlay out.

Actual behavior:

As you can see, when handleClicked() is called, I log "handle" to the console, and when I click on the overlay, it logs "handle" three times, closes the drawer, fades the overlay out, and then it flashes the overlay, fading it in, and then fading it out, without opening the drawer again. I'd imagine that's simply because I'm setting the left to 0, and then setting it to hidden again, so that's not really my concern.

Ethan Moore
  • 383
  • 1
  • 5
  • 18
  • 1
    @CalebTaylor Capturing should rarely (if ever) be used. A better solution is to just call `stopPropagation()` in the lowest handler to prevent further bubbling. – Scott Marcus Aug 22 '20 at 16:54
  • @ScottMarcus Good point, thanks for correcting me – Caleb Taylor Aug 22 '20 at 16:54
  • @ScottMarcus Where would I add the `stopPropagation()` call, then? I tried it at the top and bottom of both functions. – Ethan Moore Aug 22 '20 at 16:55
  • I haven't really looked over your code, but you could try adding the event argument to `overlayClicked` and add the call there: `function overlayClicked(event){ event.stopPropagation(); $("#handle").click(); }` – Scott Marcus Aug 22 '20 at 16:58
  • I feel kinda dumb not realizing I had to define the event, but that gave me an error: `Cannot read property 'stopPropagation' of undefined` – Ethan Moore Aug 22 '20 at 17:00
  • Yeah, as I said, I hadn't looked over your code that much and that function isn't directly being called from an event, so you get that error. But, I just tried your code as is and it seems to work just fine. When I click the overlay, I get one message in the console, not 3 and then the overlay fades out. – Scott Marcus Aug 22 '20 at 17:02
  • We don't debug live sites, but please add the relevant CSS to your question because the size and position of the overlay is probably what's making it work here and not in your actual code. – Scott Marcus Aug 22 '20 at 17:05
  • added css to my question – Ethan Moore Aug 22 '20 at 17:11
  • Hmm. Well, as you can see right here in your question, it seems to work as it should. – Scott Marcus Aug 22 '20 at 17:16
  • Yeah, I saw that... That's pretty frustrating. :P – Ethan Moore Aug 22 '20 at 17:18

2 Answers2

1

It is not the best thing to fire the event in code like this :

$("#handle").click();

instead try to call the handler and stop event propagation after that:

<div class="overlay" onclick="overlayClicked(event)"></div>
function handleClicked(cb){
    if (cb.checked){
        $(".navbar").css("left", "0px");
        $(".overlay").fadeToggle(500);
    }else{
        $(".navbar").css("left", "-202.77px");
        $(".overlay").fadeToggle(500);
    }
    console.log("handle");
}

function overlayClicked(event){
    handleClicked($("#handle"))
    if (event.stopPropagation){
       event.stopPropagation();
   }
   else if(window.event){
      window.event.cancelBubble=true;
   }
}

See also this answer: https://stackoverflow.com/a/5108218/2109287

And this on events bubbling: https://javascript.info/bubbling-and-capturing

EDIT:

I had to add a .prop call to make sure the checkbox is unchecked so that it doesn't reverse expected behavior.

function overlayClicked(event){
    handleClicked($("#handle"))
    $("#handle").prop("checked", false);
    if (event.stopPropagation){
       event.stopPropagation();
   }
   else if(window.event){
      window.event.cancelBubble=true;
   }
}
  • This worked! I figured it was bad practice to just call a `.click` and I know know why. Thanks – Ethan Moore Aug 22 '20 at 17:29
  • Just shout out to my edit: I had to add the `.prop` call to make sure the checkbox gets unchecked when clicking the overlay or else it was flipping the fade/drawer behavior. – Ethan Moore Aug 22 '20 at 17:34
  • No need to check for `window.event` if you use the event object argument – charlietfl Aug 22 '20 at 17:41
  • @charlietfl I believe this is only needed for IE < 9 , see this answer [link](https://stackoverflow.com/a/18309741/2109287) – Mustafa Naser Aug 23 '20 at 19:22
0

You can just pass a handler method into your click event registration. Here:

function overlayClicked(){
    $("#handle").click( event => event.stopPropagation() );
}
Mosia Thabo
  • 4,009
  • 1
  • 14
  • 24