views:

647

answers:

5

The code:

$('input.media-checkbox').live('click', function(e){

    e.preventDefault();
    var that = $(this);

    if (that.attr('checked') == 'checked'){

        var m = that.attr('media');
        var mid = 'verify_' + m;
        that.parents('div.state-container').find('ul.' + mid).remove();
        that.attr('checked', false);
    } else {

        var url = AJAX_URL;

        $.ajax({
           type: 'GET',
           url: url,
           dataType: 'html',
           success: function(data){

                that.parents('li').siblings('li.verification').children('div.media-verification').append(data).fadeIn(500);
                that.attr('checked', 'checked');
           }
        }); 
    }

    return false;
});

I am ajaxing in a form, then firing the click event on relevant checkboxes to ajax in another partial if necessary. The form is inserted nicely, and the click events are fired, checking the boxes that need to be checked and firing the second ajax, since the checked attribute of the checkbox was initially false.

What's curdling my cheese is if I UNCHECK one of those boxes. Despite e.preventDefault(), the checked attribute is set to false BEFORE the test, so the if statement always executes the else statement. I've also tried this with $.is(':checked'), so I'm completely baffled.

It appears that unchecked -> checked state reads the original state, but checked -> unchecked doesn't. Any help?

+1  A: 

Well, right. You have set the live event, so I think your script might also be responding to setting it as checked, but I can't totally tell what you're trying for here without seeing markup, but here's my rewrite.

$('input.media-checkbox').live('click', function(e){

    if ($(this).is(':checked')) {
        var m = $(this).attr('media');
        var mid = 'verify_' + m;
        $(this).parents('div.state-container')
            .find('ul.' + mid)
            .remove();
        $(this).attr('checked', false);
    } else {
        var url = AJAX_URL;
        var that = this;
        $.ajax({
            type: 'GET',
            url: url,
            dataType: 'html',
            success: function(data) {
                $(that).parents('li')
                    .siblings('li.verification')
                    .children('div.media-verification')
                    .append(data)
                    .fadeIn(500);
                $(that).attr('checked', true);
            }
        }); 
    }
    return false;

});
artlung
I need the `.live` event handler, since the form in question isn't in the document when it's loaded - it's ajax'd in afterwards. Setting a straight `.bind` won't work.
b. e. hollenbeck
A: 

It seems to be a due to asynchronous requests. The execution goes past $.ajax, before it's success callback fires. When you click the checkbox again, it's state has not yet been updated by the previous request's callback. What you can try is to disable the checkbox control prior to firing the ajax call, and enable it again within the success callback:

that.attr("disabled", "disabled");
var url = AJAX_URL;   
$.ajax({
    type: 'GET',
    url: url,
    dataType: 'html',
    success: function(data){

        that.parents('li').siblings('li.verification').children('div.media-verification').append(data).fadeIn(500);
        that.attr('checked', 'checked');
        that.removeAttr('disabled');
    }
}); 

That will ensure that two successive clicks will not lead to unpredictable behaviour. The other way would be to use a synchronous request, i.e. async: false but that will block the entire browser for it's duration.

karim79
I like this - but no. The checkbox being toggled is already in the DOM - this ajax call (for a second request) is made after the test for `:checked`, so the success of the ajax request doesn't affect the state of the checkbox. Check a checkbox, and jQuery reads in the correct starting state of the checkbox - uncheck it, and it clears the box BEFORE firing the click event handler. It's bizarre.
b. e. hollenbeck
+1  A: 

Try using e.stopPropagation() instead of e.preventDefault() and also remove return false. Returning false in jQuery is equivalent to e.stopPropagation() + e.preventDefault(). e.preventDefault() prevents the checkbox from being checked.

Ajaxe
A: 

I think it's due to a weird bug in IE. Check/set attribute defaultChecked along with checked. Try this in your if condition,

if (that.attr('checked')=='checked' || that.attr("defaultChecked")=='checked'){     
      var m = that.attr('media');  
      var mid = 'verify_' + m;
      that.parents('div.state-container').find('ul.' + mid).remove();
      that.attr('checked', false);
      that.attr('defaultChecked', false); 
} else {
hetu
A: 

This is probably only a partial answer, but in your test, $(this).attr('checked') should return true if checked and false if not. So just change your conditional to if (that.attr('checked'))

mVChr