There is no way to have a tri-state check button (yes, no, null) in HTML, right?
Are there any simple tricks or work-arounds without having to render the whole thing by oneself?
There is no way to have a tri-state check button (yes, no, null) in HTML, right?
Are there any simple tricks or work-arounds without having to render the whole thing by oneself?
Edit — Thanks to Janus Troelsen's comment, I found a better solution:
indeterminateSee w3c reference guide. To make checkbox appear visually indeterminate set it to true:
element.indeterminate = true;
Here is Janus Troelsen's fiddle. Note, however, that:
The indeterminate state cannot be set in the HTML markup, it can only be done via Javascript (see this JSfiddle test and this detailed article in CSS tricks)
This state doesn't change the value of the checkbox, it is only a visual cue that masks the input's real state.
Browser test: Worked for me in Chrome 22, Firefox 15, Opera 12 and back to IE7. Regarding mobile browsers, Android 2.0 browser and Safari mobile on iOS 3.1 don't have support for it.
Previous answer
Another alternative would be to play with the checkbox transparency for the "some selected" state (as Gmail
doesused to do in previous versions). It will require some javascript and a CSS class. Here I put a particular example that handles a list with checkable items and a checkbox that allows to select all/none of them. This checkbox shows a "some selected" state when some of the list items are selected.Given a checkbox with an ID
#select_alland several checkboxes with a class.select_one,The CSS class that fades the "select all" checkbox would be the following:
.some_selected { opacity: 0.5; filter: alpha(opacity=50); }And the JS code that handles the tri-state of the select all checkbox is the following:
$('#select_all').change (function () { //Check/uncheck all the list's checkboxes $('.select_one').attr('checked', $(this).is(':checked')); //Remove the faded state $(this).removeClass('some_selected'); }); $('.select_one').change (function () { if ($('.select_one:checked').length == 0) $('#select_all').removeClass('some_selected').attr('checked', false); else if ($('.select_one:not(:checked)').length == 0) $('#select_all').removeClass('some_selected').attr('checked', true); else $('#select_all').addClass('some_selected').attr('checked', true); });You can try it here: http://jsfiddle.net/98BMK/
Hope that helps!
Here is a runnable example using the mentioned indeterminate attribute:
const indeterminates = document.getElementsByClassName('indeterminate');
indeterminates['0'].indeterminate = true;
<form>
<div>
<input type="checkbox" checked="checked" />True
</div>
<div>
<input type="checkbox" />False
</div>
<div>
<input type="checkbox" class="indeterminate" />Indeterminate
</div>
</form>
Just run the code snippet to see how it looks like.
Here other Example with simple jQuery and property data-checked:
$("#checkbox")
.click(function(e) {
var el = $(this);
switch (el.data('checked')) {
// unchecked, going indeterminate
case 0:
el.data('checked', 1);
el.prop('indeterminate', true);
break;
// indeterminate, going checked
case 1:
el.data('checked', 2);
el.prop('indeterminate', false);
el.prop('checked', true);
break;
// checked, going unchecked
default:
el.data('checked', 0);
el.prop('indeterminate', false);
el.prop('checked', false);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label><input type="checkbox" name="checkbox" value="" checked id="checkbox"> Tri-State Checkbox </label>
As I needed something like this -without any plug-in- for script-generated checkboxes in a table... I ended up with this solution:
<!DOCTYPE html>
<html>
<body>
Toto <input type="checkbox" id="myCheck1" onclick="updateChkBx(this)" /><br />
Tutu <input type="checkbox" id="myCheck2" onclick="updateChkBx(this)" /><br />
Tata <input type="checkbox" id="myCheck3" onclick="updateChkBx(this)" /><br />
Tete <input type="checkbox" id="myCheck4" onclick="updateChkBx(this)" /><br />
<script>
var chkBoxState = [];
function updateChkBx(src) {
var idx = Number(src.id.substring(7)); // 7 to bypass the "myCheck" part in each checkbox id
if(typeof chkBoxState[idx] == "undefined") chkBoxState[idx] = false; // make sure we can use stored state at first call
// the problem comes from a click on a checkbox both toggles checked attribute and turns inderminate attribute to false
if(chkBoxState[idx]) {
src.indeterminate = false;
src.checked = false;
chkBoxState[idx] = false;
}
else if (!src.checked) { // passing from checked to unchecked
src.indeterminate = true;
src.checked = true; // force considering we are in a checked state
chkBoxState[idx] = true;
}
}
// to know box state, just test indeterminate, and if not indeterminate, test checked
</script>
</body>
</html>
A short snippet using an auxiliary variable and indeterminate:
cb1.state = 1
function toggle_tristate(cb) {
cb.state = ++cb.state % 3 // cycle through 0,1,2
if (cb.state == 0) {
cb.indeterminate = true;
cb.checked = true; // after 'indeterminate' the state 'false' follows
}
}
<input id="cb1" type="checkbox" onclick="toggle_tristate(this)">
Only state==0 is captured. The rest is handle automatically.
Building on the answers above using the indeterminate state, I've come up with a little bit that handles individual checkboxes and makes them tri-state.
MVC razor uses 2 inputs per checkbox anyway (the checkbox and a hidden with the same name to always force a value in the submit). MVC uses things like "true" as the checkbox value and "false" as the hidden of the same name; makes it amenable to boolean use in API calls. This snippet uses a third hidden state to persist the last request values across submits.
Checkboxes initialized with the below will start indeterminate. Checking once turns on the checkbox. Checking twice turns off the checkbox (returning the hidden value of the same name). Checking a third time returns it to indeterminate (and clears out the hidden so a submit will produce a blank).
The page also populates another hidden (e.g., triBox2Orig) with whatever value was on the query string to start, so the 3 states can be initialized and persisted between submits.
$(document).ready(function () {
var initCheckbox = function (chkBox)
{
var hidden = $('[name="' + $(chkBox).prop("name") + '"][type="hidden"]');
var hiddenOrig = $('[name="' + $(chkBox).prop("name") + 'Orig"][type="hidden"]').prop("value");
hidden.prop("origValue", hidden.prop("value"));
if (!chkBox.prop("checked") && !hiddenOrig) chkBox.prop("indeterminate", true);
if (chkBox.prop("indeterminate")) hidden.prop("value", null);
chkBox.change(checkBoxToggleFun);
}
var checkBoxToggleFun = function ()
{
var isChecked = $(this).prop('checked');
var hidden = $('[name="' + $(this).prop("name") + '"][type="hidden"]');
var thirdState = isChecked && hidden.prop("value") === hidden.prop("origValue");
if (thirdState) { // on 3rd click of a checkbox, set it back to indeterminate
$(this).prop("indeterminate", true);
$(this).prop('checked', false);
}
hidden.prop("value", thirdState ? null : hidden.prop("origValue"));
};
var chkBox = $('#triBox1');
initCheckbox(chkBox);
chkBox = $('#triBox2');
initCheckbox(chkBox);
});