Better Check Boxes with jQuery and CSS

Better Check Boxes with jQuery and CSS

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 Replacements Explained

Checkbox Replacements Explained

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 Checkboxes - jQuery Plugin

Better Checkboxes - jQuery Plugin

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.

Join our newsletter and get our PSDs!17,409 people learn about HTML5, JS and more. Join them!

by Martin Angelov

Martin is a web developer with an eye for design from Bulgaria. He founded Tutorialzine in 2009 and publishes new tutorials weekly.

62 Comments

  1. Alex says:

    Nice tutorial. Well done! Really usefull.

    Regards,
    Alex

  2. Eduardo says:

    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

  3. David says:

    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

    1. Martin Angelov says:

      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.

  4. Anders says:

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

  5. Tim says:

    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. =-)

    1. Edward says:

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

    2. feroc1ty says:

      that's default html label element functionality :)

    3. Key says:

      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.

      1. Key says:

        **I meant of a checkbox

  6. Chico says:

    Ohhh, very nice!!!

  7. archer says:

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

  8. Jason Long says:

    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

    1. Martin Angelov says:

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

      1. Matt says:

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

    2. e11world says:

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

      1. e11world says:

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

  9. Dan says:

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

  10. A reader says:

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

  11. Frank M says:

    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.

  12. krypto says:

    Woah , I Love it! :)

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

    Regards,
    Sri

  14. Sagar Ranpise says:

    Again Awesome Work! Thanks for sharing!

  15. Martin Angelov says:

    Thank you for the awesome comments, folks!

  16. O'Ryan says:

    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.

    1. Alan says:

      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.

    2. Key says:

      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.

  17. Ejaz says:

    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)

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

  19. Vamsi says:

    pretty cool effects ;) thanks for sharing the code .

  20. e11world says:

    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!

  21. Adrian says:

    Really nice tutorial. Thanx ;]

  22. Chris Coyier says:

    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

  23. Lee Regan says:

    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!

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

  25. Gary Rozanski says:

    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.

  26. Gary Rozanski says:

    yes, I have resolved this matter :]

    thanks for your awesome script. works a treat.

  27. asins says:

    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

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

    2. Sandesh says:

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

  28. 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 =)

  29. johnny says:

    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

  30. Sillier says:

    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>"
    });
    
    1. Tony says:

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

  31. epecho says:

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

  32. Jay says:

    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

  33. lucas says:

    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);
    
    
  34. lulalalla says:

    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?

    1. Sandesh says:

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

      b.sandesh@gmail.com

  35. Flash Games says:

    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

  36. Alan says:

    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

    1. Alan says:

      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.

  37. Stefan Gabos says:

    there’s also this jQuery plugin called <a href="http://stefangabos.ro/jquery/zebra-transform/">Zebra_Transform</a> 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.

  38. Sandesh says:

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

  39. Sandesh says:

    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>

  40. kostas says:

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

  41. Dino says:

    Martin, you're the best. Thank you!

  42. Duindain says:

    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. dees says:

      +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>'
      });
      
    2. Adrian says:

      it doesn't run under IE8 http://stackoverflow.com/questions/17442311/tzcheckbox-expected-identifier-string-or-number#17442311

      1. Riche says:

        use this under ie8.

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

  43. Phillip Senn says:

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

Add Comment

Add a Reply

HTML is escaped automatically. Surround code blocks with <pre></pre> for readability.
Perks:   **bold**   __italics__   [some text](http://example.com) for links