Grow your CSS skills. Land your dream job.

Indeterminate Checkboxes

Published by Chris Coyier

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 at all 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).


View Demo   Download Files

This demo isn't perfect. It only checked one level "up" for determining indeterminate state. Ideally it would check up recursively until the top. If you wanna fix it to do that, I'll update the code and credit you.

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);
                
        }
        
    });

View Demo

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.

<!-- Ghetto click handler -->
<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;
}

View Demo

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. kuya
    Permalink to comment#

    whats the browser support for this?

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

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

  5. Florian
    Permalink to comment#

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

    • 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.

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

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

    Thanks!

  8. 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

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

  10. 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

  11. Thanks! Learn something new every day!

  12. Permalink to comment#

    You learn something new… sometimes.

  13. Anil Jadhav
    Permalink to comment#

    excellent, was not aware of this trick,

  14. 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++;
                      }
              }
                
            }
        });
        });
    
  15. 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.

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

  17. 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.

  18. jason t
    Permalink to comment#

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

  19. Permalink to comment#

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

  20. Jason
    Permalink to comment#

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

  21. Akbar
    Permalink to comment#

    Beautiful new design!

  22. 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. ;)

    • 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!

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

  24. 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++;
                      }
              }
    
            }
        });
        });
  25. It works fine in firefox and ie but i am having issues with chrome.Is any quick fix available?

  26. 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?

  27. 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 !!!

  28. 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);
    });
    
    });
    
  29. 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.

  30. 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, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".