Better Check Boxes with jQuery and CSS

Demo Download

In this short tutorial, we will be creating a replacement for the default browser checkboxes in the form of a simple jQuery plugin. It will progressively enhance your forms but at the same time fall back to the default controls if JavaScript is unavailable.

HTML

The first step is to lay down the structure of the underlying HTML document. We will need a form with checkboxes which we will later be replacing with their enhanced jQuery versions.

index.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Better Check Boxes with jQuery and CSS | Tutorialzine Demo</title>

<link rel="stylesheet" type="text/css" href="css/styles.css" />
<link rel="stylesheet" type="text/css" href="jquery.tzCheckbox/jquery.tzCheckbox.css" />

</head>
<body>

<div id="page">

    <form method="get" action="./">
        <ul>
            <li>
                <label for="ch_effects">Display effects: </label>
                <input type="checkbox" id="ch_effects" name="ch_effects" data-on="Show effects" data-off="Hide effects" />
            </li>
            <li>
                <label for="ch_location">Enable location tracking: </label>
                <input type="checkbox" id="ch_location" name="ch_location" checked />
            </li>
            <li>
                <label for="ch_showsearch">Include me in search results: </label>
                <input type="checkbox" id="ch_showsearch" name="ch_showsearch" />
            </li>
            <li>
                <label for="ch_emails">Email notifications: </label>
                <input type="checkbox" id="ch_emails" name="ch_emails" data-on="ON" data-off="OFF" />
            </li>
        </ul>
    </form>

</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script src="jquery.tzCheckbox/jquery.tzCheckbox.js"></script>
<script src="js/script.js"></script>

</body>
</html>

The main container element - the #page div, holds our form. Inside it is an unordered list with a number of label elements which describe the checkboxes. Something that is good from a usability standpoint, is that clicking a label will check/uncheck the corresponding checkbox (specified by the for attribute).

Notice the HTML5 data attributes, specified on some of the labels. We are using these to attach custom data to every tag in an HTML5 document. In our case the attributes will determine the text labels of the on / off states of the checkbox replacements.

And here is the markup of our replacement checkboxes:

<span class="tzCheckBox checked">
    <span class="tzCBContent">ON</span>
    <span class="tzCBPart"></span>
</span>

When our plugin is called, it will loop through the checkboxes, and insert the HTML code you can see above after each one, while at the same time hiding the original. The checked class determines the styling of the checkbox replacement (on or off state).

checkbox-replacement-explained.png

Now lets move on to the styling.

CSS

We are using a single transparent PNG background image to style the checkbox replacements. The top part of the image is the checked (on) state and the bottom - the off state. The width of the checkbox replacement grows with the text labels.

jquery.tzCheckbox.css

.tzCheckBox{
    background:url('background.png') no-repeat right bottom;
    display:inline-block;
    min-width:60px;
    height:33px;
    white-space:nowrap;
    position:relative;
    cursor:pointer;
    margin-left:14px;
}

.tzCheckBox.checked{
    background-position:top left;
    margin:0 14px 0 0;
}

.tzCheckBox .tzCBContent{
    color: white;
    line-height: 31px;
    padding-right: 38px;
    text-align: right;
}

.tzCheckBox.checked .tzCBContent{
    text-align:left;
    padding:0 0 0 38px;
}

.tzCBPart{
    background:url('background.png') no-repeat left bottom;
    width:14px;
    position:absolute;
    top:0;
    left:-14px;
    height:33px;
    overflow: hidden;
}

.tzCheckBox.checked .tzCBPart{
    background-position:top right;
    left:auto;
    right:-14px;
}

The .tzCheckBox span is positioned as an inline-block, which keeps it on the same line as the surrounding text, while giving us the ability to style its margin and padding properties as a block element. It also has a relative positioning assigned, so we can use the sliding doors technique and show the .tzCBPart span with the remaining part of the background.

Depending on whether we are displaying the on or off state, the .tzCPContent span is either aligned to the left or the right, with the appropriate paddings so that it makes its .tzCheckBox container span grow.

better-checkbox-jquery-plugin.png

Now it is time to build the actual jQuery plugin.

jQuery

We are going to name our plugin tzCHeckbox. It is going to take a JavaScript object, with a labels property as a parameter. This property is an array specifying the text labels displayed in the on / off state.

jquery.tzCheckbox.js

(function($){
    $.fn.tzCheckbox = function(options){

        // Default On / Off labels:

        options = $.extend({
            labels : ['ON','OFF']
        },options);

        return this.each(function(){
            var originalCheckBox = $(this),
                labels = [];

            // Checking for the data-on / data-off HTML5 data attributes:
            if(originalCheckBox.data('on')){
                labels[0] = originalCheckBox.data('on');
                labels[1] = originalCheckBox.data('off');
            }
            else labels = options.labels;

            // Creating the new checkbox markup:
            var checkBox = $('<span>',{
                className   : 'tzCheckBox '+(this.checked?'checked':''),
                html:   '<span class="tzCBContent">'+labels[this.checked?0:1]+
                        '</span><span class="tzCBPart"></span>'
            });

            // Inserting the new checkbox, and hiding the original:
            checkBox.insertAfter(originalCheckBox.hide());

            checkBox.click(function(){
                checkBox.toggleClass('checked');

                var isChecked = checkBox.hasClass('checked');

                // Synchronizing the original checkbox:
                originalCheckBox.attr('checked',isChecked);
                checkBox.find('.tzCBContent').html(labels[isChecked?0:1]);
            });

            // Listening for changes on the original and affecting the new one:
            originalCheckBox.bind('change',function(){
                checkBox.click();
            });
        });
    };
})(jQuery);

All changes to the checkbox replacement are propagated back to the original checkbox (which is hidden, but still on the page). This also works the other way around, as in certain circumstances (when you click on a label element for example) the original checkbox might be changed. This will trigger the change event and update the replacement.

Keeping the original checkboxes on the page is important, as this way submitting the form (with or without AJAX) would also send the correct values the user has chosen.

Using the plugin is also pretty straightforward:

$('input[type=checkbox]').tzCheckbox({
    labels: [ 'Enable', 'Disable' ]
});

This selects all the checkboxes on the page and passes them to tzCheckbox with 'Enable' and 'Disable' as replacement text.

With this our checkbox replacement jQuery plugin is complete!

Conclusion

You can use today's plugin to enhance the admin or configuration panels of your web apps, or even built better mobile sites with it. If, for some reason, JavaScript has been disabled on the user's browser, they will still see the default checkboxes as a fallback.

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Nice tutorial. Well done! Really usefull.

Regards,
Alex

Very nice! Since the control invites to "slide" the button to the other side, I would add the option to change the value when the button is dragged http://threedubmedia.com/code/event/drag

Really cool stuff.

I just test with latest, ie, opera, safari, chrome and FF and works perfect

was wondering about ie8, ie7

not so much about ie6

Martin Angelov

It works everywhere (haven't tested it on IE6).

However there is a problem with IE in that it does not support the "change" event on checkboxes when they are hidden, so clicking the labels would not work.

It reminds me of how I created the iOS styled on/off buttons. Great job! Very useful!

Very nice, but in the demo if you click to the left of the actual button it works the same as clicking the actual button. Might want to check that out. =-)

I'm pretty sure thats a feature caused by the tag.

feroc1ty

that's default html label element functionality :)

I looked at the code in Firebug, and it appears that the label click is also triggering the change, which is the normal functionality of a button.

**I meant of a checkbox

Ohhh, very nice!!!

Would be good if there was a left-to-right & right-to-left slide effect when clicked...otherwise awesome work.

Jason Long

This is similar to a design I worked on recently. Having it wrapped up in a jQuery plugin is handy. Having the switch animate would be a nice addition - here's an example of what I did: http://preation.blackantmedia.com/switches.html

Martin Angelov

It looks really impressive Jason! The only suggestion I have is to add an outline:none property as it currently shows in Firefox.

I disagree with this. The dots are there for a good reason: keyboard navigation.

e11world

Love your implementation and I suggest you add outline: none; to your a on the button css to remove the dots in firefox!

e11world

Sorry I didn't read your reply before I posted mine Martin!

Tim it's a part of the plugin ...

"All changes to the checkbox replacement are propagated back to the original checkbox (which is hidden, but still on the page). This also works the other way around, as in certain circumstances (when you click on a label element for example) the original checkbox might be changed. This will trigger the change event and update the replacement."

A reader

Hoping for 'Better radio buttons [...]' next week!

Great article. It came just as I was working on an admin panel and was try to figure out how to style the options. You definitely pointed me in the right direction.

Woah , I Love it! :)

Srinivas Reddy

Nice job...Thanks for sharing..will help a lot.

Regards,
Sri

Sagar Ranpise

Again Awesome Work! Thanks for sharing!

Martin Angelov

Thank you for the awesome comments, folks!

I used this type of check box replacement a little while back but people couldn't understand if it was checked or not. The changing labels confused them. Is it on when it says on? Or is it off when it says on and it's telling me that if i click on it will be now be on but say off?

Even though people use switches just like this in real life. The usage still escaped them. So in the long run, while I love these, it fails as far as usability goes.

not that it would be hard to make it absolutely clear.

You beat me to it; a better description of the current state is required. In particular I thought the labels and colours sent confusing signals. Perhaps if I were red/green colourblind ...

Having the descriptions to the side, phrased in such a way that you can stick with on/off or enabled/disabled in all cases would be clearer.

But the appearance is certainly attractive.

I can see how it could be confusing, but if used in the right context I think it could be okay. Such as making sure the labels are understandable to the on/off. Ex: Lights - On/Off is okay, whereas Lights Off - On/Off might get confusing.

Great technique Martin
but It can be spiced up by using CSS3 transitions property with just a line of CSS code, e.g. "-webkit-transitions:all linear 1s;" (but in that case sprite images should be placed side by side not top to bottom)

Richard Bland

Martin,

Truly amazing again. What a great eye for code! I would never of thought to do check boxes like this.

PS. Take a look at this site for me, give me your feedback, need some fresh eyes on it: http://www.arnott-conveyors.co.uk/testing/index.html

Speak soon mate

pretty cool effects ;) thanks for sharing the code .

e11world

Really nice example and I guess a little animation won't hurt. I kind of prefer the click over the drag because of mobile functionality. So far, I haven't seen any site or plugin that makes use of the drag while browsing on a mobile device.
Very lovely and creative!

Really nice tutorial. Thanx ;]

Chris Coyier

Just a little suggestion for funzies... I think it's clearer to label either side of the control, like this: http://cl.ly/5RNJ

As it stands, there is a little confusion as to what the label inside the control means. It could mean:

1) This control is currently at the value shown
2) If I click it, the control will THEN be at the value shown

Lee Regan

Just thought you might be interested in this. http://codecanyon.net/item/html5-slick-slider-checkboxes/180044

Seems as though someone is trying to profit of what you have provided here. Nice tutorial by the way, thanks!

Fitness specialist

The codes are really amazing. I would never of thought or seen to do check boxes like this. great tutorial.

Gary Rozanski

To be clear, I am able to pass either a 1 or a null value with the checkbox, however, I can't make the check box show OFF if there is a NULL value in the database.

Gary Rozanski

yes, I have resolved this matter :]

thanks for your awesome script. works a treat.

in jquery 1.6.2 has bug

I change it

        var checkBox = $('',{
            'class' : 'tzCheckBox '+(this.checked?'checked':''),
            'html': ''+labels[this.checked?0:1]+
                    ''
        });

now good work in jquery 1.5.2 /1.6.2

Rick de Graaff

Where do you edit this? I can't find the replacement..?? :S

Thanks a lot buddy...U saved my day. Thanks a ton

Ralph Almeida

very nice! but i have a question. i don´t want to use labels and i use jquery functions to manage the option.
so when i click on button, jquery doesn´t execute the command. can u help me?

jquery:
$('#habilitar-base').click(function() { $('#senha-base').attr('disabled', false); });

html:
on

thanks in advance and congratulations for this tutorial =)

Seems to fail when i use another jquery object within the same page

http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/

and i find if i delete the line

the script works again :(

not sure if i can have 2 differnt jquery scripts riding on the same page

with Jquery 1.7.1:
(in jquery.tzCheckbox.js, find "// Creating the new checkbox markup:")

var checkBox = $('<span>',{

    "class"   : "tzCheckBox "+(this.checked?'checked':''),
    "html":   "<span class='tzCBContent'>"+labels[this.checked?0:1]+
    "</span><span class='tzCBPart'></span>"
});

Awesome, thanks for the fix and thank you Martin for sharing.

The plug in background image disappears when I use JQuery 1.7.1. Any one know of a fix for this?

Made a change to the jquery.tzCheckbox.js file
I wanted the original class from the old checkbox to set on the new button so I can handle multiple buttons on a page.
http://pastebin.com/gsAeQMpx

I have modified a JS for include 2 new function: javascript caller and enabled state..


(function($){
    $.fn.tzCheckbox = function(options){

        // Default On / Off labels:

        options = $.extend({
            labels : ['Ok','No'],
            actionJS : ['']
        },options);

        return this.each(function(){
            var originalCheckBox = $(this),
                labels = [],actionJS = [];

            // Checking for the data-on / data-off HTML5 data attributes:
            if(originalCheckBox.data('on')){
                labels[0] = originalCheckBox.data('on');
                labels[1] = originalCheckBox.data('off');
            }else{ 
                labels = options.labels;
            }

            // Checking for the actionJS / data-off HTML5 data attributes:
            if(originalCheckBox.data('actionJS')){
                actionJS='onClick="javascript: ' + originalCheckBox.data('actionJS')+'"';
            }else{
                actionJS = 'onClick="javascript: ' + options.actionJS +'"';
            }

            // Checking for the ENABLED / data-off HTML5 data attributes:
            if(originalCheckBox.data('disabled')){
                state=originalCheckBox.data('disabled');
            }else{
                state = '';
            }

            //alert(actionJS);

            // Creating the new checkbox markup:
            var checkBox = $('<span>',{
                className   : 'tzCheckBox '+(this.checked?'checked':''),
                html:   '<span class="tzCBContent"  ' + actionJS +
                '>'+labels[this.checked?0:1]+'</span><span  class="tzCBPart"></span>'
            });

            // Inserting the new checkbox, and hiding the original:
            checkBox.insertAfter(originalCheckBox.hide());
            if(state==''){
                checkBox.click(function(){
                    checkBox.toggleClass('checked');
                    var isChecked = checkBox.hasClass('checked');
                    // Synchronizing the original checkbox:
                    originalCheckBox.attr('checked',isChecked);
                    checkBox.find('.tzCBContent').html(labels[isChecked?0:1]);
                });
                // Listening for changes on the original and affecting the new one:
                originalCheckBox.bind('change',function(){
                    checkBox.click();
                });

            }
        });
    };
})(jQuery);

lulalalla

http://jsfiddle.net/ULphC/
I have custom select all/none buttons. However when they are clicked tzCheckboxes are not changed accordingly. Is it because the way I did it does not fire change events?

If u got any solution then do mail me d solution...required it very badly.

[email protected]

Flash Games

Work more than good
But I have problems not to display the value when you use this code

$ ('input [type = checkbox]'). tzCheckbox ({labels: ['Enable', 'Disable']});

Thanks

This is a great plugin - exactly what I was looking for. I'm having two difficulties, though. First - I can't seem to get the checkbox to return a value. Even when I click it and change the state, it's value isn't changing. Second - I want to have a function execute when the state of the checkbox changes - and I bound a change function to the ID of the checkbox - but nothing is triggering. Using query 1.7.1

In doing some additional troubleshooting it seems that the problem is with binding the change event to the new "checkbox." No matter how I try to bind it - to the original ID, or any of the tz classes that are tied to the spans in the script - the change event doesn't trigger when the slider is clicked. Also doesn't work for click events.

Stefan Gabos

there’s also this jQuery plugin called <a href="http://stefangabos.ro/jquery/zebra-transform/">Zebra_Transform</a&gt; which transforms all the check boxes, radio buttons and select boxes on a page. it’s not as fancy as yours, but it is very very small (3KB) and works in all major browsers.

How to "Check all" or "Uncheck all" using a single button click???

To check or uncheck all using a single button

<input type="button" class="check" value="check all" />

<script>
$('input[type=checkbox]').tzCheckbox({
labels: [ 'Enable', 'Disable' ]
});
$(document).ready(function(){
$('.check:button').click(function(){
if(!$('.tzCheckBox').hasClass('checked'))
{
$('.tzCheckBox').addClass('checked');
$('.tzCheckBox').find('.tzCBContent').html('Enable');
$(this).val('uncheck all');
}
else
{
$('.tzCheckBox').removeClass('checked');
$('.tzCheckBox').find('.tzCBContent').html('Disable');
$(this).val('check all');
}
})
});
</script>

Your script is perfect but i can't take the value from checkbox and submit form onchange

Martin, you're the best. Thank you!

Duindain

For later versions of Jquery I use 1.9.1 you need to change the javascript to the following

var checkBox = $('<span>',{
    class   : 'tzCheckBox'+(this.checked?' checked':''),
    html:   '<span class="tzCBContent">'+labels[this.checked?0:1]+
            '</span><span class="tzCBPart"></span>'
});

I think className has been depreciated, also there were some extra spaces in the class declaration
The entire code segment is

(function($){
    $.fn.tzCheckbox = function(options){

        // Default On / Off labels:

        options = $.extend({
            labels : ['ON','OFF']
        },options);

        return this.each(function(){
            var originalCheckBox = $(this),
                labels = [];

            // Checking for the data-on / data-off HTML5 data attributes:
            if(originalCheckBox.data('on')){
                labels[0] = originalCheckBox.data('on');
                labels[1] = originalCheckBox.data('off');
            }
            else 
            {
                labels = options.labels;
            }
            // Creating the new checkbox markup:
            var checkBox = $('<span>',{
                class   : 'tzCheckBox'+(this.checked?' checked':''),
                html:   '<span class="tzCBContent">'+labels[this.checked?0:1]+
                        '</span><span class="tzCBPart"></span>'
            });

            // Inserting the new checkbox, and hiding the original:
            checkBox.insertAfter(originalCheckBox.hide());

            checkBox.click(function(){
                checkBox.toggleClass('checked');

                var isChecked = checkBox.hasClass('checked');

                // Synchronizing the original checkbox:
                originalCheckBox.attr('checked',isChecked);
                checkBox.find('.tzCBContent').html(labels[isChecked?0:1]);
            });

            // Listening for changes on the original and affecting the new one:
            originalCheckBox.bind('change',function(){
                checkBox.click();
            });
        });
    };
})(jQuery);

Thanks for the example best one I could find

+1 I rename className to class:

var checkBox = $('<span>',{
    class   : 'tzCheckBox '+(this.checked?'checked':''),
    html:   '<span class="tzCBContent">'+labels[this.checked?0:1]+
    '</span><span class="tzCBPart"></span>'
});

use this under ie8.

'class' : 'tzCheckBox'+(this.checked?' checked':''),

Phillip Senn

I think you have to use:
originalCheckBox.prop('checked',isChecked);
now instead of
originalCheckBox.attr('checked',isChecked);