1

i have the following button with id "openWindow"

<button id="openWindow">Open Window on Click</button>

and I have this jquery code, which send an alert "Open" and change the button id to "closeWindow"

$( "#openWindow" ).click(function() {
   alert("Open");
   $( "#openWindow" ).attr("id", "closeWindow");
})


$( "#closeWindow" ).click(function() {
        alert("Close");
});

On first click:

  • Alert "open" appears and the id changed to "closeWindow"

On second click

  • the alert "Close" will not appear.

Can anybody tells me why?

Ghost108
  • 3,277
  • 8
  • 37
  • 79
  • 1
    Because you bound the event before the element had the id of `closeWindow`. You could use `on` to bind the event and it would work, e.g. $('body').on('click', '#closeWindow', function() { alert('Close');});`. – clav Oct 31 '19 at 16:37
  • 1
    Aside from the answers below you should note that dynamically changing the `id` of an element at runtime isn't a good idea. They are intended to be static and should remain as such. If you want to change an element's behaviour at runtime I would suggest adding/removing a class instead – Rory McCrossan Oct 31 '19 at 16:47

5 Answers5

1

Because you bound the event before the element had the id of closeWindow. You could use jQuery's on event handler to bind the event on something like the body or a wrapper element and it would work, like this.

$('body').on('click', '#closeWindow', function() { 
   alert('Close');
});
clav
  • 4,073
  • 25
  • 39
1

$( "#closeWindow" ) selects all elements with that ID at the time the code is executed.

You don't change the id until the button is clicked which is after that.

You could use a delegated event handler:

$(document).on("click", "#closeWindow", handler);

… noting that unless you also use a delegated event handler for the first handler, it will still be bound to the button even after you change the ID.

Quentin
  • 800,325
  • 104
  • 1,079
  • 1,205
1

This happens because at the moment that your script runs, the #closeWindow doesn't exists.

To solve this kind of issue, you can bind events like this:

<div id="someParentDiv"> 
    <button id="openWindow">Open Window on Click</button>
</div>
$("#someParentDiv")
    .on('click', '#openWindow', function() {
        alert("Open");
        $( "#openWindow" ).attr("id", "closeWindow");
    })
    .on('click', '#closeWindow', function() {
        alert("Close");
    });

This way, jQuery will observe clicks that bubbles until #someParentDiv and filter them by the given selectors (#openWindow and #closeWindow)

Better approach suggestion

You can make your code better by doing something like this:

<button id="toggleWindow">Open Window on Click</button>
let windowOpened = false;

$('#toggleWindow').click(function() {
    windowOpened = !windowOpened; // Use the `!`(Not operator)  to invert (toggle) the boolean value.

    if (windowOpened) {
        alert('Open');
    } else {
        alert('Close');
    }
});
Elias Soares
  • 7,626
  • 4
  • 22
  • 48
  • True but, IMHO, better to use a variable associated to the button itself (e.g. via an attribute) than polluting the global scope with random ad-hoc state variables. – ADyson Oct 31 '19 at 16:44
  • Probably this code will not be placed in a global scope. :-) Using a variable is simpler, faster and less jQuery dependent – Elias Soares Oct 31 '19 at 16:54
  • Using data attributes needn't require jQuery either. In fact none of this feature really benefits greatly from jQuery, it's just a choice the OP made. – ADyson Oct 31 '19 at 17:21
  • Sure, but require you manipulating DOM instead of just using a variable. Even if manipulating DOM is simple, setting a variable is ever simpler. Variables scopes are a concern for the OP. :-) – Elias Soares Oct 31 '19 at 17:26
0

This happens because when $( "#closeWindow" ).click( was executed to create the event handler, there was no element with the id "closeWindow", therefore the event handler did not get attached to anything. Event handlers get attached to the elements which exist when the handler is created. They cannot detect elements added subsequently.

Now, you could use delegated events to get round this, as other answers are suggesting.

But...it's not really good practice to change the ID - the element ID should uniquely and permanently identify the element. Instead, you could set a data-attribute or class to record its current state, and just use one event handler.

Here's a simple demo using a data attribute:

$("#openCloseWindow").click(function() {
  var btn = $(this);
  var state = btn.data("nextaction");
  if (state == "open") {
    btn.data("nextaction", "close");
    btn.text("Close Window on Click");
  } else {
    btn.data("nextaction", "open");
    btn.text("Open Window on Click");
  }

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="openCloseWindow" data-nextaction="open">Open Window on Click</button>
ADyson
  • 44,946
  • 12
  • 41
  • 55
0

You can do this by making a variable e.g. var opened = false;. Then ypu bind click to this button without changing it's id. And inside you write something like this:



    if (opened == false) {
        alert("opened");
        opened = true;
    } else {
        alert("Closed");
        opened = false;
    }