Dynamically check / uncheck checkboxes in a tree - javascript

I have a similar question to Uncheck parent node if all children unchecked in JQuery (with this solution) and have tried to modify it so that the children will also all uncheck if the parent is unchecked (which the above/below fails to do).
JSFiddle for the above (http://jsfiddle.net/fRxVs/)
jQuery.each(jQuery('#treeList ul li').find(':checkbox'), function(){
jQuery(this).change(function (){
if (jQuery(this).is(':checked')) {
jQuery(this).parentsUntil('#treeList').siblings().filter('input:checkbox').attr('checked', true).trigger('change');
}else{
jQuery(this).parents('ul:first').siblings('input:checkbox').prop('checked', $(this).parent().siblings().children('input:checked').length).trigger('change');
}
});
});
Though I'm not sure why, but I had to change prop from the last line to attr in order for it to correctly work as JSFiddle locally for some reason...
Basically I have a 3 level setup:
Grand-Parent
- Parent
-- Child
If the grandparent is checked/unchecked, then its children and grandchildren should all be checked/unchecked too.
If the parent is checked/unchecked, its children should be checked/unchecked as well as its parent.
If the children are checked, then its parent and grandparent should be checked (if no children are checked then parent shouldn't be checked).
I'm trying to change this to Continent, Country, Region - I think you will understand if I were to just say this....
Thanks

Here's the solution: Fiddle: http://jsfiddle.net/fRxVs/55/
$('#treeList :checkbox').change(function (){
$(this).siblings('ul').find(':checkbox').prop('checked', this.checked);
if (this.checked) {
$(this).parentsUntil('#treeList', 'ul').siblings(':checkbox').prop('checked', true);
} else {
$(this).parentsUntil('#treeList', 'ul').each(function(){
var $this = $(this);
var childSelected = $this.find(':checkbox:checked').length;
if (!childSelected) {
$this.prev(':checkbox').prop('checked', false);
}
});
}
});
Modifications and explanations
$ is exactely the same as jQuery. Be consistent when using it: jQuery should only be used when you're not sure whether $ === jQuery (is $ overwritten?).
:checkbox is a selector which matches every input[type="checkbox"]. It's obsolete to specify input:checbkox, since a checkbox element is always an input element.
.find(..) seeks for any element which is a child (not necessery the direct child) of the matched selector. "#treeList :checkbox" is faster, and has the equivalent result as $('#treeList').find(':checkbox').
When a property/method/event is added to a jQuery object, all elements which match the selector will be modified. Therefore, you don't have to loop through each element individually: jQuery.each(jQuery('#treeList :checkbox'), function(){ jQuery(this).change(...)}) can be shortened to jQuery('#treeList :checkbox').change(...).
You don't have to trigger change after changing a checkbox check state, because the function already takes care of the full tree.

Old question but you could just do it like this:
$('input[type=checkbox]').change(function(){
$(this).next().find('input[type=checkbox]').prop('checked', this.checked);
$(this).parents('ul').prev('input[type=checkbox]').prop('checked', function(){
return $(this).next().find(':checked').length;
});
});
Fiddle: http://jsfiddle.net/ojc1qg1f/

Related

JQuery: Checkbox Chain not working properly

I have 'chain' of checkboxes (parent checkbox and childs), and problem is:
When first clicking on 'parent' checkbox it is working well, but after that when clicking on 'childs', the 'parent' checkbox then isn't doing what is supposed. Parent is checking/unchecking childs except the child which was pressed before.
Here is code:
JavaScript
checks_bind();
function checks_bind(){
$("#x_main").off('click');
$("#x_main").on('click',function(){
var obj = $(this);
var val = obj.is(':checked');
$("#checks").find("input[type='checkbox']").attr('checked',val);
});
}
HTML
<input id='x_main' type='checkbox'/>Main<br>
<p>--------------------------------</p>
<div id='checks'>
<input type='checkbox'/>1<br>
<input type='checkbox'/>2<br>
</div>
<p>--------------------------------</p>
<i>1 - Click on 1 or 2 <br>2 - Try <b>Main</b> checkbox. <br>
3 - Main checkbox isn't working</i>
jsfiddle example
And one more question:
Is it good to use .on('click.namespace') on checkboxes since it's working well? I can use .change() method, but I want to call .off('click.namespace') (or something to unbind) before .on() each time when calling the function.
As checked is a property, You need to use .prop() instead of .attr()
$("#checks").find("input[type='checkbox']").prop('checked', val);
Updated Fiddle, A good read .prop() vs .attr()
If you want to use .off() then its advisable to use namespaced event.
Try this: user 'prop' instead of attribute and you can check all or uncheck all as per checked condition of main check box.
Also, you can check the count of all checkbox to check /uncheck main checkbox. see below
Note: bind click handler when DOM is ready hence user $(document).ready or $(function(){})
$(function(){
$("#x_main").on("change", function(){
$("#checks").find("input[type='checkbox']").prop("checked",$(this).is(":checked"));
});
$("#checks input[type='checkbox']").on("change", function(){
var total = $("#checks").find("input[type='checkbox']").length;
var checked = $("#checks").find("input[type='checkbox']:checked").length;
$("#x_main").prop("checked",total==checked);
});
});
JSFiddle Demo

Update CSS onDomReady for nested checkboxes

I have some nested checkboxes that are loading correct data from the database, however I would like to put a line through the text label for the ones that are checked. It's working fine for the second level checkboxes, but I haven't been able to make the first level checkboxes work fine.
You can see the bug here:
http://jsfiddle.net/Zfv7h/3/
$(document).ready(function() {
$(".content-finalized").each(function() {
if ($(this).attr("checked")) {
$(this).closest("li").css("text-decoration", "line-through");
}
});
$(".subchapter-finalized").each(function() {
if ($(this).attr("checked")) {
$(this).closest("li").css("text-decoration", "line-through");
}
});
});
Thanks for your help.
UPDATED ANSWER
Change your Javascript to this
$(document).ready(function () {
$("input.content-finalized:checkbox:checked").next('span').css("text-decoration", "line-through");
$("input.subchapter-finalized:checkbox:checked").parent().css("text-decoration", "line-through");
});
I had to slightly change your html markup. All I did was wrap the Chapter in a .
Here's why. When I applied the css line-though to the parent <li>, it also applied it to it's children even though I specified the text-decoration to none for unchecked elements. So, now instead of applying it to the parent <li>, I'm applying it to the <span> instead.
Here's an updated fiddle

Deleting <li> issue

I'm building a recipe saving application where I have a form that looks like this http://jsfiddle.net/LHPbh/.
As you can see, I have a set of form elements contained in an <li>. You can click Add Ingredient and have more li's added to the field.
My problem is:
The first li is the only one that deletes. If you click Add Ingredient, and then try and delete that one, nothing works?
Is there a way to not have the first li have a delete by it, but all subsequent li's have a delete link on the side? (Just because there should always be at least one ingredient?)
When you call clone(), it isn't duplicating the events. You need to call clone(true) in order for it to do this, as explained in the documentation.
You did not put an event listener on the cloned elements. Also, you should not give the "delete"-link its own id, as those need to be unique.
To make the first ingredient have no delete button, just don't include one in your markup but only dynamically create and append them to the cloned elements:
var deleteButton = $("<a class='float-left'>Delete</a>").click(deleteThis);
$('ul#listadd > li:first')
.clone()
.attr('name', 'ingredient' + newNum)
.append(deleteButton)
.appendTo('ul#listadd');
function deleteThis() {
var li = $(this).closest('li')
li.fadeOut('slow', function() { li.remove(); });
}
Demo at jsfiddle.net
http://jsfiddle.net/LHPbh/2/
$('.deleteThis').live("click", function () {
var li = $(this).closest('li')
li.fadeOut('slow', function() { li.remove(); });
});
It is answer to the 1. point. The problem was, that the eventhandler binding did not happen in newly created elements, because this code runs only on the load of the page. This can be solved by using .live(). And an other problem was, that id-s must be unique. So instead id, here you can use class .deleteThis.
http://jsfiddle.net/LHPbh/19/
This has added answer to the 2. point:
if ($("#listadd li").length == 1) {
return;
}
If the list only contains 1 li element the rest of the callback will not run.
You are adding items that are added to the DOM dynamically, thus jQuery can't access them :)
In this case you can use the following code:
$(document).on('click', '.selector', function(e) {
//code here
});
Secondly, you were loading a quite old version of jQuery.
Thirdly, you were trying to select an element with an ID that already existed, and ID's can only exist one time. I've changed it to a class in the updated example.
Lastly, you were defining the class of the link twice like this:
<a class='float-left' id="deletethis" href='#' class="deletethis">Delete</a>
That also gave a problem, so I changed it to correct markup like this:
<a class='float-left deletethis' href='#'>Delete</a>
Good luck :) I've updated your jsFiddle here:
http://jsfiddle.net/q4pf6/

check for a value in all the child divs of a particular parent div

In my project. I am trying to check whether a certain number already present in any other adjacent div's which are present in a parent div.
//generated dynamically using jQuery
<div> //parent div
//many div's
//one of the child div is selected by the user.
</div>
<div> //another parent div
//my function is checking one of these div's instead of the above, rarely
</div>
I used the following below code. but it sometimes(rarely) selecting the another parent div's child elements which I dont want to check for (I checked with debugger in chrome). and if sometimes even if the function selects the correct parent div it is always selecting the same child div element which is selected by the user.
jQuery
function checkInGroup(){
var selectedDiv = $('#' + _selectedDivId); //updated after engineer's comment
selectedDiv.parent("div").children().each(function(){
debugger;
if(this !== selectedDiv[0]){ //selecting the same div
var self = $(this);
if(self.text() == _randomNumber){
debugger; //never executing
//do some function here
}
}
});
}
EDIT: I am not sure why my code is not working when it is working fine in jsbin. I need to check why so.. It is always checking with the same user selected div?? Hence the function is not entering into the if statement..
The debugger which is present inside the if statement is never executing. and also the each function loop is happening only once with the same selected div (hence the function is not entering in to the if statement)
Here is the jsfiddle
The $.each() function has an actual signature of $.each([Array], index, element). In your case:
function checkInGroup(){
var selectedDiv = $('#' + _selectedDivId);
$.each(selectedDiv.parent("div").children(), function(index, element){
debugger;
if($(element) !== selectedDiv){ //selecting the same div
if($(element).text() == _randomNumber){
//do some function here
}
}
});
}
try:
selectedDiv.parent("div").children().not(selectedDiv).each(function(){
//your code
});
Use siblings .
$('#' + _selectedDivId).siblings().each(function(){
});
Updated:
selectedDiv id is not actually div it is button id. that's the problem. please
change your code like below.
selectedDiv.parent("div").parent("div").find("button").each(function(){
demo
selectedDiv may be an array in your example.
I don't think you can compare two jQuery objects like this.
You could consider using data attributes?
e.g.
var selectedDiv = $('#' + _selectedDivId);
selectedDiv.data('selected', true);
then
selectedDiv.parent("div").children().each(function(){
debugger;
if($(this).data('selected') !== true){ //selecting the same div
if($(this).text() == _randomNumber){
//do some function here
}
}
});
Just remember to set selected to false on the div when it's deselected.
There's probably a more efficient way to do this but this should work.

Javascript/html: How to store div, and hide its children later

I am storing a div which gets selected
var selectedCell = null;
$(".selectableBox").on('click', function (event) {
selectedCell = $(this);
}
Later I want to hide one of selectableCell's children name selectableCellChild
$('#deleteConfirmed').on('click', function (event) {
selectedCellList.($'selectableCellChild').hide();
});
How can I correctly hide this child div? I know the syntax of the above example isn't right, and I have tried it many ways including using children() and next() methods of selectedCellList
selectedCellList.find('{selectableCellChild}').hide();
Where selectableCellChild is a placeholder for the real selector of the cell.
I have tried it many ways including using children() and next()
children - traverse only one level deep.
find - traverse the all the DOM levels deep.
next select the next immediate sibling.
For the second part, this is what you want:
$('#deleteConfirmed').on('click', function (event) {
$(selectedCellList).find('.selectableCellChild').hide();
});
If I understood correctly, you are trying to hide the children of clicked div. Try like below,
var selectedCell = null;
$(".selectableBox").on('click', function (event) {
selectedCell = $(this);
}); //Your missed );
$('#deleteConfirmed').on('click', function (event) {
//v-- Changed from selectedCellList to selectedCell as this is the clicked div.
selectedCell.find('.selectableCellChild').hide();
//assuming selectableCellChild-^ is class of child elements in the clicked div
});
Use .find.
selectedCellList.find('selectableCellChild').hide(); // I hope selectableCellChild isn't your real selector, it won't work
Also, when declaring your variable, make it a jQuery object since you intend to store a jquery object in it to avoid undefined method errors.
var selectedCell = $();

Categories

Resources