Tri State Checkbox

A div that mimics checkbox behaviour but with a third state - for use in a treeview where a subset of child nodes are selected.

by Stephen Dunne

HTML

<div id="div1" class="checkbox"></div>
        <div id="div2" class="checkbox" checked="true"></div>
        <div id="div3" class="checkbox"></div>
        <div id="div4" class="checkbox" checked="true"></div>
<br>
<br>
<input type="button" value="ThirdState"  onclick='$("#div1").triStateCheckbox("indeterminate")' />

CSS

.checkbox {
    border: 1px solid silver;
    cursor: pointer;
    width: 16px;
    height: 16px;
}
.ui-icon {
    float: left;
}
.ui-icon-check {
    background-position: -64px -144px;
}
.ui-icon-indeterminate {
    background-position: -80px -144px;
}
.ui-icon-minus {
    background-position: -48px -128px;
}
.ui-icon-none {
    background-position: -160px -144px;
}

JavaScript

$(function () {
    $.widget("cmb.triStateCheckbox", {
        options: {
            state: 0
        },
        _create: function () {
            var ctx = this;
            ctx.element.addClass("ui-icon");

            /*set initial state based on some attribute value*/
            ctx._setOption("state", (ctx.element.attr("checked") != null ? 1 : 2));

            ctx._on(ctx.element, {
                click: ctx._click
            });
        },
        _setOption: function (key, value) {
            var ctx = this;
            this._super(key, value);
            switch (key) {
                case "state":
                    TriStateCheckbox.updateStyle(ctx);
                    break;
            }
        },
        _click: function (e) {
            var ctx = this;
            if (ctx.options.state === 1) {
                ctx._setOption("state", 2);
            } else {
                ctx._setOption("state", 1);
            }
        },
        indeterminate: function () {
            var ctx = this;
            ctx._setOption("state", 0);
        }
    });
    
    var TriStateCheckbox = {
        states: ["Indeterminate", "Checked", "Unchecked"],
        styles: ["ui-icon-indeterminate", "ui-icon-check", "ui-icon-none"],

        thirdState: function (ctx) {
            ctx._setOption("state", 0);
            TriStateCheckbox.updateStyle(ctx);
        },
        updateStyle: function (ctx) {
            //                    var ctx = this;
            //this.element.text(this.state);
            ctx.element.removeClass("ui-icon-indeterminate");
            ctx.element.removeClass("ui-icon-check");
            ctx.element.removeClass("ui-icon-none");
            ctx.element.addClass(TriStateCheckbox.styles[ctx.options.state]);
        }
    };
    $(".checkbox").triStateCheckbox();
});