Triggering an event on an element with a changed class?

I’m fairly new to jQuery, so this question may be very basic, but I need some help understanding.

I have a form button:

<input type="button" id="do" class="edit" value="Edit" />

I change the the class and the value via jQuery:


        $('.edit').click(function(){
          $('#do').attr('value', 'Submit');
          $('#do').attr('class', 'submit');
        });

So now it looks like this:

<input type="button" id="do" class="submit" value="Submit" />

Now, when I click on the changed button, I was anticipating the execution of this code:

        
      $('.submit').click(function(e){
          alert(this.form.id);
          info = $(this.form.id).serialize();
          info = info + '&do=' + $('#do-').val();
          e.preventDefault();
          $.ajax({
            type:   'POST',
            url:    'ajax.saveEvent.php',
            data:    info,
            success: function(data){
              $('#eventFormDiv').html(data);
            }
          });
        });

But, nothing happens, and Firebug records nothing on the console. So, as far as I can tell, the new class is not being recognized.

Any advice?

Thanks!

Let’s start with some basic tips, nothing to complex :slight_smile:

Your click-event on the edit button:

  • Not that there is anything wrong with what you have written, it’s nicer to write it as below (you may ignore the new-lines and the tabs, it just reads easier :-))
    If you have to change multiple attributes of an element, you can just group them, as you can see below
$('.edit').click(function(){
	$('#do').attr({
		'value': 'Submit',
		'class': 'submit'
	});
});
  • My code is a bit more clean now, but it can be cleaner, so step 2: Seeing as we want to change the same button we’ve just clicked on, there is no need to search the DOM again. We can just replace $(‘#do’) with $(this). $(this) is a reference to the element we’ve just clicked on, in our case, the “edit”-button.
$('.edit').click(function(){
	$(this).attr({
		'value': 'Submit',
		'class': 'submit'
	});
});
  • Seeing as we are changing the class of the button to “submit”, there is actually no use anymore to have the “edit” click-event anymore on our button, so we should unbind it, like so:
$('.edit').click(function(){
	$(this).attr({
		'value': 'Submit',
		'class': 'submit'
	});
	$(this).unbind('click');
});	

Ok, the code is nice and neat, lets have a look at your problem!

The problem you’re facing is that the button doesn’t execute the “click”-event of the “submit”-button. That’s normal… I assume you’ve added the “$(‘.submit’).click(function(e){ // code });” code in the onload-function. Let me explain the problem:

  • When the browser reads/executes your code it will look for any element with the class “submit” and place a “click”-event on them. HOWEVER when your code is executed, there is no element with the class “submit”, because you’re putting (changing, in your case) the class “submit” on an element after your code was executed. (I hope this makes sense, it’s much easier explaining it in my own language :p)

So what you could do is, place the code of the submit-click inside the edit-click, like so:

$('.edit').click(function(){
	$(this).attr({
		'value': 'Submit',
		'class': 'submit'
	});
	$(this).unbind('click');
	$(this).click(function(e){
		alert(this.form.id);
		info = $(this.form.id).serialize();
		info = info + '&do=' + $('#do-').val();
		e.preventDefault();
		$.ajax({
			type:   'POST',
			url:    'ajax.saveEvent.php',
			data:    info,
			success: function(data){
				$('#eventFormDiv').html(data);
			}
		});
	});
});

Notice that I have changed “$(‘.submit’)” with “$(this)”. The difference between them is that I’m only placing the new click-event on the current button.
It would also be a bit nicer if you would create a seperate function for the “submit”-click event, but not necessary.

If something is still unclear, feel free to ask!

I like Denk’s approach here, but I think we can step it up another notch :slight_smile:

I’m not normally a fan of declaring event handlers inside of event handlers, it looks a little messy. So there are probably 2 different ways we could rewrite this.

  1. Use a click handler on the #do button and see what the current class name is.
  2. Use an event delegate handler to have 2 distinct event handler methods.

Method 1 is excellent if your code is relatively short and you’re not branching too far in to if/else statements, e.g.


$("#do").click(function(e) {
    var $this = $(this);

    if ($this.hasClass("edit")) {
        //do stuff for edit case
        doEditStuff();
        console.log("done editing");
        $this.attr({
            "class": "submit",
            "value": "Submit"
        });
    }
    else if ($this.hasClass("submit")) {
        //do stuff for the submit case
        doSubmitStuff();
        console.log("submitting");
    }

});

Method 2 would be better suited to larger chunks of code, especially if you like to keep everything separated nicely (might work nicer for code readability for example).


// the thing to remember when using the ".on()" method is that you should
// pick a common parent element, in this case the <form> element should do the trick
// of course if you have more than 1 form on the page, you might need to qualify it
// with a class or an id

$("form").on("click", ".submit", function(e) {
    //do stuff for submit case
    console.log("submitting");
});


$("form").on("click", ".edit", function(e) {
    //do stuff for submit case
    console.log("done editing");

    $(this).attr({
        "class": "submit",
        "value": "Submit"
    });
});

// I quite often like to completely separate out the event handlers from their binding, so I might do something like this:

//declare the actual functions we'll be using
function theSubmitHandler(e) {
    //do stuff
}

function theEditHandler(e) {
    //do stuff
}

//then simply pass them in as handlers.
$("form").on("click", ".submit", theSubmitHandler);
$("form").on("click", ".edit", theEditHandler);

The big difference here is of course that you have multiple event handlers when using the delegates, but you’ll have to weigh that against the readability/clarity of the code compared to an if-statement.
Once you have more than, say, 20 lines of code, an if-statement would probably be a bit harder to read.

Anyhow, just my 2c :slight_smile:

Thx AussieJohn for your remarks - I’ve learned something more now :slight_smile:

I’m not 100% fond of your second method, because you’re placing 2 click event handlers on the form. Eventhough it reads easier, I would go for a combo of the 2. Put 1 click-handler on the form and do an if-else check for the classname.

It’s also not clear what “PHP John” wants to accomplish and how the rest of the code looks. If this is the only part of JavaScript code, I’d go for my method (with or without an extra function for the submit-click event) or for your first method because it’s less complex.

Feel free to correct me though - I’m learning much about JavaScript by discussing/reading comments of others :slight_smile:

Yeah, the main reason you would want to use the multiple event handlers is to have more readable code. In the end it all comes down to how big your application is and how many people need to read your code, in multi-dev teams it’s important that everyone has an easy time of reading the code.

When you’re coding by yourself it’s mostly a personal preference as to whether you use an if/statement or multiple handlers, you just have to weigh up the pros and cons for your situation :slight_smile: