Indeterminate Checkboxes

Chris Coyier on Updated on

Checkbox inputs can only have two states: checked or unchecked. They can have any value, but they either submit that value (checked) or don’t (unchecked) with a form submission. The default is unchecked. You can control that in HTML like this:

<!-- Default to unchecked -->
<input type="checkbox">
<!-- Default to checked, XHTML -->
<input type="checkbox" checked="checked" />
<!-- Default to checked, HTML5 -->
<input type="checkbox" checked>

Visually, there are actually three states a checkbox can be in: checked, unchecked, or indeterminate. They look like this:

Here are some things to know about indeterminate checkboxes:

You can’t make a checkbox indeterminate through HTML. There is no indeterminate attribute. It is a property of checkboxes though, which you can change via JavaScript.

var checkbox = document.getElementById("some-checkbox");
checkbox.indeterminate = true;

or jQuery style:

$("#some-checkbox").prop("indeterminate", true); // prop is jQuery 1.6+

The indeterminate state is visual only. The checkbox is still either checked or unchecked as a state. That means the visual indeterminate state masks the real value of the checkbox, so that better make sense in your UI!

Like the checkboxes themselves, indeterminate state looks different in different browsers. Here’s Opera 11.50 on Mac:

Use Case

The reason I’m writing this is because I just had a use case come up for this state: nested checkboxes. Each checkbox may have child checkboxes. If all those children are checked, it may be checked. If none are checked, it is unchecked. If some of them are checked, then it’s in an indeterminate state (in this case symbolically meaning “partially” checked).

Here’s that in jQuery:

See the Pen Indeterminate Checkboxes by Chris Coyier (@chriscoyier) on CodePen.

Here’s an alternate jQuery version from TheNotary.

And here’s a plain JavaScript version from Jakob Eriksen:

See the Pen
Indeterminate Checkboxes (Vanilla JS)
by jakob-e (@jakob-e)
on CodePen.

And a couple of alternate approachs from Alvaro Montoro , Bramus, Gridbuilder, Lewin Probst, and Jason Wilson.

Using StimulusJS from Stephen Margheim:

See the Pen
checkbox family
by Stephen Margheim (@smargh)
on CodePen.

Rotating amongst the states

Jon Stuebe was messing around with the idea of rotating the state between unchecked, indeterminate, and checked with a click. Here’s some jQuery to do that:

var $check = $("input[type=checkbox]"), el;
$check
 .data('checked',0)
 .click(function(e) {
 
 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);
 
 }
 
 });

See the Pen Rotate Through Indeterminate Checkboxes by Chris Coyier (@chriscoyier) on CodePen.

Reader Casual Trash sent me in a library-free and far more succinct version of rotating through all three visual states which utilizes the readonly attribute that checkbox inputs can have.

<!-- Inline click handler, just for demo -->
<input type="checkbox" id="cb1" onclick="ts(this)">
function ts(cb) {
 if (cb.readOnly) cb.checked=cb.readOnly=false;
 else if (!cb.checked) cb.readOnly=cb.indeterminate=true;
}

See the Pen Rotate Through Indeterminate Checkboxes by Chris Coyier (@chriscoyier) on CodePen.

Psst! Create a DigitalOcean account and get 200ドル in free credit for cloud-based hosting and services.

Comments

  1. seelts
    Permalink to comment#

    Thanks,

    Just tried to find how to implement it some days ago.

    Have decided to make like in Gmail: opacity: .5;

    Now thinking of rewriting the code.

  2. Pelshoff
    Permalink to comment#

    Thanks for sharing this, I never knew :)

    You open up saying checkboxes only have two values. However, checkboxes have the attribute ‘value’ as well. Did you mean something like ‘state’?

    http://w3schools.com/html5/att_input_value.asp

    • Dalibor Sojic
      Permalink to comment#

      I agree… small mistake by Chris, I am sure he did it in hurry.

      However, great lesson.

  3. kuya
    Permalink to comment#

    whats the browser support for this?

    • Chris
      Permalink to comment#

      I did a little testing and i found it works as far back as IE 6

      PS: Nice tutorial Chris, i saw this somewhere recently not as detailed as this.

  4. Jeremy Worboys
    Permalink to comment#

    Out of curiosity: what value would an indeterminate checkbox return in this example?

    • Pelshoff
      Permalink to comment#

      The indeterminate attribute is not related to check/unchecked or the value attribute. In indeterminate, checked checkbox with value 42 would return 42, an indeterminate, unchecked checkbox with value 37 would return nothing.

  5. Salman Saeed
    Permalink to comment#

    I don’t think Chrome allow us to do it :/

  6. Smasty
    Permalink to comment#

    The reason checkboxes look so ugly in Opera in your demo is this CSS rule:

    * { margin: 0; padding: 0; }

    If you try to style checkboxes (and radios as well) in Opera, they will get that ugly “Windows 98” look.

  7. Florian
    Permalink to comment#

    Whydid you use .prop() instead of .attr() ?

    • Louis
      Permalink to comment#

      I’m not an expert in this area — .prop() is new to me — but here’s what the documentation says:

      The difference between attributes and properties can be important in specific situations. Before jQuery 1.6, the .attr() method sometimes took property values into account when retrieving some attributes, which could cause inconsistent behavior. As of jQuery 1.6, the .prop() method provides a way to explicitly retrieve property values, while .attr() retrieves attributes.

  8. George Dina
    Permalink to comment#

    Didn’t knew this.
    Thanks a lot for sharing.

  9. Joss Crowcroft
    Permalink to comment#

    Wow, that’s crazy! I had no idea browsers supported this at all.

    Thanks!

  10. Hamza Sağ
    Permalink to comment#

    hi chris,

    i don’t know if is that normal
    first check “giants” than uncheck “andre” and “paul bunyan” than see what happens

    • Chris Coyier
      Permalink to comment#

      See last sentence.

    • Axel
      Permalink to comment#

      Well, I see a lot of logic in this… If none of the childrens are selected then it should be no reason for the parent to be checked.

      It’s a very logic limitation….

  11. Web Design Leicester
    Permalink to comment#

    Been using checkboxes and other form elements for over 10 years, but I guess you learn something new everyday!

  12. Eric Barnes
    Permalink to comment#

    The best use case I have seen for this is in the git-tower app. They use the indeterminate checkbox when you have only part of a file staged.

    Here is a screenshot showing it:
    http://bit.ly/ornzBG

  13. Jordan Foreman
    Permalink to comment#

    Thanks! Learn something new every day!

  14. Liam
    Permalink to comment#

    You learn something new… sometimes.

  15. Anil Jadhav
    Permalink to comment#

    excellent, was not aware of this trick,

  16. James Collins
    Permalink to comment#

    My Javascript/jQuery knowledge is supremely limited, but I wanted to take a shot at making changes recursive. There’s still problems with it. Someone else could probably make it way better.

    
     $(function() {
     // Apparently click is better chan change? Cuz IE?
     $("input[type=checkbox]").click(function() {
     
     var el = $(this); 
     
     el	
     .parent()
     .find("ul input[type=checkbox]")
     .prop("indeterminate", false)
     .prop("checked", el.prop("checked"));
     
     var siblings = el.parent().siblings().add(el.parent());
     
     if (siblings.length > 1) {
     
     var parents = el.parents(); 
     
     var numChecked = 0; 
     
     siblings.find("> input[type=checkbox]").each(function(i) { 
     if ($(this).prop("checked")) {
     numChecked++;
     } 
     }); 
     
     if ((numChecked > 0) && (numChecked < siblings.length)) { 
     var level = 0;
     while (parents.length > level) {
     	el
     	.parent().parent().parents()
     	.find("> input[type=checkbox]")
     	.prop("indeterminate", true)
     	.prop("checked", false);
     	level++;
     }
     } else if (numChecked == 0) {
     var level = 0;
     while (parents.length > level) {
     	el
     	.parent().parent().parents()
     	.find("> input[type=checkbox]")
     	.prop("indeterminate", false)
     	.prop("checked", false);
     	level++;
     } 
     } else { 
     var level = 0;
     while (parents.length > level) {
     	el
     	.parent().parent().parents()
     	.find("> input[type=checkbox]")
     	.prop("indeterminate", false)
     	.prop("checked", true);
     	level++;
     }
     }
     
     }
     });
     });
    
  17. IT Mitică
    Permalink to comment#

    I keep thinking about another way:

    -event Delegation with jQuery:

    $(‘ul’).delegate(‘li’, ‘click’, function(event) {
    // this == li element
    });

    -a multidimensional array keeping track of all the <li>s current states, default/start all columns and rows 0, changed on click

    in order to provide a mechanism to add or remove the indeterminate class name to all li that need it.

    I’ll see if I can put together some code tomorrow.

  18. Johnny Simpson
    Permalink to comment#

    Oh my god! I didn’t even know this was possible! Great :D

  19. Roflo
    Permalink to comment#

    Chris,

    The label for “Short Things” seems wrong:
    <label for="tall">Short Things</label>
    Shouldn’t it be <label for="short">...?

    Anyway.. clicking on the text “Short Things” activates the “Tall Things” checkbox.

    • Clare
      Permalink to comment#

      It’s just a typo :)

      if you change it to ‘short’ (obviously in the second instance) it works as expected; that is, it selects all the Short Things’.

    • Chris Coyier
      Permalink to comment#

      fixed.

  20. David Harsha
    Permalink to comment#
  21. jason t
    Permalink to comment#

    Doesn’t seem to work on my nexus one running android.

  22. Amit
    Permalink to comment#

    Great article and an awesome new design to the site, Chris! Really great design.

  23. Jason
    Permalink to comment#

    New site looks good, not a fan on the buttons and the rainbow hover though

  24. Akbar
    Permalink to comment#

    Beautiful new design!

  25. Michael
    Permalink to comment#

    I wonder if there’s any way of retrieving that indeterminate status after a form submit.
    This could come in handy when interpreting some kind of survey to determine if the checkbox was clicked at all (set to true or false) or it was completely ignored, and thus not even clicked once.

    Chris, do you have any information on this one?

    I bet there would be a gain in value for wufoo in this, too. ;)

    • Chris Coyier
      Permalink to comment#

      Since you have to set it with JS, you’d have to alter the actual value of the checkbox somehow. So like value = value + “-indeterminate”.

    • Michael
      Permalink to comment#

      Yeps,
      I guess a workaround would do the job since you’re using JS for the fiddling already.

      Maybe HTML6 will do it. ;)

      Thanks for the quick response!

  26. Freelance Website Development
    Permalink to comment#

    Boolean input fields with more than 2 states?
    This is almost as bad as dividing by zero!

  27. sourav
    Permalink to comment#
     $(function() {
     // Apparently click is better chan change? Cuz IE?
     $("input[type=checkbox]").click(function() {
     var el = $(this); 
     el
     .parent()
     .find("ul input[type=checkbox]")
     .prop("indeterminate", false)
     .prop("checked", el.prop("checked"));
     var siblings = el.parent().siblings().add(el.parent());
     if (siblings.length > 1) {
     var parents = el.parents(); 
     var numChecked = 0; 
     siblings.find("> input[type=checkbox]").each(function(i) {
     if ($(this).prop("checked")) {
     numChecked++;
     }
     }); 
     if ((numChecked > 0) && (numChecked level) {
     el
     .parent().parent().parents()
     .find("> input[type=checkbox]")
     .prop("indeterminate", true)
     .prop("checked", false);
     level++;
     }
     } else if (numChecked == 0) {
     var level = 0;
     while (parents.length > level) {
     el
     .parent().parent().parents()
     .find("> input[type=checkbox]")
     .prop("indeterminate", false)
     .prop("checked", false);
     level++;
     }
     } else {
     var level = 0;
     while (parents.length > level) {
     el
     .parent().parent().parents()
     .find("> input[type=checkbox]")
     .prop("indeterminate", false)
     .prop("checked", true);
     level++;
     }
     }
     }
     });
     });
  28. nirja sahini
    Permalink to comment#

    It works fine in firefox and ie but i am having issues with chrome.Is any quick fix available?

  29. matt
    Permalink to comment#

    looks great, the only thing I noticed is if you click on an indeterminate checkbox it loses its “green” fill/state, which I don’t think is a desired behavior?

  30. Jessy
    Permalink to comment#

    Hello,

    I’d like to find the same thing, but with a “init” function. Than, if some checkboxes are “pre”-checked (pre-populate form ie) => it determines the states of the parents boxes before any changes, with low level to high level priority.

    Any ideas ?

    Thanks a lots !!!

  31. yurii
    Permalink to comment#

    Fixed a bug related to improper installation attribute indeterminate and optimized code. Now it has a smaller volume and removed the useless cycles.

    $(function() {
    $('input:checkbox').change(function(e){
    var check = $(this).prop('checked'),
    container = $(this).parent();
    container.find('input:checkbox').prop({
    indeterminate: false,
    checked: check
    });
    function checkSiblings(el) {
    var all = true,
    parent = el.parent().closest('li');
    el.siblings().each(function(){
    return all = ($(this).children('input:checkbox').prop('checked') === check);
    });
    if(all){
    parent.children('input:checkbox').prop({
    indeterminate: false,
    checked: check
    });
    checkSiblings(parent);
    }else{
    el.parents('li').children('input:checkbox').prop({
    indeterminate: true,
    checked: false
    });
    }
    }
    checkSiblings(container);
    });
    });
    
  32. Lokesh
    Permalink to comment#

    how to achieve for the simple Html.

    <input type="checkbox" value="something" indeterminate/>
    this won’t work
    <input type="checkbox" value="something" indeterminate="true"/>
    either this one
    <input type="checkbox" value="something" indeterminate="indeterminate"/>
    What is the way to make it work.

  33. Sameer
    Permalink to comment#

    The code is failing in one scenario for my code not sure why……

    http://jsfiddle.net/sameerengg/84ajt/6/
    please help.

    • Sameer
      Permalink to comment#

      Found the problem ul was extra removed that working fine now……
      There is one more problem which I found on IE where we have intermediate checked
      when you change that the change event is not fired. I changed it to click event and it is working for me.

This comment thread is closed. If you have important information to share, please contact us.

AltStyle によって変換されたページ (->オリジナル) /