jQuery/Javascript: Click event on a checkbox and the 'checked' attribute - javascript

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?

B.E., I know it has been a year but I think I found the solution,
The issue here is that the click event actually get's called and runs BEFORE the "checked" property is added to the checkbox input. So the function runs, looks to see if the input has the "checked" attribute, and runs the else condition. THEN the element is given the "checked" property.
I just ran into this as well, and my solution was to bind the function to the change function rather than the click function, as change only fires AFTER the checked property has been updated on the input.
Hopefully this help you, and if not, anyone else who happens to stumble upon this post while experiencing a similar issue.

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;
});

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.

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.

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 {

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'))

Related

How to avoid ajax async when using jQuery events

I have an input field with ajax call (filling some other input fields) on blur and buttons with click events (some of the click events set input fields to an empty string).
For example,
$("#input_field1").on('blur', function () {
$.ajax({
//async:false,
method: "GET",
url: "my_ajax.php",
cache: false,
dataType: "json",
data: { data1: $("#input_field1").val()},
success: function(result){
$("#some_other_input").val(result.data2);
}
})
});
$("#button1").on('click', function () {
$("#input_field1").attr("readonly", true);
var form = $("#my_form");
form.validate().resetForm();
form[0].reset();//form contains #some_other_input
});
When that input field is focused and then user clicks on any button, blur event is triggered and of course, appropriate click event after it.
If I don't use async:false, ajax will fill those input fields after click event is processed and other inputs will be filled instead of being empty.
Reading about how async:false should be avoided always, I need a way for my click events to wait until ajax is done, if there is an ajax call at that moment.
You need a concurrency check for your cases. On your blur action; check and set a value to prevent reentering the other calls.
Also during an ajax request, you need to prevent clicking the buttons.
One more thing, what if just after bluring your input field, user re-focuses and re-focusouts your input? In this case, your action will be triggered twice.
As a result, you need to handle concurrency. There are also some javascript libraries to handle concurrency, you can either use them or handle by your own.
Anyway to handle your case, here is a sample code:
let hasCall = false;
$("#input_field1").on('blur', function () {
if(hasCall){
return; //there is an active call...
}else{
hasCall = true;
$("#button1").prop('disabled', true);//prevent clicking on button
}
$.ajax({
//async:false,
method: "GET",
url: "my_ajax.php",
cache: false,
dataType: "json",
data: { data1: $("#input_field1").val()},
success: function(result){
$("#some_other_input").val(result.data2);
}
}).always(()=>{
//reset the state:
hasCall=false;
$("#button1").prop('disabled', false);
});
});
$("#button1").on('click', function () {
$("#input_field1").attr("readonly", true);
var form = $("#my_form");
form.validate().resetForm();
form[0].reset();//form contains #some_other_input
});

Why is my javascript function finding my checkbox to be false

My code looks something like this
HTML
<div><input type="checkbox" name="showInactiveBox" value="showInactiveBox" id="showInactiveBox">Show inactive project numbers</div>
Javascript
$("#showInactiveBox").live("click", function () {
if ($(this).is(":checked")) { //checkbox is checked
$.ajax({
type: "POST",
url: "#Url.Action("UpdateInactiveBox", "Project")",
data: {
showInactive: true
}
})
.done(function (data) {
Debug.writeln("Check box is checked?: " + $("#showInactiveBox").is(":checked"));
$("#project-numbers-grid").data("kendoGrid").dataSource.read();
});
I have an else that looks similar to above, just sets the checkbox to false basically. My debug line above in the .done of the Ajax function returns the correct value whether it be checked or not. However I then call the .read() on my grid which relates to more javascript below:
read: {
url: "#Html.Raw(Url.Action("GetActiveProjectNumbers", "Project"))",
type: "POST",
cache: false,
dataType: "json",
data: {
q: function () {
var model = {
projectid: "#Model.Id",
};
return JSON.stringify(model);
},
showinactive: $("#showInactiveBox").is(":checked")
}
},
When I put another Debug.writeline in the server, it is receiving the correct value for "q" but "showinactive" is always read in as false. Is there something with javascript that I don't quite understand where going from function to function will read a different value for ($"checkboxid").is(":checked")?
Thanks
Are you sure .is() works with the jQuery version you are running? You could also try .prop('checked'). I would try running this in the Chrome developer tools on the page:
$("#showInactiveBox").is(":checked")
and
$("#showInactiveBox").prop("checked")
If those return the value you are looking for, then there is something else going on. Try running those in console.log at the point at which you are calling those functions to see what the active output is:
console.log('This is my checkbox value', $("#showInactiveBox").is(":checked"));
In addition to Lawrence Johnson's (.prop) answer I'd take a look at some of the other code you are using.
For instance, when dealing with checkboxes it's always better to use the .change() method rather than click.
Also, the .live method is deprecated now and should be using .on().
When dealing with checkboxes you can easily use "vanilla javascript" to test if the box is checked or not. jQuery is not always the "less code/best solution".
See the below code for all the amendments mentioned above:
<input type="checkbox" name="showInactiveBox" value="showInactiveBox" id="showInactiveBox">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
$('#showInactiveBox').on('change',function(){ //using .on and change rather than click
if ( this.checked ) {//test if checked or not
console.log('checked');
}
})
});
</script>
Change
showinactive: $("#showInactiveBox").is(":checked")
to
showinactive: function() {
return $("#showInactiveBox").is(":checked");
}
and call it with
showinactive()
Currently, you are setting showinactive to the value of the property, which I'm assuming starts as false. Values in javascript don't update with the DOM like that--you need to explicitly check each time.

Call jquery function when one function completes

So I have a jquery click function assigned to an on/off toggle. Very simple script:
$('.on-off').on('click', function(e) {
e.preventDefault();
var $this = $(this);
$this.find('.slider').toggleClass('active');
});
We have two versions of this toggle. One toggles instantly when clicked and then we submit the value when clicking next(aka submit).
Our other one calls a jquery ajax function that toggles on success and upon success if it is a specific message code that is defined on the backend.
jQuery.ajax({
url: url,
type: 'POST',
dataType: 'json',
cache: false,
data: {'requestType': requestType},
success: function(message) {
if(message.STATUS=='2000'){
if(currentButtonClicked=='dashboardChargingButton'){
if($('#dashboardChargingButton').html()==startCharge)
$('#dashboardChargingButton').html(stopCharge);
else
$('#dashboardChargingButton').html(startCharge);
}
if(currentButtonClicked=='invokeChargingButton'){
$( "#invokeChargingButton .slider" ).toggleClass( 'active');
}
}
},
error: function(xhr, ajaxOptions, thrownError) {
alert(xhr.status + " - " + xhr.statusText);
}
});
}
As you can see I have to toggle the class again using the same code but with direct targeting.
The on off toggles of this type have an onclick inside the actual html calling the function that handles this ajax.
My goal is to have my first set of code the one that targets the element and toggles the class to do all of this, but dynamically to where we don't have to call a function everytime.
Conceptually what I thought is:
$('.on-off').on('click', function(e) {
e.preventDefault();
var $this = $(this);
if (!$this.attr('onclick')) {
$this.find('.slider').toggleClass('active');
} else {
var clickFunction = $this.attr('onclick');
call the clickFunction
if (clickfunction = true) {
$this.find('.slider').toggleClass('active');
}
}
});
What this would do is grab the onclick, but not call it until I specify. And inside the ajax request instead of toggling I would just return true.
This might not be the best method. I am just trying to ecapsulate everything to limit the amount of code as well as make all the dom changes for those elements in one spot for any potential defects.
Here is a link to a basic fiddle of the on/off toggle.
Fiddle
I hope I explained everything in good enough detail.

jQuery val() not working in IE for dynamically added option

I am generating options for my dropdown by jQuery ajax method, filling it by db.
$.ajax({
type: "POST",
url: pageUrl + '/FillAssignee',
data: {},
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
for (var i = 0; i < response.d.length; i++) {
$('#SelectAssignee').append($("<option></option>").val(response.d[i]['Value']).html(response.d[i]['Text']));
}
if (response.d.length > 0)
{
$('#SelectAssignee>option:eq(1)').attr('selected', true);
alert($('#SelectAssignee').val()); //this line giving me correct value but value not showing in dropdown as selected in ie
}
}
});
It's works fine. only problem is that first value not got selected by default on IE. So for that I used many options
1. $('#SelectAssignee').val();
2. $('#SelectAssignee option:first').attr('selected','selected');
3. $('#SelectAssignee option:first').prop('selected',true);
How can I get it work?
try something like this
var selObj = document.getElementById('SelectAssignee');
selObj.selectedIndex = 0;
Your logic is saying that you are targeting the FIRST element in your select menu, however, you are adding the new option to the END of the select input. You either need to change your .append to .prepend, or target the last option when setting the selected attribute to true
Try setting as attributes rather than properties.
$('#SelectAssignee').append($("<option />", { value: response.d[i]['Value'], html: response.d[i]['Text'] }));
Could you please try below solution.
After the for loop, just add this
if (response.d.length > 0) {
$('#SelectAssignee>option:eq(1)').attr('selected', true);
}
My best guess is that this should work. Although, this seems like a dirty hack, there was an occasion when I had to resort to this and it worked.
setTimeout( function() {
$('#SelectAssignee option:first').prop('selected', true);
$('#SelectAssignee').trigger('change');
}, 1000 );
Try putting this code block in the success callback, after the for loop.
After the for loop, try following condition
if (response.d.length > 0) { $('#SelectAssignee>option:eq(0)').attr('selected', 'selected');}
Try setting the value explicitly to the value of the first option
$('#SelectAssignee').val(response.d[0]['Value']);

jquery $(this) object missing issue

I have a problem with jQuery's $(this) object and make me miss the this element
in code :
$('.star').click(function (){
var id = $(this).parent().attr('id').split('rating')[1];
var vote = $(this).index() + 1;
var ThisStar = $(this);
alert(ThisStar.parent().html())
$.ajax({
type: 'POST',
url: EXECUTION_URL + 'ajax/rate.php',
data: {'article' :id ,'rate': vote},
success:function(data,tt,ff){
alert(ThisStar.parent().html())
}
});
the first alert fire : the true contents of the parents of the clicked node
the second alert fire : null !!!!
why it have been changed into the ajax ? or from another reason ? please tell me a good solution for this issue
ok actually i checked my code that there is two lines after the ajax function that delete the stored node in the this object and because of the ajax function is asynchronous function the two lines executed before the code inside the success function >> thanks for all
Your code should work fine, check the proof. The only difference would be this:
success:function(data,tt,ff){
alert(ThisStar.parent().html())
}
vs.
success:function(data,tt,ff){
alert(ThisStar.parent().html())
}});

Categories