iPhone Checkboxes Using MooTools

By  on  
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!

Recent Features

  • By
    9 Mind-Blowing WebGL Demos

    As much as developers now loathe Flash, we're still playing a bit of catch up to natively duplicate the animation capabilities that Adobe's old technology provided us.  Of course we have canvas, an awesome technology, one which I highlighted 9 mind-blowing demos.  Another technology available...

  • By
    9 Mind-Blowing Canvas Demos

    The <canvas> element has been a revelation for the visual experts among our ranks.  Canvas provides the means for incredible and efficient animations with the added bonus of no Flash; these developers can flash their awesome JavaScript skills instead.  Here are nine unbelievable canvas demos that...

Incredible Demos

Discussion

  1. Darkimmortal

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

  2. Chris

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

  3. 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. //damn, for this example you should change the click to the mouseup function in the class xD

  5. wow this is really cool…good work David

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

  7. 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. 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. @olivier: Good point — I’ll update that.

  10. Zoran

    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. Check out http://reaktivo.com/mt/mooswitch I made a year ago…

  12. Ga

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

  13. Alan

    @Marcel

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

  14. Charlie Skapes

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

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

  16. 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");
        }
    });
    
  17. 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

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

  19. vocal

    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?

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

  21. Dipen

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

  22. Dipen

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

  23. olivier refalo

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

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

  25. Fixed the demo. Sorry everyone!

  26. Demo page doesn’t work.

  27. olivier refalo

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

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

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

  30. Reinier

    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.

  31. Reinier

    oops sorry demo works now..

  32. Olivier Refalo

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

  33. Olivier Refalo

    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
    }
  34. Amanada

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

    Thanks

  35. Hi David,

    Here’s challenge for you. Can this script be made to work with a group of radio buttons so that when one is clicked, it slides to on, and the others automatically slide to off?

    Best,

    Matt

  36. Notice on the demo video, they use the iPhone sliders? I don’t think they are yours, but still kinda neat!

    http://preyproject.com/

  37. ayoub

    it looks great but it dosen’t work with the latest version of mootools.
    i’m having issues with handlebg.hide is not a function

  38. Samuel

    And it also doesnt work since a few days in the latest IE 9 !? having big troubles with that… the first checkbox is hide and all others are in normal html style. Could someone help me? i have to deactivate it now because of the missing first checkbox :-(

  39. Samuel

    iam sorry! not the first one is hide, the activated ones not visible.

  40. Samuel

    Update from Mootools 1.2 to mootools-core-1.4.1-full-compat worked for me!

  41. Igor Fernandes

    Someone can post the update code for mootools 1.4?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!