Feb 07 2009

6

Insights from Link Nudging

Category: Javascript, MootoolsTags: ,

Someone recently posted the question “Mootools link nudging?”. I honestly wasn’t familar with the term, at first I thought it was an SEO trick. I fired up google and quickly learned it was a css padding animation trick. I saw that David Walsh did a version in

jquery

$('a.nudge').hover(function() { //mouse in  
	$(this).animate({ paddingLeft: '20px' }, 400);  
}, function() { //mouse out  
	$(this).animate({ paddingLeft: 0 }, 400);  
});

and mootools.

$$('a.nudge').each(function(el) {    
	var fx = new Fx.Morph(el,{ duration:300, link:'cancel' });  
	el.addEvents({  
		'mouseenter': function() { fx.start({ 'padding-left': 20 }); },  
		'mouseleave': function() { fx.start({ 'padding-left': 0 }); }  
	});  
});

I first noticed that the mootools code could be more “moo”.

‘$$’ returns an ‘Elements’ object. Elements acts like an array but extended with ‘$’ (’Element’). Mootools has the generic ’set’ function for setting a multitude of things on each element. The ’set’ properties can be customized or added by extending ‘Element.Properties’.

$$('a.nudge').set({
	'morph' : { duration: 400, link : 'cancel'},
	'events' : {
		'mouseenter' : function(){ this.morph({'padding-left' : 20}); },
		'mouseleave' : function(){ this.morph({'padding-left' : 0}); }
	}
});
 
// or
$$('a.nudge').set({
	'tween' : { 'property' : 'padding-left', duration: 400, link : 'cancel'},
	'events' : {
		'mouseenter' : function(){ this.tween(20); },
		'mouseleave' : function(){ this.tween(0); }
	}
});

Aaron points out in the comments that this method does have one drawback.

The only issue here is that this instance of Fx.Morph could be accessed and altered by using the .morph shortcut again. If your work had any chance of morphing that object again you might want to segregate the instances. Still, a pretty rare edge case. I’d probably write it as you have it above.

Update:
As suggested below it should be in a class. Here you go.

var Nudger = new Class({
 
	Implements : [Options],
 
	options : {
		transition : Fx.Transitions.linear,
		duration : 400,
		amount : 20
	},
 
	initialize : function(selector,options){
		this.setOptions(options);
 
		this.elements = $$(selector);
 
		this.elements.each(function(el){
 
			var nudge = {
				'fx' :  new Fx.Morph(el,{ 
							duration: this.options.duration,  
							transition: this.options.transition,
							link:'cancel'}),
				'enter' : this.enter.bind(this,el),
				'leave' : this.leave.bind(this,el)
			};
 
			el.addEvents({
				'mouseenter' : nudge.enter,
				'mouseleave' : nudge.leave
			});
 
			el.store('nudge',nudge);
 
		},this);
	},
 
	enter : function(el){
		el.retrieve('nudge').fx.start({ 'padding-left' : this.options.amount});
	},
 
	leave : function(el){
		el.retrieve('nudge').fx.start({ 'padding-left' : 0});
	},
 
	destroy : function(){
		this.elements.each(function(el){
			var nudge = el.retrieve('nudge');
			el.removeEvents({
				'mouseenter' : nudge.enter,
				'mouseleave' : nudge.leave
			});
		});
	}
 
});
/*
Default options:
   transition: Fx.Transitions.linear. // easing transition 
   duration: 400. // duration of effect
   amount : 20 // amount of pixels to move
 
Usage:
 
var nudger = new Nudger('a.nudge');
 
// with amount changed
 
var nudger = new Nudger('a.nudge',{amount : 40 });
 
*/

The class has the advantage of easily changing the transition effect, the duration and the amount the padding-left is moved. In addition I added a method to destroy the function. This will remove the functionality from the elements.

I shifted my attention toward the jquery example and immediately the code looked more cryptic. If the comments were removed the code would be even harder to follow. What is hover? I like speaking similar language as the DOM interface. I like frameworks but when you start to get unexpected results its good to be able to search for generic solutions, the high level abstraction in jquery makes this harder. $ working as an array and a single value variable seems to me that it would make debugging harder in some cases. Also, what does that 400 do? Mootools passes objects as parameters, oh duration : 400. I have to know that ‘hover’ takes two functions and the order. Again more time debugging subtle errors. Even code syntax gets harder with a bunch of anonymous functions and commas.

Walsh did something in his Mootools code that he didn’t do in jquery. The animation stops whenever the effect changes. This helps to create smooth transitions if you move in and out of the link. I discovered that the jquery code didn’t have this advantage. Lateral Code had the solution:

$(function() {
    $('a').hover(function() {
        $(this).stop().animate( {
            paddingLeft:"30px"
        }, 300);
    }, function() {
        $(this).stop().animate( {
            paddingLeft:"0"
        }, 300);
    });
});

The problem solved with chaining the stop() method in. This code is pretty easy to maintain at the moment. While this is just a taste. From my point of view the scaling ability of the two sets of code is considerably different.

Your thoughts?

Tags: ,

6 Responses to “Insights from Link Nudging”

  1. Aaron Newton says:

    It should be a class.

    new Nudger($(’myLink’));

  2. Lim Chee Aun says:

    Also note that in jQuery, there’s a “queue” option for the animate() function which by default is “true” and it explains why the animation doesn’t stop whenever the effect changes. If it’s true, the animation queues, just like {link: ‘chain’} in Mootools’ Fx Class. If it’s false, it’s like {link: ‘cancel’}.

  3. Aaron Newton says:

    IMHO, Nudger should take a single dom element, not a selector of them. I don’t like creating a group of things when the class essentially is about one element. They don’t have anything to do with each other and there’s no advantage to adding a state for the group. I’ve broken this rule in my own code (actually, I authored things before I created this rule for myself) and I’ve been going back and changing it. OverText (in the clientcide libs) is an example. It should control the input over text for a single input, not a group. By having it be a group I find I am always trying to figure out if an input already has OverText applied…

  4. David Walsh says:

    IMHO, I don’t think that this small effect needs to be a class, considering how large the class became. Nice work but I think it’s a bit much.

  5. nwhite says:

    @aaron - Good point on the single dom element. In theory I agree with you. In practice I am not sure it is always necessary. Do I want to make 50 new objects or 1? Always value your input, thanks.

    @david - I agree the class seems a bit overkill. The extra lines were mainly picked up to allow for the event destruction. I have been getting in the habit of making my classes not only easy to install but easy to remove. Anyways great job on the posts and your site. Keep up the great work.

  6. Erik says:

    Everyone says to install a reset.css, because the links are not fully justified to the left when the page loads. Is there something in the nudge code, itself, we can tweek to make all the links line up to the left correctly after a page loads?

    Thanks.

Leave a Reply