2

I have no idea why this is not working. Seems like such a simple concept. I have some classes being toggled when a button is clicked. These classes just control the display of text open menu and close menu. That part is working, however, I need to have some additional scripts run when the button is clicked when its in it's opened state. When I click the button when it has the class opened then nothing in my button.opened click function happens.. it just runs the button.closed function. What am I doing wrong?

https://jsfiddle.net/dmcgrew/sfvhtahq/7/

$("button.closed").on("click", function(){
 console.log("open the menu");
  
  $(this).toggleClass("opened");
  $(this).toggleClass("closed");
});

$("button.opened").on("click", function(){
  //why does nothing in here happen?
 console.log("close the menu");
  
  $(this).toggleClass("opened");
  $(this).toggleClass("closed");
});
.expand {width:100px; height:100px; background:red; display:block;}

.close, .open {display:none;}

.opened .close {display:inline;}
.closed .open {display:inline;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="expand closed"><span class="open">open menu</span><span class="close">close menu</span></button>
Sphinx
  • 10,519
  • 2
  • 27
  • 45
Dustin
  • 4,314
  • 12
  • 53
  • 91
  • 2
    You're trying to add an event to the elements with "opened" before they exist on the DOM – zfrisch Mar 05 '18 at 18:09
  • How many elements have the `"opened"` class *when that code runs*? Because those are the *only* ones that will get the second handler assigned. –  Mar 05 '18 at 18:10
  • 2
    Related. https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements – Taplar Mar 05 '18 at 18:12
  • Why don‘t you apply only one function on one button and handle open/close there? – Nikolaus Mar 05 '18 at 19:14

3 Answers3

0

When you assign these event handlers, no button with class "opened" exists, so that event doesn't get attached to anything.

The easiest way to solve this is to instead assign delegated event handlers to a DOM element that does already exist. Now it doesn't matter whether button.closed or button.opened existed when the handler was assigned; it will check for the existence of the child node when the event occurs.

(In practice it's better to not hang everything on the body; choose a DOM node that's a closer parent to the nodes you're delegating to, but for now body will do:)

$("body").on("click", "button.closed", function(){
  console.log("open the menu");
  $(this).toggleClass("opened");
  $(this).toggleClass("closed");
});

$("body").on("click", "button.opened", function(){
  console.log("close the menu");
  $(this).toggleClass("opened");
  $(this).toggleClass("closed");
});
.opened .open, .closed .close {display:none}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="expand closed">
    <span class="open">open menu</span>
    <span class="close">close menu</span>
</button>

Meanwhile: it's rarely necessary to have two separate classes controlling the same state change. Treat one state as the default, and add CSS rules only for the other state:

$('body').on("click", "button", function() {
  $(this).toggleClass("opened");
  if ($(this).hasClass('opened')) {
    console.log("opened the menu")
  } else {
    console.log("closed the menu")
  }
});
.expand .close,
.expand.opened .open {
  display: none
}

.expand.opened .close {
  display: block
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="expand">
    <span class="open">open menu</span>
    <span class="close">close menu</span>
</button>
Daniel Beck
  • 20,653
  • 5
  • 38
  • 53
0

Since the .opened class does not exist on the button at assignment, you will have to use an event-delegation approach. Like so:

<div class="dynamic-button">
    <button class="expand closed">
        <span class="open">open menu</span>
        <span class="close">close menu</span>
    </button>
</div>

$(".dynamic-button").on("click", "button.closed", function(){
    console.log("open the menu");

  $(this).toggleClass("opened");
  $(this).toggleClass("closed");
});

$(".dynamic-button").on("click", "button.opened", function(){
  console.log("close the menu");

  $(this).toggleClass("opened");
  $(this).toggleClass("closed");
});

For more explanation, read more about $.on().

Drewness
  • 5,004
  • 4
  • 32
  • 50
-2

Try unbinding the click event first and then binding it again.

$("button.opened").off("click").on("click", function(){
  console.log("close the menu");

  $(this).toggleClass("opened");
  $(this).toggleClass("closed");
});
rudy0807
  • 152
  • 1
  • 8