Why does the browser stop displaying checkbox updates? - javascript

I'm using jQuery to help select a some matching checkbox elements in a form. The form is created dynamically, but here is a stripped down version of it that illustrated my point:
http://jsfiddle.net/phinor/qj7uj3rc/
This is the relevant JavaScript:
$(document).ready (function () {
$('.lessoncount').on ('click', tickAlike);
});
function tickAlike ()
{
var classes = $(this).attr('data-classes');
if ($(this).siblings ('.tick_' + classes).attr ('checked'))
{
console.log ('ticking');
$('.tick_' + classes).removeAttr ('checked');
}
else
{
console.log ('unticking');
$('.tick_' + classes).attr ('checked', true);
}
}
The idea is that checkboxes can be individually selected or, if the "label" is clicked on, all checkboxes with the same class will be selected or deselected to match the box next to it. Because I want two possible behaviours depending on whether they click on the box or the label, I cannot use the <label> tag. As a follow on from that, because of the independent selection, "toggling" is not viable.
This seems to work for one cycle of "ticks" and "unticks" and then stops. If I inspect the checkbox element in Chrome, I can see that the "checked" attribute is being added and removed appropriately, but the browser view is no longer updated and the ticks do not show.
This is consistent in the lastest versions of Chrome, Firefox and IE, so I'm guessing that I'm doing something wrong!

You should be using .prop() instead of .attr() when modifying boolean attributes, e.g. checked, disabled, readonly, selected and etc. Using .removeAttr() or .removeProp() will cause the attribute to be removed forever, and cannot be further manipulated. To understand the difference between .prop() and .attr(), there is actually a very good question (with a well-structured answer) asked on SO before.
To programmatically check a checkbox, simply use $selector.prop('checked',true). To uncheck, use $selector.prop('checked',false).
Also, to check if a checkbox is checked (no pun intended), you can use the .is() method to do so, i.e. $selector.is(':checked').
$(document).ready(function () {
$('.lessoncount').on('click', tickAlike);
});
function tickAlike ()
{
var classes = $(this).attr('data-classes');
if ($(this).siblings('.tick_' + classes).is(':checked'))
{
console.log('ticking');
$('.tick_' + classes).prop('checked', false);
}
else
{
console.log('unticking');
$('.tick_' + classes).prop('checked', true);
}
}
A working demo: http://jsfiddle.net/teddyrised/qj7uj3rc/5/
Even better: I can see that the <span> element containing the description for each checkbox is semantically unhinged from its accompanying checkbox, i.e. there is no way to give context to each checkbox. The <label> element is created for this purpose.
You might still want to style your descriptors independently, so we can keep them wrapped in <span>, but we can wrap all the content within each <td> with the <label> element, so that a click on both the text and the checkbox will trigger the same response.
A revised markup for the rows in the table body would look something like this:
<tr>
<td>1-Mo</td>
<td><label for="lesson_1_1"><input type="checkbox" name="lesson[1][1]" value="1" id="lesson_1_1" class="tick_1645_1646_1647_1648_1649_1650_2024"/> <span data-classes="1645_1646_1647_1648_1649_1650_2024" class="lessoncount">(7)</span></label></td>
<td><label for="lesson_1_2"><input type="checkbox" name="lesson[1][2]" value="1" id="lesson_1_2" class="tick_1582_1583_1584_1585_1586_1587"/> <span data-classes="1582_1583_1584_1585_1586_1587" class="lessoncount">(6)</span></label></td>
<td><label for="lesson_1_3"><input type="checkbox" name="lesson[1][3]" value="1" id="lesson_1_3" class="tick_1658_1673_1684_1700_1706_1736_1737_1769"/> <span data-classes="1658_1673_1684_1700_1706_1736_1737_1769" class="lessoncount">(8)</span></label></td>
<td><label for="lesson_1_4"><input type="checkbox" name="lesson[1][4]" value="1" id="lesson_1_4" class="tick_1602_1603_1604_1605_1618_1628"/> <span data-classes="1602_1603_1604_1605_1618_1628" class="lessoncount">(6)</span></label></td>
<td><label for="lesson_1_5"><input type="checkbox" name="lesson[1][5]" value="1" id="lesson_1_5" class="tick_"/> <span>(0)</span></label></td>
<td><label for="lesson_1_6"><input type="checkbox" name="lesson[1][6]" value="1" id="lesson_1_6" class="tick_1659_1674_1701_1738_1755_1756"/> <span data-classes="1659_1674_1701_1738_1755_1756" class="lessoncount">(6)</span></label></td>
<td><label for="lesson_1_7"><input type="checkbox" name="lesson[1][7]" value="1" id="lesson_1_7" class="tick_1719_1720_1721_1722_1723_2003"/> <span data-classes="1719_1720_1721_1722_1723_2003" class="lessoncount">(6)</span></label></td>
<td>detail | clear</td>
</tr>
With a slightly modified script to listen to the click even on the wrapping <label> element instead of a text span:
$(document).ready(function () {
$('table tbody label').on('click', tickAlike);
});
function tickAlike ()
{
var classes = $(this).find('span.lessoncount').attr('data-classes');
if ($(this).find('input[type="checkbox"]').is(':checked'))
{
console.log('ticking');
$('.tick_' + classes).prop('checked', false);
}
else
{
console.log('unticking');
$('.tick_' + classes).prop('checked', true);
}
}
See alternative working demo: http://jsfiddle.net/teddyrised/pb68gk2n/

The problem is that you should not manipulate with attributes when you want to toggle properties checked.
Here is updated and simplified code:
$(document).ready(function () {
$('.lessoncount').on('click', tickAlike);
});
function tickAlike() {
var classes = $(this).data('classes');
var $checkbox = $(this).siblings('.tick_' + classes).click();
$('.tick_' + classes).prop('checked', $checkbox.is(':checked'));
}
Demo: http://jsfiddle.net/qj7uj3rc/6/
However this is still not ideal: note how clicking on checkbox itself does nothing. So it can be improved. You need to use label instead of span and wrap checkboxes into those labels. So HTML should be this:
<label data-classes="1645_1646_1647_1648_1649_1650_2024" class="lessoncount">
<input type="checkbox" name="lesson[1][1]" value="1" id="lesson_1_1" class="tick_1645_1646_1647_1648_1649_1650_2024" /> (7)
</label>
and after that bind onchange event on checkboxes instead of spans:
$(document).ready(function () {
$('.lessoncount :checkbox').on('change', tickAlike);
});
function tickAlike() {
var classes = $(this).parent().data('classes');
$('.tick_' + classes).not(this).prop('checked', this.checked);
}

Use "is(':checked')" for the conditional and use ".prop ('checked', false)" and ".prop ('checked', true)" in order to make changes in the check input.
Here is the link https://jsfiddle.net/qj7uj3rc/7/
if ($(this).siblings ('.tick_' + classes).is(':checked'))
{
console.log ('ticking');
$('.tick_' + classes).prop ('checked', false);
}
else
{
console.log ('unticking');
$('.tick_' + classes).prop ('checked', true);
}

If your form is created dynamically then it might behave differently than static. Use bind or live instead of click for dynamically created elements:
$(".lessoncount" ).live( "click", function() {
tickAlike();
});

Related

Disable Dropdownlist based on Radio Button

Goal:
If you select "Dates", you can select the dropdownlist for Start date and end date.
If you select "All ... only" the start and end date will be grey colored in the background and you cannot click on the arrow down. These dropdownlists are disable.
Problem:
I don't know how to create it in frontend code.
Info:
*The dropdownlists are created in ASP.net MVC 4
*I'm using jquery 1.10 and bootstrap
<input id="aa" type="radio" name="searchselection" value="all" style="display: inline-block;" checked>
<label for="aa" style="width: 100px; display: inline-block; ">All ...only</label>
<input id="dates" type="radio" name="searchselection" value="dates" style="display: inline-block;">
<label for="dates" style="width: 100px; display: inline-block;">Dates</label>
#{
DateTime myDate = DateTime.Today;
List<SelectListItem> myListSelectListItem_YearStartDate = new List<SelectListItem>();
for (int i = 0; i < 10; i++) {
myListSelectListItem_YearStartDate.Add(new SelectListItem { Text = (myDate.Year - i).ToString(), Value = (i + 1).ToString(), Selected = DateTime.Today.Year == (myDate.Year - i) ? true : false });
}
}
#Html.DropDownList("YearStartDate", myListSelectListItem_YearStartDate)
You could try something like
$(document).on('change', 'input[type=radio][name=searchselection]', function() {
//func body
....
if(this.value == *your choices*){
//disable
$(YourDropdownSelector).attr('disabled', 'disabled');
}else {
//enable again
$(YourDropdownSelector).removeAttr('disabled');
}
});
This is fixed in the following jsfiddle
I've stripped out some of the unneeded HTML attributes (such as the style tags - styles are better applied in css) and also stubbed out the back end code generating the <select> in order to simplify the example and focus on the solution.
Let's look at what's happened:
<select class='js-date-selector' disabled='disabled'>
Firstly, each of your select elements has been edited to add the following two attributes. The class allows targeting from javascript (or JQuery) - note that the js- prefix is not essential, it's just a nice way of keeping your javascript class attributes separate from others. Also, a class is used instead of an id, this is generally best as it is easier to re-use, as we have to in this example.
The disabled attribute is how you mark-up an HTML element so it's greyed out. If you're going to mark 'all dates' as checked on page load and 'all dates' being checked means the selects should be disabled, then your HTML also needs to mark the selects as disabled on load.
Next is the bit that does the toggling:
$('.js-all-or-dates').on('click',function() {
var justClicked = $(this),
dateSelectors = $('.js-date-selector');
if (justClicked.attr('id') === 'aa') {
dateSelectors.attr('disabled', true);
}
else {
dateSelectors.attr('disabled', false);
}
});
Firstly, we bind a function to the click event for each of our .js-all-or-dates radio inputs.
Secondly, we assign variables, using justClicked = $(this) to store a jquery version the element that was just clicked and dateSelectors to store all of our select items, using the class mentioned above
Finally, we look at what was just clicked and if it has the ID of the 'all dates' radio input we set the disabled property on all the select elements.
Also, for good practice and smoother development: === is used for equality; $ function calls are minimised by assigning results to local variables; and the var statement contains comma separated declarations.

find text contained in a div and get its child checkbox ID

I`m trying to write a script that gets the ID from a checkbox element and do something with it. The problem is that I can not target the checkboxes by value since its generated from mysql query.
Here is how the html for the checkboxes looks like :
<div class="cat_checkbox cat_unchecked">
<input id="r_c136" type="checkbox" value="36" name="r_c1[]">
Sample
</div>
<div class="cat_checkbox cat_unchecked">
<input id="r_c131" type="checkbox" value="31" name="r_c1[]">
Text1
</div>
And here is the code I managed to write (I`m still learning java script and jQquery).
What I am trying to do is to tell js to select the that contains "Text1" and find the checkbox inside it, get its id and then add checked prop to the item.
But instead it selects the first div finds the checkbox inside and checks it no matter the value inside.
$( "#click" ).click(function() {
if ($( "div:contains('Text1')" ))
{
id = $("div:contains('Text1')").find("input[type='checkbox']").attr("id")+"";
alert(id);
$("#"+id).prop("checked", !($("#"+id)).is(':checked'));
}
});
});
Any help will be aprciated. Thank you.
Use $("div:contains('Text1')").length instead of $("div:contains('Text1')")
Change Your code as
$("#click").click(function () {
if($("div:contains('Text1')").length) {
var checkbox = $("div:contains('Text1')").find("input[type='checkbox']"); //Find Checkbox here
checkbox.prop("checked", !checkbox.is(':checked'));
}
});
DEMO
Change your code as
$("#click").click(function () {
var checkbox = $("div:contains('Text1')").find("input[type='checkbox']");
if (checkbox.length) {
checkbox.prop("checked", !checkbox.is(':checked'));
}
});
Updated DEMO

Pure Javascript target label

I have some checkboxes and I want the text of the label to change when the checkbox is selected:
JSFIDDLE
var listener = function() {
document.addEventListener('change', function(e) {
if (e.target.checked) {
e.target.label.className = "option-selected";
}
}
}
}
HTML if you are interested:
<input id="0A" class="individual-checkbox" type="checkbox" value="A">
<label for="0A">A</label>
<br>
<input id="0B" class="individual-checkbox" type="checkbox" value="B">
<label for="0B">B</label>
Obviously, target.label doesn't work. How do I access the label of the target and give it a CSS class (purely in JavaScript)?
To make your jsFiddle work, you have to change the following:
Fix the syntax error (missing a closing paren for the addListener() function call and you have an extra closing brace.
Actually call the listener() function to make it run.
Target the actual label, not the checkbox with your class
There are several different ways to target the label. The simplest would be to enclose the input inside the label and then just use .parentNode to get the label from the checkbox.
HTML:
<label for="0A">
<input id="0A" class="individual-checkbox" type="checkbox" value="A">
A</label>
<br>
<label for="0B">
<input id="0B" class="individual-checkbox" type="checkbox" value="B">
B</label>
code:
var listener = function() {
document.addEventListener('change', function(e) {
if (e.target.checked) {
e.target.parentNode.className = "option-selected";
}
});
}
listener();
Working jsFiddle: http://jsfiddle.net/jfriend00/s2A7W/
If you don't want to change your HTML, then you just need to find the label element that is right after your input element.
You can do that like this:
var listener = function() {
document.addEventListener('change', function(e) {
var label;
if (e.target.checked) {
label = next(e.target, "label");
if (label) {
label.className = "option-selected";
}
}
});
}
listener();
function next(src, tag) {
tag = tag.toUpperCase();
while (src && src.tagName !== tag) {
src = src.nextSibling;
}
return src;
}
Working jsFiddle: http://jsfiddle.net/jfriend00/3wQKa/
FYI, you should probably also restrict the action of your listener to only when a checkbox is actually the target or an element with a particular classname or some other distinguishing feature that makes sure it's a checkbox you want targeted by this code. You are probably safe with e.target.checked, but I don't like the fact that this event listener responds to all propagated change events in the entire page.
Assuming there is only one label element associated with the element:
e.target.labels[0].className = "option-selected";
This is an HTML5 property though, I don't know how well it is supported in older browsers. Source: MDN.
Alternatively, if IE8 support is enough for you, you can explicitly search for it with document.querySelector:
var label = document.querySelector('[for=' + e.target.name + ']');
This only works if you give the input elements name attributes (which you really want to do, otherwise the labels are not properly connected to the input elements).
And finally, if the label always comes after the input, you can traverse the DOM:
var label = e.target.nextSibling;
while (label.nodeName !== 'LABEL') {
label = label.nextSibling;
}
If you'd restructure your HTML so that the input element is a child of the label element:
<label for="0A">
<input id="0A" class="individual-checkbox" type="checkbox" value="A">
A
</label>
then you could simply use e.target.parentNode to get the label. Putting the input element inside the label also connects the label to the input.
this will do the trick:
function addListener(elem) {
// remember: checkboxes never change trair value
elem.addEventListener('click', function () {
var ls = document.getElementsByTagName('label');
for (var l = 0, ll = ls.length, lbl; l < ll; ++l) {
lbl = ls[l];
if ( this.id == lbl.getAttribute("for") )
{
lbl.className = ( this.checked ? "option-selected" : "" );
}
}
});
}
see http://jsfiddle.net/fGSCH/10/
be aware that addListener() might not work in every browser. but since you used it in the example i will use it too.

jquery issues with tr click changes checkbox

My condition: I have a checkbox inside <tr>, written in <asp:repeater> on an user control, placed in an aspx page.
My goal: when <tr> is clicked, JQuery changes (or specifically, toggles) the checkbox checked attribute as well as the css class of the <tr> itself.
I've tried several methods, and so far, only this one works for me: http://jsfiddle.net/xixonia/WnbNC/
, but unfortunately, all of the checkboxes are toggled instead of only the selected one, just like the example on jsfiddle.net. moreover, if I clicked on the checkbox itself, all of the other checkboxes are checked instead of the clicked one.
What I've done:
$(".moduleTableItem").click(function (e) {
// Toggles CSS
if (!$(this).closest('tr').hasClass("tr.active")) {
$(this).closest('tr').css('background-color', 'blue');
$(this).closest('tr').addClass("tr.active");
} else {
$(this).closest('tr').css('background-color', 'red');
$(this).closest('tr').removeClass("tr.active");
}
// Toggles Checkbox
$(':checkbox').prop('checked', function (i, value) {
return !value;
});
ASP:
<asp:Repeater ID="repModuleGeneral" runat="server" EnableViewState="true">
<ItemTemplate>
<tr id="trModuleGeneral" class="moduleTableItem" runat="server">
<td>
<input type="checkbox" id="cbxSelect" class="cbxSelect" runat="server" autopostback="false" /></td>
<td>
<asp:Label ID="lblNo" runat="server" /></td>
</tr>
</ItemTemplate>
</asp:Repeater>
This is my first question. Please let me know if my question is somewhat unclear. Any code snippets or working solution would be appreciated.
Thank you.
Change
$(':checkbox').prop('checked', function (i, value) {
To
$(this).find(':checkbox').prop('checked', function (i, value) {
this used here will point to the current clicked tr.
This wont work if you directly click on the checkbox. Why, you ask? Because the checkbox lies inside tr, to which you've assigned a click event to. So, when click on a checkbox which is unchecked, these two actions are triggered :
Checkbox selected because of default checkbox action - checkbox set to true.
The click on the tr is triggered. You've got a checkbox prop event there. Reverts the selection done by default action.
So its a basic counter action, which cancels the default actions of the checkbox. To rectify this, you'll have to check if the default target is NOT a checkbox and then run the prop function :
$(".moduleTableItem").click(function (e) {
// Toggles CSS
if (!$(this).closest('tr').hasClass("tr.active")) {
$(this).closest('tr').css('background-color', 'blue');
$(this).closest('tr').addClass("tr.active");
} else {
$(this).closest('tr').css('background-color', 'red');
$(this).closest('tr').removeClass("tr.active");
}
//check here
if (!$(e.target).is(":checkbox")) {
// Toggles Checkbox
$(this).find(':checkbox').prop('checked', function (i, value) {
return !value;
});
}
});
Demo : http://jsfiddle.net/hungerpain/Kph8M/
But, just as a side thought, if you have control over IDs generated over the checkboxes, you could assign the same id as the for attribute in the label. Your HTML would look like this :
<tr class="moduleTableItem">
<td>
<input type="checkbox" id="cbxSelect1" class="cbxSelect" />
</td>
<td>
<label for="cbxSelect1">Text</label>
</td>
</tr>
Not the id attribute of the checkbox and the for attribute of the label. A lot of hassle will be solved by this. And you could use the change event of the check box. Also, did some optimisations to your code. It would look something like this :
$(".moduleTableItem :checkbox").change(function (e) {
//cache this - you are using it more than once.
var $tr = $(this).closest('tr');
// check if class is active - I took away if..else and made it ternary
var bg = $tr.hasClass("tr.active") ? 'red': 'blue';
//set css and toggleClass. Anyway you're switching everytime. Why not use toggle?
$(this).closest('tr').css('background-color', bg).toggleClass("tr.active");
});
Demo : http://jsfiddle.net/hungerpain/Kph8M/4/

JQuery Custom Radio Buttons code (code not working)

usual but I need to have different custom radio button images per button.
So Radio1 would have different images to Radio2.
Trying it out on the code below but it won't work so I must be doing something wrong?
Here's the code:
<label for="radio1">
<img src="radio1_unchecked.png" style="vertical-align:middle" />
<input name="radiogroup" type="radio" id="radio1" style="display:none;">
</label>
<label for="radio2">
<img src="radio2_unchecked.png" style="vertical-align:middle" />
<input name="radiogroup" type="radio" id="radio2" style="display:none;">
</label>
<script>
$(document).ready(function(){
var radio1checkedImage = "radio1_checked.png",
radio1uncheckedImage = "radio1_unchecked.png",
radio2checkedImage = "radio2_checked.png",
radio2uncheckedImage = "radio2_unchecked.png";
$('img').attr("src", radio1uncheckedImage);
$('#radio1, #radio2').change(function() {
var r;
r = $("#radio1");
r.prev().attr("src", r[0].checked ? radio1checkedImage : radio1uncheckedImage);
r = $("#radio2");
r.prev().attr("src", r[0].checked ? radio2checkedImage : radio2uncheckedImage);
});
});
</script>
Update: Here is the same code as above but without the multiple images.
As you can see it works. Can't the code be modified to have multiple images per radio?
You could use CSS to define which images goes with which radio button. Building on the JSBin example from my answer to you previous question, you can use JavaScript to add a classname (e.g. 'checked') to the parent of the checked radio (i.e. the <label>):
var radios = $('input:radio');
radios.change(function() {
radios.filter(':checked').parent().addClass('checked');
radios.filter(':not(:checked)').parent().removeClass('checked');
});
So, now that the <label> will have the 'checked' class if that radio is selected, you can use CSS to style it:
label {
/* regular styles */
}
label[for="radio1"].checked {
/* checked styles for #radio1's label */
}
label[for="radio2"].checked {
/* checked styles for #radio2's label */
}
Using CSS instead of <img> tags does mean you will need to use background-image, so be aware of that.
Live example: http://jsbin.com/ebapov/edit#javascript,html,live
Just in case, here is a more verbose version of the JavaScript posted above:
// Fetch the radio buttons (this is a jQuery collection):
var radios = $('input:radio');
radios.change(function() {
// Filter the radio inputs into 'checked' and 'unchecked':
var checkedInputs = radios.filter(':checked');
var uncheckedInputs = radios.filter(':not(:checked)');
// Get the 'checked' and 'unchecked' labels:
var checkedLabels = checkedInputs.parent();
var uncheckedLabels = uncheckedInputs.parent();
// Add the class "checked" to the checked labels:
checkedLabels.addClass('checked');
// ... and remove it from the unchecked labels:
uncheckedLabels.removeClass('checked');
});
Most of this code relies on the fact that jQuery functions can be chained. This means that when you call the parent() function on a jQuery collection, it will actually return a collection containing the parent of each of the elements in the original collection. If you then call addClass on that new collection, it will add a classname to each of those parents.
Most of jQuery's functions can be chained.
In addition my other answer, which uses CSS, let me offer an alternative solution.
Firstly; you want to keep track of which image goes with which radio button. Why not use an object literal?
var checkedImages = {
'radio1': "radio1_checked.png",
'radio2': "radio2_checked.png"
};
var uncheckedImage = "unchecked.png";
This way you can easily refer to the different URL's like, e.g.: checkedImages['radio2'].
The change event handler would look very similar. The only difference is what you do with the checkedLabels and the uncheckedLabels:
var radios = $('input:radio');
radios.change(function() {
var checkedLabels = radios.filter(':checked').parent();
var uncheckedLabels = radios.filter(':not(:checked)').parent();
uncheckedLabels.children('img').attr('src', uncheckedImage);
checkedLabels.each(function() {
var image = $(this).children('img');
var name = $(this).attr('for');
if (checkedImages[name] !== undefined) {
// We have checked image for this radio button, so set it:
image.attr('src', checkedImages[name]);
} else {
// We don't have checked image for this radio button.
image.attr('src', uncheckedImage);
}
});
});
The main differences:
We use children() to find the images.
We use each() to add more complicated logic for each of the unchecked label.
We check the selected <label>s for attribute (using .attr('for')) to find out which image we should apply.
In this example only the checked state has different images for the different radio's. If you need different unchecked images as well, you can easily apply the same principle.
Live example: http://jsbin.com/acalir/edit#javascript,html,live
P.S. don't forget to set the src to the unchecked images (in HTML), or do the following (in JS):
$('input:radio').change();
This fires the change event programmatically, which will cause the unchecked images to be applied.

Categories