Welcome!
http://pres.learningjquery.com/jqcon2010/

* Most of the animations in this presentation are slower than advisable on a real production site.
$('.somediv').css('display', '');left, top, width, height, padding, margin, borderWidth, opacity, fontSize, and so onscrollLeft, scrollTop, and so onBut…
color, backgroundColor, borderColorz-index, backgroundImage, overflow, and so onProperties can be animated by:
…as long as the property allows it (e.g. opacity: only 0-1)
top, right, bottom, or left property, the element must have position: relative/absolute/fixed, not the default position: staticbottom and top—or both left and right—properties can lead to unexpected resultsfont, border, text-shadow, and so on
| row 1 | slide row | row height, cell height: 1.5em |
| row 2 | slide cells | row height: 1.5em, cell height: auto |
| row 3 | slide cells | row height: 1.5em, cell height: 4em |
row 4 |
slide row and divs |
row height, cell height: 1.5em |
| row 5 | animate row line-height | row height, cell height: 1.5em |
| row 6 | fade row | row height, cell height: 1.5em |
The Problem:
$('html, body') can be problematic.
$.fn.firstScrollable = function(dir) {
var scrollable = [], scrolled = false;
var scrollDir = /left$/i.test(dir) ? 'scrollLeft' : 'scrollTop';
this.each(function() {
if (this == document || this == window) { return; }
var el = $(this);
if ( el[scrollDir]() > 0 ) {
scrollable = [this];
return false;
}
el[scrollDir](1);
scrolled = el[scrollDir]() > 0;
el[scrollDir](0);
if ( scrolled ) {
scrollable = [this];
return false;
}
});
return this.pushStack(scrollable);
};
$('html, body')
.firstScrollable()
.animate({scrollTop: '+=200px'}, 800);
The Problem:
.slideDown() to slide up!
The Background: What .slideUp() and .slideDown() really mean...
.slideUp(): animate elements' height, padding-top, padding-bottom, margin-top, and margin-bottom properties from current value to 0..slideDown(): animate elements' height, top and bottom padding, and top and bottom margin from 0 to their full valuesSo, all we need is a little CSS to switch things up:
.container { position: relative; }
.slidy {
position: absolute;
bottom: 0; /* here we are! */
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div class="container"> <div class="slidy">slidy</div> </div>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$('div.slidy').slideToggle(1200);
The Problem:
.slideToggle().toggle().fadeToggle()
All shortcut methods use .animate() internally.
jQuery.each({
slideDown: genFx("show", 1),
slideUp: genFx("hide", 1),
slideToggle: genFx("toggle", 1),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" }
}, function( name, props ) {
jQuery.fn[ name ] = function( speed, easing, callback ) {
return this.animate( props, speed, easing, callback );
};
});
We can do the same thing:
jQuery.fn.fadeToggle = function(speed, easing, callback) {
return this.animate({opacity: 'toggle'}, speed, easing, callback);
};
And use it like the other shortcuts:
$('#myid').fadeToggle();
$('#myid').fadeToggle('slow');
$('#myid').fadeToggle('slow', function() {
// do something
});
Problem: I need something more robust. I need a plugin that will…
opacity[showSpeed, hideSpeed] array The Solution...
$.fn.toggleEffect = function(effect, speed, easing, callback) {
var args = Array.prototype.slice.call(arguments);
if (effect == undefined || typeof effect != 'string' || effect in $.fx.speeds) {
args.unshift('opacity');
}
var prop = {};
prop[ args[0] ] = 'toggle';
speed = args[1];
easing = args[2];
callback = args[3];
if ( !$.isArray(speed) ) {
return this.animate(prop, speed, easing, callback);
}
return this.animate(prop, this.is(':hidden') ? speed[0] : speed[1], easing, callback);
};
Now we can do this sort of thing:
// toggle opacity w/ defaults
$('some selector').toggleEffect();
// toggle height at 200ms
$('some selector').toggleEffect('height', 200)
// fade in at 800ms; fade out at 800ms
$('some selector').toggleEffect('opacity', [800, 450])
// toggle width slowly with linear easing
// and call the doSomething function when animation is complete
$('some selector').toggleEffect('width', 'slow', 'linear', doSomething)
The Problem:
The Solution:
// 'swing' is default, so unnecessary
$('#swing').slideToggle(1200, 'swing');
// same velocity throughout. *in core*
$('#linear').slideToggle(1200, 'linear');
// all others require plugin*
$('#easeInOutBounce').slideToggle(1200, 'easeInOutBounce');
Avoid this sort of thing:
$('p').slice(1) // select paragraphs 2–20.
.slideDown();
Do something like this instead:
// first wrap 'em up.
var paragraphWrapper = $('p').slice(1)
.wrapAll('<div></div>')
.parent();
// then animate the wrapper
paragraphWrapper.slideDown();
The Problem: This doesn't work in jQuery 1.4.2 or below:
$('#combo').fadeOut(800, 'linear', function() {
$(this).fadeIn();
});
The Solution: This does:
$('#combo').fadeOut({
duration: 800,
easing: 'linear',
complete: function() {
$(this).fadeIn();
}
});
// clickie on the combo. Then...
$(this).parent().prev().fadeOut({
duration: 800,
easing: 'linear',
complete: function() {
$(this).fadeIn(750);
}
});
(thanks, me)
// as of jQuery 1.4.3, this now works
$('#combo').fadeOut(800, 'linear', function() {
$('#something-else').fadeIn();
});
The Problem:
The Background:
$('.myclass).slideDown().slideUp().fadeIn();
$('.myclass).slideDown();
$('.yourclass).slideDown();
We can change this default queue behavior by:
queue option$('.myclass).slideDown()
.animate({opacity: 'show'}, {queue: false});
$('.myclass).slideDown(function() {
$('.yourclass).slideDown();
});
.queue() method makes it easy to have non-animation methods occur within a sequence as well. var $queuewrap = $('#queuewrapper'),
$queue1 = $queuewrap.find('.demodiv').eq(0),
$queue2 = $queuewrap.find('.demodiv').eq(1);
// .queue() callback functions
var greenify = function(next) {
$(this).css({backgroundColor: '#090'});
next();
};
var slideFirst = function(next) {
$queue1.slideToggle();
next();
};
var slideSecond = function(next) {
$queue2.slideToggle().queue(greenify);
next();
};
$queuewrap.find('button').click(function() {
$queue1.queue(slideFirst)
.queue(greenify)
.queue(slideSecond);
});
The Problem:
The Evidence:
$('.mydiv').eq(0).fadeOut('slow', function() {
$('.mydiv').eq(1).fadeOut('slow', function() {
$('.mydiv').eq(2).fadeOut('slow', function() {
$('.mydiv').eq(3).fadeOut('slow', function() {
$('.mydiv').eq(4).fadeOut('slow', function() {
// abandon all hope
});
});
});
});
});
The Solution:
var myCallback = function() {
// I'mma gonna do something
}
function yourCallback() {
// You do something, too!
};
$('.mydiv).slideUp(250, myCallback);
$('.yourdiv).slideUp(250, yourCallback);
(Thanks, Dave!)
var $sequence = $('div.sequence'),
index = 0;
(function sequencer() {
$sequence.eq(index++)
.animate({opacity: 'toggle'}, 'slow', sequencer);
})();
var $pulse = $('div.pulse'),
index = 0,
total = 10;
(function pulse() {
if (index++ < total) {
$pulse
.animate({opacity: 'toggle'}, pulse);
}
})();
$.fn.pulsee = function(options) {
var counter = 0, self = this;
var opts = $.extend({}, $.fn.pulsee.defaults,
typeof options === 'number' ? {total: options} : options || {}
);
(function pulse() {
if (counter++ < opts.total) {
self.animate({opacity: 'toggle'}, opts.duration, opts.easing,
function() {
opts.complete.call(this, counter-1);
pulse();
}
);
}
})();
return self;
};
$.fn.pulsee.defaults = {total: 10, complete: $.noop};
var colors = ['#900', '#090', '#009', '#cc0', '#0cc', '#c0c'];
var pulseOpts = {
total: 9,
duration: 489,
complete: function(i) {
this.style.backgroundColor = colors[i % 6];
}
};
$('#pulseer').click(function() {
$('div.pulsee').slice(0,5).pulsee(11);
$('div.pulsee').slice(5).pulsee(pulseOpts);
});
A more complex, "real-world" example
The Problem:
:animated Selector$('#fadeinout').click(function() {
var $div1 = $('#inoutwrap').find('div.demodiv').eq(0);
var $div2 = $('#inoutwrap').find('div.demodiv').eq(1);
$div1.fadeIn().fadeOut();
if ( !$div2.is(':animated') ) {
$div2.fadeIn().fadeOut();
}
});
.stop() Method.stop()
.stop(true, true)
$('#badges li').hover(function(){
$(this).stop(true, true)
.animate({bottom:"30px"}, 200);
}, function(){
$(this).stop(true, false)
.animate({bottom:"8px"}, 500);
});
$drop.slideDown(800); $drop.slideUp(800); |
$drop.stop(false, true).slideDown(800); $drop.stop(true, true).slideUp(800); |
$drop.stop(true, true).slideDown(800); $drop.stop(true, false).slideUp(800); |
if ( event.type === 'mouseenter' ) {
$drop
.stop(true, true)
.css('height', $drop.data('height'))
.slideDown(800);
} else {
$drop.stop(true, false).slideUp(800);
}
|
// first, store full height in element's .data()
// then...
$('#nav4 > li').bind('mouseenter mouseleave', function(event) {
if ( event.type === 'mouseenter' ) {
$drop
// if animating, sets display: none
.stop(true, true)
// so setting height won't make visible
.css('height', $drop.data('height'))
// and now it knows what to slide down to
.slideDown(800);
} else {
$drop.stop(true, false).slideUp(800);
}
});
The Problem:
The Solution:
step optionvar $ball = $easingDiv.find('div.demodiv');
$easingDiv.find('button').click(function() {
var $button = $(this), hit = false;
var mystep = function() {
if (!hit && 298 <= $ball.height() +
parseFloat($ball.css('top')) +
parseFloat($ball.css('marginTop'))
) {
$button.after('SMACK!');
hit = true;
}
};
$easingDiv.find('div.demodiv').animate(props, {
duration: 5000,
easing: 'linear',
specialEasing: myEase,
step: mystep
});
});
The Problem:
The Solution:
$.fx.off = !$.fx.off
Global effects are on
The Problem:
The Clue:
$.fx.speeds to set the default duration, as well as the duration values for 'slow' and 'fast'// from jQuery core...
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ?
opt.duration :
jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
// ....
jQuery.extend(jQuery.fx, {
speeds: {
slow: 600,
fast: 200,
// Default speed
_default: 400
},
// ...
});
The Solution:
$.fx.speeds object$.extend($.fx.speeds, {
_default: 300,
crawl: 1200
});
/*
* jQuery (jquery.com)
* ...
*
* $Date: 2006-05-12 12:52:18 -0400 (Fri, 12 May 2006) $
* $Rev: 29 $
*/
// ...
var ss = {"crawl":1200,"xslow":850,"slow":600,
"medium":400,"fast":200,"xfast":75,"normal":400};
The Problem:
A (non-)?Solution
$('#me-me').fadeIn().animate({snooze: true}, 1200).fadeOut();
Another Solution
.delay() method.$('#delayee').fadeIn(800).delay(3000).fadeOut(600);
.queue()$('#delayez')
.fadeIn(800)
.delay(1500)
.queue(function(next) {
$(this).css('backgroundColor', '#090');
next();
})
.delay(1500)
.fadeOut(600);
});
Yet Another Solution
setTimeout and clearTimeout.this.
$('#me-too').fadeIn(backout);
function backout() {
var self = this;
setTimeout(function() {
// you probably don't want to use "this" here.
$(self).fadeOut();
}, 2000);
}
$('#me-three').fadeIn(function() {
$(this).doTimeout(1800, function() {
this.fadeIn();
});
});
$('div.demodiv').animate({
left: '300px',
top: '245px',
width: 0,
height: 0,
marginTop: '55px'
}, {
duration: 5000,
easing: 'linear',
specialEasing: {
left: 'easeOutQuad',
top: 'easeOutBounce'
}
});
(Thanks, John!)
$.fx.step.corner = function(fx) {
fx.elem.style.top = fx.now + fx.unit;
fx.elem.style.left = fx.now + fx.unit;
};
// usage
$('#cornerify').click(function() {
$('div.cornered').animate({corner: '40px'}, 500);
});
$.fx.step.leftTop = function(fx) {
fx.elem.style.left = fx.now + fx.unit;
fx.elem.style.top = fx.now + fx.unit;
};
// allow start from a point other than 0 based on "left"
var _oldcur = jQuery.fx.prototype.cur;
jQuery.fx.prototype.cur = function( force ) {
if (this.prop == 'leftTop') {
// change this.prop to 'left'
var r = parseFloat(jQuery.css(this.elem, 'left', force));
return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, 'left')) || 0;
}
return _oldcur.call(this, force);
};
$('#topleftify').click(function() {
$('div.cornered').animate({corner: '+=40px'}, 500);
$('div.left-topped').animate({leftTop: '+=40px'}, 500);
});
$('div.demodiv').animate({
left: '300px',
top: '245px',
width: 0,
height: 0,
marginTop: '55px'
}, {
duration: 5000,
easing: 'linear',
specialEasing: {
left: 'easeOutQuad',
top: 'easeOutBounce'
}
});
(Thanks, John!)
$.fx.step.corner = function(fx) {
fx.elem.style.top = fx.now + fx.unit;
fx.elem.style.left = fx.now + fx.unit;
};
// usage
$('#cornerify').click(function() {
$('div.cornered').animate({corner: '40px'}, 500);
});
$.fx.step.leftTop = function(fx) {
fx.elem.style.left = fx.now + fx.unit;
fx.elem.style.top = fx.now + fx.unit;
};
// allow start from a point other than 0 based on "left"
var _oldcur = $.fx.prototype.cur;
$.fx.prototype.cur = function( force ) {
if (this.prop == 'leftTop') {
// change this.prop to 'left'
var r = parseFloat($.css(this.elem, 'left', force));
return r && r > -10000 ? r : parseFloat($.curCSS(this.elem, 'left')) || 0;
}
return _oldcur.call(this, force);
};
$('#topleftify').click(function() {
$('div.cornered').animate({corner: '+=40px'}, 500);
$('div.left-topped').animate({leftTop: '+=40px'}, 500);
});
Thanks so much for listening.