Skip to the content...

Welcome to the David Walsh Blog. I'm a MooTools, Dojo, jQuery, CSS, and PHP Web Developer located in Madison, Wisconsin, United States. Please contact me if I can make your experience on my website better.

iPhone Checkboxes Using MooTools

35 Responses »
iPhone Checkboxes

One of the sweet user interface enhancements provided by Apple's iPhone is their checkbox-slider functionality. Thomas Reynolds recently released a jQuery plugin that allows you to make your checkboxes look like iPhone sliders. Here's how to implement that functionality using the beloved MooTools JavaScript framework.

The XHTML

<h2>Off by default</h2>
<p><input type="checkbox" /></p>
<h2>On by default</h2>
<p><input type="checkbox" checked="checked" /></p>

This is how the XHTML code is initially embedded within the page. MooTools will change this drastically.

The CSS

.iPhoneCheckContainer{ position:relative; width:85px; height:27px; cursor:pointer; overflow:hidden;  }
.iPhoneCheckContainer input{ position:absolute; top:5px; left:30px;  }
.iPhoneCheckHandle{ display:block; height:27px; width:39px; cursor:pointer; position:absolute; top:0; left:0;  }
.iPhoneCheckHandle .iPhoneCheckHandleBG{ position:absolute; width:5px; height:100%; top:0; left:0; z-index:1;  }
.iPhoneCheckHandle .iPhoneCheckHandleSlider{ position:absolute; top:0; left:0; height:27px; width:39px; background:url(iphone-slider.png) no-repeat; z-index:2;  }
label.iPhoneCheckLabelOn,label.iPhoneCheckLabelOff{ font-size:17px; line-height:17px; font-weight:bold; font-family:Helvetica Neue,Arial,Helvetica,sans-serif; text-transform:uppercase; cursor:pointer; display:block; height:22px; position:absolute; width:52px; top:0;  }
label.iPhoneCheckLabelOn{ color:#fff; background:url(iphone-on.png) no-repeat; text-shadow:0px 0px 2px rgba(0,0,0,0.6); left:0; padding:5px 0 0 8px;  }
label.iPhoneCheckLabelOff{ color:#8B8B8B; background:url(iphone-off.png) no-repeat right 0; text-shadow:0px 0px 2px rgba(255,255,255,0.6); text-align:right; right:0; padding:5px 8px 0 0;  }

The CSS is pretty simple but notice we have to define classes for each state.

The MooTools JavaScript

window.addEvent('domready',function(){
	(function($) {

		this.IPhoneCheckboxes = new Class({

			//implements
			Implements: [Options],

			//options
			options: {
				checkedLabel: 'ON',
				uncheckedLabel: 'OFF',
				background: '#fff',
				containerClass: 'iPhoneCheckContainer',
				labelOnClass: 'iPhoneCheckLabelOn',
				labelOffClass: 'iPhoneCheckLabelOff',
				handleClass: 'iPhoneCheckHandle',
				handleBGClass: 'iPhoneCheckHandleBG',
				handleSliderClass: 'iPhoneCheckHandleSlider',
				elements: 'input[type=checkbox]'
			},

			//initialization
			initialize: function(options) {
				//set options
				this.setOptions(options);
				//elements
				this.elements = $$(this.options.elements);
				//observe checkboxes
				this.elements.each(function(el) {
					this.observe(el);
				},this);
			},

			//a method that does whatever you want
			observe: function(el) {
				//turn off opacity
				el.set('opacity',0);
				//create wrapper div
				var wrap = new Element('div',{
					'class': this.options.containerClass
				}).inject(el.getParent());
				//inject this checkbox into it
				el.inject(wrap);
				//now create subsquent divs and labels
				var handle = new Element('div',{'class':this.options.handleClass}).inject(wrap);
				var handlebg = new Element('div',{'class':this.options.handleBGClass,'style':this.options.background}).inject(handle);
				var handleSlider = new Element('div',{'class':this.options.handleSliderClass}).inject(handle);
				var offLabel = new Element('label',{'class':this.options.labelOffClass,text:this.options.uncheckedLabel}).inject(wrap);
				var onLabel = new Element('label',{'class':this.options.labelOnClass,text:this.options.checkedLabel}).inject(wrap);
				var rightSide = wrap.getSize().x - 39;
				//fx instances
				el.offFx = new Fx.Tween(offLabel,{'property':'opacity','duration':200});
				el.onFx = new Fx.Tween(onLabel,{'property':'opacity','duration':200});
				//mouseup / event listening
				wrap.addEvent('mouseup',function() {
					var is_onstate = !el.checked; //originally 0
					var new_left = (is_onstate ? rightSide : 0);
					var bg_left = (is_onstate ? 34 : 0);
					handlebg.hide();
					new Fx.Tween(handle,{
						duration: 100,
						'property': 'left',
						onComplete: function() {
							handlebg.setStyle('left',bg_left).show();
						}
					}).start(new_left);
					//label animations
					if(is_onstate) {
						el.offFx.start(0);
						el.onFx.start(1);
					}
					else {
						el.offFx.start(1);
						el.onFx.start(0);
					}
					//set checked
					el.set('checked',is_onstate);
				});
				//initial load
				if(el.checked){
					offLabel.set('opacity',0);
					onLabel.set('opacity',1);
					handle.setStyle('left',rightSide);
					handlebg.setStyle('left',34);
				} else {
					onLabel.set('opacity',0);
					handlebg.setStyle('left',0);
				}
			}
		});

	})(document.id);

	/* usage */
	var chx = new IPhoneCheckboxes();
});

The class allows for customization of which events get iPhone-ized, the label text statements, and what each CSS class is called. Besides those given options, you have nothing else to do unless you plan on hacking the class completely.

Sweet, no? Funny how we emulate functionality from an inferior technology (mobile browser). I would consider this functionality "proof of concept," as increasing the font-size may cause issues in some browsers. Cool effect though -- consider it for your websites!

Discussion

  1. darkimmortal
    August 3, 2009 @ 7:47 am

    Make the text unselectable or it gets selected after a couple of clicks :P

  2. chris
    August 3, 2009 @ 8:17 am

    Like it – but in my opinion it should also work with dragging the knob.

  3. August 3, 2009 @ 8:28 am

    For doing what Darkimmortal said someone can add the following events:

    //IE uses the select start event =>
    ‘selectstart’:function(){
    return false;
    },
    //Any other browser is happy with the mouse down =>
    ‘mousedown’:function(){
    return false;
    },
    ‘click’:function(){
    //does whatever it is planned to do
    }

    Nice eye candy, but it would be harder for simple PC users to get, if it needed them to scroll that div by themselves.

  4. August 3, 2009 @ 8:31 am

    //damn, for this example you should change the click to the mouseup function in the class xD

  5. August 3, 2009 @ 8:51 am

    wow this is really cool…good work David

  6. August 3, 2009 @ 8:55 am

    the spacing of the handle is a little messed up in IE7 and IE6…other than that it’s pretty cool…

  7. August 3, 2009 @ 9:05 am

    It’s nice, but I agree, it would be even better if it could be dragged. That’s the first thing I tried to do

  8. August 3, 2009 @ 9:31 am

    after reading the code I see that you create a new Fx instance on every mouse up. You should create a single object like you did with the other Fx, you can even set the ‘link’ option to cancel. You can can make bg_left a class variable so it’s accessible in the onComplete function

  9. August 3, 2009 @ 9:34 am

    @olivier: Good point — I’ll update that.

  10. zoran
    August 3, 2009 @ 11:48 am

    If i had to choose word for this i would go with cute or sweet… hahaha… Good work by the way, i like it very much

  11. August 3, 2009 @ 1:18 pm

    Check out http://reaktivo.com/mt/mooswitch I made a year ago…

  12. ga
    August 3, 2009 @ 1:46 pm

    REALLY NICE!!!!!!!! Thx David!!

  13. alan
    August 4, 2009 @ 3:35 pm

    @Marcel

    That is really cool. I like the options you have available

  14. charlie skapes
    August 5, 2009 @ 4:25 pm

    This is awesome! Great work David! Can’t wait to see what you come up with next!

  15. August 6, 2009 @ 6:12 am

    erm, great idea but I thought it could use some event binding on the click in case you want to.
    rather than adding Events to class, a quick change…

    add to options defaults:

    changeEvent: $empty,

    the end of the mouseup should become:

    //set checked
    el.set(‘checked’,!is_onstate);
    this.options.changeEvent.run([is_onstate,el]); // pass clicked state and original checkbox as object
    }.bind(this));

    … and for usage:

    new IPhoneCheckboxes({
    changeEvent: function(status, el) {
    console.log(status, el); // do something with it, like ajax write new state or whatever.
    }
    });

  16. August 6, 2009 @ 6:16 am

    is this thing on? it ‘ate’ my comment!

  17. August 6, 2009 @ 6:23 am

    right. anyway – it’s a handy little class and all that, just a small change as I needed it to call a function on the change of state, hopefully posting it this time won’t have it disappear…

    //options
    options: {
    changeEvent: $empty, // add an empty event function

    end of the wrap.addEvent(‘mouseup’,function() { anon function:

    //set checked
    el.set(‘checked’,!is_onstate);
    this.options.changeEvent.run([is_onstate, el]);
    }.bind(this));

    usage exmaple to enable / disable products from a list via ajax:

    [input type="checkbox" data-id="44" /]

    new IPhoneCheckboxes({
    changeEvent: function(status, el) {
    new Request({
    url: “somefile.php”
    method: “get”,
    onComplete: function() {

    }
    }).send(“a=productStatus&id=”+el.get(“data-id”) + “&status=” + (status) ? “live” : “disabled”);
    }
    });

  18. August 6, 2009 @ 6:32 am

    well twice already I cannot post the suggested change so I am putting it into a .txt file.
    the idea is, as great as the class is, i needed it to be able to call a function with the checkbox state on each change, as well as pass on the original element to that function (in order to be able to update a DB via ajax)

    resulting code looks like this: http://fragged.org/dev/iphoneCheckboxChange.txt (a quick change rather than implementing Events).

    cheers

  19. August 6, 2009 @ 9:10 am

    oh heh, and now they are here. can you delete the dupes please. http://fragged.org/dev/iphoneCheckboxChange.txt now contains an updated version which also addresses some issues with non-movers on click when there are MANY checkboxes on screen (30+)

  20. vocal
    August 6, 2009 @ 9:23 am

    so,this is very cool. I love it.

    but I find a small problem: for the styles of of outer divs of the example, such as id=”content” or class=”content” or id=”wrap” and so on, if i add a margin property at them, the event will be invalid, it will not change status when you click at the button. you can test by adding “margin:10px” to #content or other div, it doesn’t work.

    I wanna know why it comes with this. is it a little bug? or a margin property can’t be use here?

  21. September 18, 2009 @ 1:33 pm

    thnx for all your good scripts!
    this one I have a problem installing, it hangs when loaded. I tried a demo page seperated from any other content and it still hangs when it comes to #fff. Tried you mootools version and it still stops..

    Can you help me out? my javascript knowledge ain’t that good with debugging… only simple scripts I am able to implement and I am working from examples..

  22. dipen
    September 21, 2009 @ 6:30 am

    i got following error when using iPhone-CheckBox…..”handlebg.hide is not a function” :(

  23. dipen
    September 21, 2009 @ 6:31 am

    i got this error when i click on [on or off] button

  24. olivier refalo
    September 24, 2009 @ 7:14 am

    you need to change the event from mousedown to click for this to work on Safari…

  25. September 25, 2009 @ 3:16 pm

    That have a conflict in off to on On Safari Web Browser ???

  26. September 25, 2009 @ 5:32 pm

    Fixed the demo. Sorry everyone!

  27. September 25, 2009 @ 8:35 pm

    Demo page doesn’t work.

  28. olivier refalo
    September 26, 2009 @ 8:30 am

    Demo doesn’t work under safari, the switch flips sides only once…

  29. September 26, 2009 @ 8:32 am

    Flipping only once on Firefox and you still haven’t made the text unselectable :P

  30. September 26, 2009 @ 10:04 am

    Patched up the switching issues and updated my post’s code.

  31. reinier
    September 26, 2009 @ 10:26 am

    I found few years ago an example like this one.. only with on and off buttons from a old television..was pretty funny!

    Here demo doesn’t work either.. it worked before, only when installing I had the same problem as Dipen.

  32. reinier
    September 26, 2009 @ 10:27 am

    oops sorry demo works now..

  33. olivier refalo
    September 28, 2009 @ 12:35 pm

    Actually, it still behaves funny under IE7.. looks like a layer priority issue because the widget seems to be working as designed…

  34. olivier refalo
    September 29, 2009 @ 3:04 pm

    please change the CSS to the following to fix z-index issue on IE7

    .iPhoneCheckHandle {
    display: block;
    height: 27px;
    width: 39px;
    cursor: pointer;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2; <<— ADD THIS
    }

  35. amanada
    July 14, 2010 @ 9:51 pm

    Loved it These are checkboxes and i was wondering can these be achieved on list items?

    Thanks

Be Heard!

Share your thoughts with fellow developers of all skill levels! I want to hear from you!

Name*:
Email*:
Website:  
Wrap your code with <code> tags, f00!