Sometimes my ideas for jQuery plugins come from the need to solve a particular problem. Other times I see something done one way, and want to do it another way. Still other times they just come out of thin air. The inspiration for my latest plugin? Well, it came from a regular old, ugly checkbox.
I’m working on an application that has a lot of forms, and on some of the forms there are options that are simply yes/no or on/off. Now ordinarily I’d just slap a checkbox on the page and be done with it. In terms of functionality nothing is easier. A checkbox has two states, check or unchecked (although CSS3 will let us tap into an indeterminate state — but that’s not important right now). If the checkbox is checked, the option is selected; if not, it isn’t. Pretty basic. But from a design perspective, using a single checkbox to indicate whether something is on or off is well, pretty mundane. So I began to think, what could I do to improve upon the look while retaining the simplicity of a checkbox?
I don’t own an iPhone and probably won’t until Verizon gets them. I have, however, seen some of the iPhone’s interface and it’s obvious that UI (user interface) is where Apple excels. The wi-fi setting screen for the iPhone (and iPod touch and probably the iPad) has a great light switch style button that you can slide to turn wi-fi on or off
. Perfect! This button has the exact same functionality as a HTML checkbox. That’s all I needed to start me on developing this plugin.
Before I began to develop the plugin, I searched around to make sure I wasn’t going to be reinventing the wheel (always a good idea). Styling checkboxes (and radio buttons) using JavaScript isn’t something new, so I figured someone at some point did something similar. As it turns out, the closest thing I found was http://widowmaker.kiev.ua/checkbox/. This was very close to what I had envisioned, yet it didn’t quite fit the bill exactly. This plugin was a bit jerky and I wanted to use some basic animation effects to make it look smooth. I also wanted to make it more configurable. Finally, since this project hasn’t been updated in over a year, instead of trying to pickup from where this developer left off I decided to start fresh.
But once I began to develop a plugin to accomplish this idea, I thought that it may not be very useful if it only worked on individual checkboxes. So I decided it should be able to work on any number of checkbox and radio buttons. Now handling checkboxes via JavaScript is pretty easy. They stand alone and don’t depend on other elements. Radio buttons however are a different story. Radio buttons exist in groups of other radio buttons, all of which share the same name attribute.
This makes things a little trickier. But in the end I created an easy to use plugin that changes any checkbox or radio button to an on/off, yes/no, true/false, binary call-it-what-you-want switch. I call this plugin LightSwitch because that’s the closest thing in the real world I could think of that resembled this plugin.
This plugin can be applied to any checkbox or radio button and the image used can be different whenever you call the plugin. For example, you could use the same image for all of your buttons, use one image for checkboxes and another for radio buttons, or apply a different image to each element individually. Since explaining this doesn’t have the same impact as using it, please check out the demos for a complete understanding. Oh, and since I wrote a post about using the <label> tag in forms, I thought it was only fitting to make sure that this plugin worked with labels too.
So why the name lightswitch? Because radioandcheckboxprettifier plugin doesn’t roll of the tongue.
Download
Download jquery.lightswitch.zip (13K – mostly images)
Demos
- Example 1 – The most basic example.
- Example 2 – Radio buttons with a default option checked.
- Example 3 – Checkboxes, some using label tags.
- Example 4 – Multiple groups of radio buttons, some with label tags and a special toggle button that displays the actual radio buttons alongside the plugin buttons.
- Example 5 – The whole shebang including radio buttons, checkboxes, labels, and disabled buttons. Pressing the submit button shows what options have been selected.
One note about the demos. I created all the button images myself and you are free to use them. Note that I don’t claim to be any kind of graphics whiz, so feel free to substitute my images with your own. Also, note that each switch image uses an overlaying ‘switchplate’ image which is used to mask part of the switch.
How to Use
You don’t need to do anything to your existing checkboxes or radio buttons (sweet!). To use the plugin you’ll need to have a copy of jQuery, or point to jquery on Google, and the plugin. Place the files on your site and link to them:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="jquery.lightswitch.js"></script>
<script type="text/javascript">
$(function(){
$('input').lightSwitch();
});
</script>
That’s it! You can apply the lightswitch to any number of radio buttons and checkboxes on a page. View the examples for just a few ways.
Options
The following options are configurable:
- animSpeed – the number of milliseconds it takes for the switch to change from one state to the other (default: 120)
- hoverSpeed – the number of milliseconds it should take the switch to complete the peek animation when mouseing over a switch (default: 100)
- switchImg – the path to your switch image (default: ‘switch.png’)
- switchImgCover – the path to the switch plate image: (default: ‘switchplate.png’)
- switchImgCoverWidth – width of the cover plate image (default: ’63px’)
- switchImgCoverHeight – height of the cover plate image (default: ’18px’)
- disabledImg – the path to the disabled image (default: ‘disabled.png’)
- onShift – the CSS background-position of the on state (default: ’0px 0px’)
- offShift – the CSS background-position of the off state (default:’-37px 0px’)
- peekOff the CSS background-position of the peek off state (default: ‘-6px 0px’)
- peekOn – the CSS background-position of the peek on state (default: ‘-31px 0px’)
Note that the images, dimensions, and background-position options are all dependent on the images you use.Options are added when calling the script:
$('input:[name="radio1"]').lightSwitch({
switchImg:'switch-1.png',
animSpeed:250
});
Enjoy!
Since the issue is with IE8 and how jQuery does animations for those of you that want to get it working you can try adding in the background position plugin (http://plugins.jquery.com/project/backgroundPosition-Effect) which should fix the IE issue. Since the plugin works in every other browser just fine, I won’t be updating this to work with IE8 and will see what happens with IE9.
Was also receiving the issue in IE7 with jQuery 1.4.4. Adding the backgroundPosition plugin seems to have fixed it.
Is there any way to use a callback with this?
Thanks for the plugin, it is very useful the interface upgrade that we are doing.
I made a small modification to the span.switch click handler that I submit to you. The reason for this is that we are using the plugin with a radiobutton group. But if you click twice on a radio button, it’s state should not change, should remain checked. However Lightswitch was alternating between “on” and “off” states. Here is the modified handler:
$(this).next(‘span.switch’).click(function() {
// When we click any span image for a radio button, animate the previously selected radio button to ‘off’.
if ($(this).prev().is(‘:radio’)) {
radioGroupName = $(this).prev().attr(‘name’);
$(‘input[name="' + radioGroupName + '"]‘ + ‘:checked + span’).stop().animate({ ‘background-position’: o.offShift }, o.animSpeed);
}
$(this).prev().click(); // Added for correction
if ($(this).prev().is(‘:checked’)) {
$(this).stop().animate({ ‘background-position’: o.onShift }, o.animSpeed); // off
//$(this).prev().removeAttr(‘checked’); // Removed for correction
}
else {
$(this).stop().animate({ ‘background-position’: o.offShift }, o.animSpeed); // on
//if ($(this).prev().is(‘:radio’)) $(‘input[name="' + radioGroupName + '"]‘ + ‘:checked’).removeAttr(‘checked’); // Removed for correction
//$(this).prev(‘input’).attr(‘checked’, ‘checked’); // Removed for correction
}
})
Very easy to use for a customer feedback form:
- swapped switches for faces
- updated with the Teorist patch due to them being radio buttons
thanks, very useful!
Another change that I am submitting. The reason for this one, is that we are using images of variable length for each radio button, so we have to use an offset particular to each button. What was happening, was that the previously selected button was offset with the wrong value, making the interface look funny. As an extra, I applied the same algorithm to the animation speed. So here are the updated click handler and the updated span attributes. I removed some of the original comments, I am sorry, but being in a rush, I did not have time to put them back:
$(this).next(‘span.switch’).css({ ‘display’: ‘inline-block’, ‘background-image’: ‘url(“‘ + o.switchImg + ‘”)’, ‘background-repeat’: ‘no-repeat’, ‘overflow’: ‘hidden’, ‘cursor’: ‘pointer’, ‘margin-right’: ’2px’ }).attr(‘offShift’, o.offShift).attr(‘animSpeed’, o.animSpeed);
$(this).next(‘span.switch’).click(function() {
// When we click any span image for a radio button, animate the previously selected radio button to ‘off’.
if ($(this).prev().is(‘:radio’)) {
radioGroupName = $(this).prev().attr(‘name’);
var prevSel = ‘input[name="' + radioGroupName + '"]‘ + ‘:checked + span’;
var animSpeed = ” != $(prevSel).attr(‘animSpeed’) ? parseInt($(prevSel).attr(‘animSpeed’), 10) : o.animSpeed;
$(prevSel).stop().animate({ ‘background-position’: $(prevSel).attr(‘offShift’) }, animSpeed);
}
$(this).prev().click();
if ($(this).prev().is(‘:checked’)) {
$(this).stop().animate({ ‘background-position’: o.onShift }, o.animSpeed);
}
else {
$(this).stop().animate({ ‘background-position’: o.offShift }, o.animSpeed);
}
})
Thanks again!
Me again: the new jQuery 1.5 does not support anymore background-position with it’s animate function, so the animation either has to be dropped or modified.
For more details please see here:
http://bugs.jquery.com/ticket/7755
:(
We don’t need the animation, so I just replaced all the stop().animate() calls with css(). This makes the plugin work with jQuery 1.5 but obviously you use the animation:
$(this).next(‘span.switch’).click(function() {
// When we click any span image for a radio button, animate the previously selected radio button to ‘off’.
if ($(this).prev().is(‘:radio’)) {
radioGroupName = $(this).prev().attr(‘name’);
var prevSel = ‘input[name="' + radioGroupName + '"]‘ + ‘:checked + span’;
//var animSpeed = ” != $(prevSel).attr(‘animSpeed’) ? parseInt($(prevSel).attr(‘animSpeed’), 10) : o.animSpeed;
$(prevSel).css({ ‘background-position’: $(prevSel).attr(‘offShift’) }); //.stop().animate({ ‘background-position’: $(prevSel).attr(‘offShift’) }, animSpeed);
}
$(this).prev().click();
if ($(this).prev().is(‘:checked’)) {
$(this).css({ ‘background-position’: o.onShift }); //.stop().animate({ ‘background-position’: o.onShift }, o.animSpeed);
}
else {
$(this).css({ ‘background-position’: o.offShift }); //.stop().animate({ ‘background-position’: o.offShift }, o.animSpeed);
}
}).hover(function() {
$(this).css({ ‘background-position’: $(this).prev().is(‘:checked’) ? o.peekOff : o.peekOn }); //.stop().animate({ ‘background-position’: $(this).prev().is(‘:checked’) ? o.peekOff : o.peekOn }, o.hoverSpeed);
}, function() {
$(this).css({ ‘background-position’: $(this).prev().is(‘:checked’) ? o.onShift : o.offShift }); //.stop().animate({ ‘background-position’: $(this).prev().is(‘:checked’) ? o.onShift : o.offShift }, o.hoverSpeed);
});
$(this).next(‘span.switch’).css({ ‘background-position’: $(this).is(‘:checked’) ? o.onShift : o.offShift });
$(‘input + span’).live(“click”, function() { return false; });
$(this).change(function() {
radioGroupName = $(this).attr(‘name’);
if ($(this).is(‘:radio’)) {
$(this).css({ ‘background-position’: o.onShift }); //.stop().animate({ ‘background-position’: o.onShift }, o.animSpeed);
$(‘input[name="' + radioGroupName + '"]‘ + ‘ + span’).css({ ‘background-position’: o.offShift }); //.stop().animate({ ‘background-position’: o.offShift }, o.animSpeed);
}
$(this).next(‘span’).css({ ‘background-position’: $(this).is(‘:checked’) ? o.onShift : o.offShift }); //.stop().animate({ ‘background-position’: $(this).is(‘:checked’) ? o.onShift : o.offShift }, o.animSpeed);
});
I took Teorist modifications and merged them into a single file which for me at least, seems to work decently for jQuery 1.5.2. (I ran it trough a JS beautifier, so the formatting is a little different from the original.)
http://pastie.org/1855020
I’m not good enough at jQuery yet to take a crack at fixing the animation problems with this plugin relative to jQuery 1.5.2.
Great plugin and I hope it gets updated to be fully functional with 1.5.x going forward.
I added the ability to drag all the widgets to the ‘disabled’ list in the newest version. This feature had been requested a bit before.