How to check parent when all children checkboxes are checked - javascript

I have a rather different HTML structure to other questions similar to this on SO. I want to make the parent checkbox checked when all children checkboxes are checked. I have a JSFiddle where I have added my rather long HTML code. What am I doing wrong in the Jquery? This is my jquery:
$('li :checkbox').on('click', function() {
var $chk = $(this),
$container = $chk.closest('td,li');
if (!$container.hasClass('list-group-item')) {
$container.find(':checkbox').not(this).prop('checked', this.checked)
}
do{
$ul = $container.parent();
$parent = $ul.siblings(':checkbox');
if($chk.is(':checked')){
$parent.prop('checked', $ul.has(':checkbox:not(:checked)').length == 0)
} else {
$parent.prop('checked', false)
}
$chk = $parent;
$container = $chk.closest('td,li');
} while($ul.is(':not(.someclass)'));
});
PS: The parent/child relationship in the bottom half of the HTML structure don't work well coz of some unclosed tags. But the first few parent/children are working

The example below should help you understand and give you a way to accomplish what you want.
Explanation
What you should do is give each sub-category an ID which can be used to iterate through the children so the only those checkboxes can be checked and not all of them.
HTML
<ul>
<li>
<input type="checkbox" id="test-main"> test
</li>
<ul id="test-sub-items">
<li>
<input type="checkbox"> test
</li>
<li>
<input type="checkbox"> Something
</li>
<li>
<input type="checkbox"> Another Thing</li>
</ul>
<li>
<input type="checkbox"> Blah
</li>
<li>
<input type="checkbox"> blah
</li>
</ul>
JavaScript
var parent = document.getElementById('test-sub-items'),
parentCheckbox = document.getElementById('test-main');
parentCheckbox.addEventListener('click', function () {
for (var i = 0; i < parent.children.length; i++) {
if (parentCheckbox.checked == true)
parent.children[i].firstElementChild.checked = true;
else
parent.children[i].firstElementChild.checked = false;
}
});
This is simply an example for you to try and incorporate in to your code.
Update
After reading your comment I quickly came up with this function which should help:
function areAllChecked()
{
for (var i = 0; i < parent.children.length; i++) {
if (parentCheckbox.checked == false)
return false;
}
return true;
}
Check the console in the live example for the updated version.
Live Example
JSFiddle
JSFiddle (Update 1)
Reading Material
children
firstElementChild

Because i did not find a way any where else i tried with trial and error on the example of #Script47
And got it to work with that:
var child = document.getElementById('test-sub-items'), parentCheckbox = document.getElementById('test-main');
child.addEventListener('click', function() {
for (var i = 0; i < child.children.length; i++) {
if (child.children[i].firstElementChild.checked == true)
parentCheckbox.checked = true;
else
parentCheckbox.checked = false;
}
});
<ul>
<li>
<input type="checkbox" id="test-main"> Essen
</li>
<ul id="test-sub-items">
<li><input type="checkbox"> Apfel</li>
<li><input type="checkbox"> Birne </li>
<li><input type="checkbox"> Cadmium</li>
</ul>
<li><input type="checkbox">Bier</li>
<li><input type="checkbox">C hohes</li>
</ul>

Related

Show a message when checking a checkbox and show another message when unchecking it

I'm fairly new in Javascript/AngularJS and want to play around with it to learn/understand these languages. On the Angular website there's a little basic to do list script I want to change a little. I've changed it into a checklist of things I need to learn. When i check the checkbox I got a little msg like, 'well done!'. But when I uncheck it, the same msg pops up and that I want to change in something like, 'you sure?'.
I've spent several hours on looking for the answer but couldn't find the answer so I hope someone can help me figuring out how to do this. This is my code:
HTML:
<div ng-controller="TodoListController as todoList">
<span>Nog {{todoList.remaining()}} van de {{todoList.todos.length}} te gaan!</span>
<!--[ archive ]-->
<ul class="unstyled">
<li ng-repeat="todo in todoList.todos">
<label class="checkbox">
<input type="checkbox" onclick="alert('Well done!')" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</label>
</li>
</ul>
</div> <!--einde ng-controller -->
AngularJS code:
todoList.remaining = function() {
var count = 0;
angular.forEach(todoList.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
todoList.archive = function() {
var oldTodos = todoList.todos;
todoList.todos = [];
angular.forEach(oldTodos, function(todo) {
if (!todo.done) todoList.todos.push(todo);
});
};
If there's missing something, let me know!
Thanks in advance!
Here's the updated code for you:
<div ng-controller="TodoListController as todoList">
<span>Nog {{todoList.remaining()}} van de {{todoList.todos.length}} te gaan!</span>
<!--[ archive ]-->
<ul class="unstyled">
<li ng-repeat="todo in todoList.todos">
<label class="checkbox">
<input type="checkbox" onclick="showAlert(todo.done)" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</label>
</li>
</ul>
</div> <!--einde ng-controller -->
AngularJs Code:
todoList.remaining = function() {
var count = 0;
angular.forEach(todoList.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.showAlert = function(isSuccess){
if(isSuccess)
alert('Well done!')
else
alert('Unchecked')
};
todoList.archive = function() {
var oldTodos = todoList.todos;
todoList.todos = [];
angular.forEach(oldTodos, function(todo) {
if (!todo.done) todoList.todos.push(todo);
});
};

multiple filters based on checkbox and divs id's and classes

The idea is to filter divs by comparing dynamically generated classes for divs and dynamically generated id's for checkboxes, where only divs with classes matching id's of checked checkboxes would show.
First subnav filters everything fine. However, subnav2 dosen't do anything.
Simplified html of it
<div class="itemswrap">
<div class="inditem dynamic1 dynamic12"></div>
<div class="inditem dynamic2 dynamic22"></div>
<div class="inditem dynamic3 dynamic32"></div>
<div class="inditem dynamic2 dynamic42"></div>
</div>
<ul class="subnav">
<li class="lifilter">
<input type="checkbox" class="filtercheck" id="dynamic1" />
<label for="dynamic1">whatever label</label>
</li>
<li class="lifilter">
<input type="checkbox" class="filtercheck" id="dynamic2" />
<label for="dynamic1">whatever label</label>
</li>
<li class="lifilter">
<input type="checkbox" class="filtercheck" id="dynamic3" />
<label for="dynamic1">whatever label</label>
</li>
</ul>
<ul class="subnav2">
<li class="lifilter2">
<input type="checkbox" class="filtercheck2" id="dynamic12" />
<label for="dynamic12">whatever label</label>
</li>
<li class="lifilter2">
<input type="checkbox" class="filtercheck2" id="dynamic22" />
<label for="dynamic12">whatever label</label>
</li>
<li class="lifilter2">
<input type="checkbox2" class="filtercheck2" id="dynamic32" />
<label for="dynamic12">whatever label</label>
</li>
</ul>
And the js i've got so far.
var $filters = $('.filtercheck').change(function() {
var $items = $('.inditem').hide();
var filters = $filters.filter(':checked').map(function() {
return '.' + this.id;
}).get();
if (filters.length) {
$items.filter(filters.join()).show();
} else {
$items.show();
}
});
var $filtersb = $('.filtercheck2').change(function() {
var $itemsb = $('.inditem').hide();
var filtersb = $filtersb.filter(':checked').map(function() {
return '.' + this.id;
}).get();
if (filtersb.length) {
$itemsb.filter(filters.join()).show();
} else {
$itemsb.show();
}
});
I see that you fixed it but I had created a jsfiddle for your problem, akin to one I had to face in another project. Basically, you create a string from the checked checkboxes IDs and use that to show only those items that match the elements you checked. If no elements are checked, all items are shown (i.e. no filters are applied).
$("input[type='checkbox']").change(function()
{
var list = "";
$("input[type='checkbox']").each(function()
{
if(this.checked)
{
list = list + '.' + $(this).attr('id');
}
});
if(list !=='')
{
$("div.inditem").hide();
$(list).show();
}
else {
$("div.inditem").show();
}
});

parent child checkbox selection

<input type="checkbox" name="all[]" id="<?php echo $record_id;?>" value="<?php echo $record_id; ?>" onclick="childChecked(this, this.form.elements['prnt'])">
above code creates dynamic child checkboxes for each row by fetching data from database
<input type="checkbox" name="all[]" id="<?php echo $record_id;?>" value="<?php echo $record_id; ?>" onclick="allChecked(this, this.form.elements['prnt'])">
and above is the parent checkbox
javascript code:
function checkAll(){
var main_check=document.getElementById("check");
var all_check=document.getElementsByName('all[]');
if(main_check.checked){
for(var i=0;i<all_check.length;i++){
all_check[i].checked=true;
}
}else{
for(var i=0;i<all_check.length;i++){
all_check[i].checked=false;
}
}
}
function childChecked(child, prnt){
if (!child.length){ // if not an array
prnt.checked = child.checked;
//alert(prnt.checked);
return;
}
for (var i=0; i<child.length; i++){
if (!child[i].checked)
return;
}
prnt.checked = true;
}
above is my code for parent child checkbox selection
it does:
1. checks/unchecks child checkboxes on checking/unchecking parent checkbox
2. unchecks parent checkbox if one of the child checkbox is unchecked
and now my problem is, it checks parent checkbox if we checked a single child checkbox but i want parent checkbox to be checked if and only if all child checkboxes are checked
Your code a can be simplified:
var main_check = document.getElementById("check");
var all_check = document.getElementsByName('all[]');
main_check.onchange = checkAll;
for (var i = 0; i < all_check.length; i++) {
all_check[i].onchange = childChanged;
}
function checkAll() {
for (var i = 0; i < all_check.length; i++) {
all_check[i].checked = main_check.checked;
}
}
function childChanged() {
if (!this.checked) {
main_check.checked = false;
return;
}
// Check if main checkbox should be checked
for (var i = 0; i < all_check.length; i++) {
if (!all_check[i].checked) return;
}
main_check.checked = true;
}
and HTML:
<input type="checkbox" id="check" /> Main
<ul>
<li><input type="checkbox" name="all[]" /></li>
<li><input type="checkbox" name="all[]" /></li>
<li><input type="checkbox" name="all[]" /></li>
<li><input type="checkbox" name="all[]" /></li>
</ul>
I also got rid of inline event handlers.
Demo: http://jsfiddle.net/TXRNF/

Checkbox bind/unbind logic

Hi i have a situation where the binding for a table containing checkbox is done at the header checkbox
i.e when u select the header check all table rows get selected .
but due to this the problem comes when i unselect an row from the table the header chekcbox should get unselected which is not happening
and suppose i uncheck the header checkbox entire table row will be unchecked coz of the binding issue
please suggest some logic to solve it
I tried retrieving the table row checkbox value and again rendering it but it's a memory constraint.
Thanks in advance
HTML
<input type="checkbox" id="header" />
<li>
<input type="checkbox" class="sub" />
</li>
<li>
<input type="checkbox" class="sub" />
</li>
<li>
<input type="checkbox" class="sub" />
</li>
JS
var headCheckbox = document.getElementById('header');
var subCheckbox = document.getElementsByClassName('sub');
headCheckbox.addEventListener('change', function (e) {
for (var i = 0; i < subCheckbox.length; i++) {
subCheckbox[i].checked = (this.checked ? true : false);
}
});
for (var i = 0; i < subCheckbox.length; i++) {
subCheckbox[i].addEventListener('change', function (e) {
if (!this.checked) {
headCheckbox.checked = false;
} else {
var checked = 0;
for (var i = 0; i < subCheckbox.length; i++) {
if (subCheckbox[i].checked) checked++;
}
if (checked == subCheckbox.length) headCheckbox.checked = true;
}
});
}
DEMO
You may do like below to
1. Select all checkbox on selecting header checkbox.
2. UnSelect header checkbox on unselecting any child checkbox.
3. Select header checkbox on selecting all child checkbox.
JavaScript :
function selectAll(source)
{
var checkboxes = document.getElementsByName('foo');
for(var i=0, n=checkboxes.length;i<n;i++) {
checkboxes[i].checked = source.checked;
}
}
function selectChk(src)
{
var isAllChecked = true;
var headerChk = document.getElementsByName('headerChk');
if(!src.checked){
headerChk[0].checked = false;
}
else{
var checkboxes = document.getElementsByName('foo');
for(var i=0, n=checkboxes.length;i<n;i++) {
if(!checkboxes[i].checked)
isAllChecked = false;
}
if(isAllChecked)
headerChk[0].checked = true;
}
}
HTML :
<input type="checkbox" onClick="selectAll(this)" name="headerChk" /> Select All<br/>
<input type="checkbox" name="foo" value="bar1" onClick="selectChk(this)"> Bar 1<br/>
<input type="checkbox" name="foo" value="bar2" onClick="selectChk(this)"> Bar 2<br/>
<input type="checkbox" name="foo" value="bar3" onClick="selectChk(this)"> Bar 3<br/>
<input type="checkbox" name="foo" value="bar4" onClick="selectChk(this)"> Bar 4<br/>

What is the best practices to make checkbox tree using indeterminate javascript HTML?

I am developing a checkbox tree where i need three states of checkbox, I know there is two state of checkbox, but i have to use indeterminate
Check
Uncheck
Indeterminate
How can i developed these type of tree using javascript?
This is a trivial one from scratch: http://jsfiddle.net/pimvdb/MgYs7/1/.
var span = document.getElementById('span');
span.setAttribute('data-state', 0);
span.onselectstart = function() { return false }; // prevent selecting, doesn't look too good
span.onclick = function() {
var state = span.getAttribute('data-state') - 0; // convert to number
if(state === 0) { // set next symbol
span.innerHTML = 'V';
} else if(state === 1) {
span.innerHTML = 'X';
} else {
span.innerHTML = '';
}
span.setAttribute('data-state', (state + 1) % 3); // set state; 3 will be 0 again
};
<div id="page-wrap">
<h1>Indeterminate Checkboxes</h1>
<ul>
<li>
<input type="checkbox" name="tall" id="tall">
<label for="tall">Tall Things</label>
<ul>
<li>
<input type="checkbox" name="tall-1" id="tall-1">
<label for="tall-1">Buildings</label>
</li>
<li>
<input type="checkbox" name="tall-2" id="tall-2">
<label for="tall-2">Giants</label>
<ul>
<li>
<input type="checkbox" name="tall-2-1" id="tall-2-1">
<label for="tall-2-1">Andre</label>
</li>
<li>
<input type="checkbox" name="tall-2-2" id="tall-2-2">
<label for="tall-2-2">Paul Bunyan</label>
</li>
</ul>
</li>
<li>
<input type="checkbox" name="tall-3" id="tall-3">
<label for="tall-3">Two sandwiches</label>
</li>
</ul>
</li>
<li>
<input type="checkbox" name="short" id="short">
<label for="short">Short Things</label>
<ul>
<li>
<input type="checkbox" name="short-1" id="short-1">
<label for="short-1">Smurfs</label>
</li>
<li>
<input type="checkbox" name="short-2" id="short-2">
<label for="short-2">Mushrooms</label>
</li>
<li>
<input type="checkbox" name="short-3" id="short-3">
<label for="short-3">One Sandwich</label>
</li>
</ul>
</li>
</ul>
</div>
$(function() {
// Apparently click is better chan change? Cuz IE?
$('input[type="checkbox"]').change(function(e) {
var checked = $(this).prop("checked"),
container = $(this).parent(),
siblings = container.siblings();
container.find('input[type="checkbox"]').prop({
indeterminate: false,
checked: checked
});
function checkSiblings(el) {
var parent = el.parent().parent(),
all = true;
el.siblings().each(function() {
return all = ($(this).children('input[type="checkbox"]').prop("checked") === checked);
});
if (all && checked) {
parent.children('input[type="checkbox"]').prop({
indeterminate: false,
checked: checked
});
checkSiblings(parent);
} else if (all && !checked) {
parent.children('input[type="checkbox"]').prop("checked", checked);
parent.children('input[type="checkbox"]').prop("indeterminate", (parent.find('input[type="checkbox"]:checked').length > 0));
checkSiblings(parent);
} else {
el.parents("li").children('input[type="checkbox"]').prop({
indeterminate: true,
checked: false
});
}
}
checkSiblings(container);
});
});
You can use already developed plugins (or read their code if you are just curious how to manipulate the checkboxes' states).
Just drop one of them. If you choose niTree plugin I can help you with any questions concerning the code:
https://github.com/AlexLibs/niTree
I would use values -1, 0 and 1. Every onclick the attached function checks its value.
-1 = Indeterminate
0 = unchecked
1 = checked.
If 0 change to 1
If 1 change to -1
If -1 change to 0
If the value = -1 add this property (e.g. via jQuery) to the checkbox:
$(checkbox).prop("indeterminate", true);
For every other value the indeterminate value should be false.
See this link for example
Of course your tree logic should act on what its value is going to be e.g. unchecking children or setting parent at indeterminate.

Categories