You might want to have a look at my recent answer or this older one (Demo), on how to implement such an effect.
Tip: Don’t clone the elements into new ones, just hide them and make them appear part-for-part.
Also, it might be easier not to deal with jQuery instances at all but native DOM elements. So yes, a rewrite might do 🙂 And I think it does need a stack as well.
function animate(elements, callback) {
/* get: array with hidden elements to be displayes, callback function */
var i = 0;
(function iterate() {
if (i < elements.length) {
elements[i].style.display = "block"; // show
animateNode(elements[i], iterate);
i++;
} else if (callback)
callback();
})();
function animateNode(element, callback) {
var pieces = [];
if (element.nodeType==1) {
while (element.hasChildNodes())
pieces.push(element.removeChild(element.firstChild));
setTimeout(function childStep() {
if (pieces.length) {
animateNode(pieces[0], childStep);
element.appendChild(pieces.shift());
} else
callback();
}, 1000/60);
} else if (element.nodeType==3) {
pieces = element.data.match(/.{0,2}/g); // 2: Number of chars per frame
element.data = "";
(function addText(){
element.data += pieces.shift();
setTimeout(pieces.length
? addText
: callback,
1000/60);
})();
}
}
}
animate($("#foo").children());
How it works:
- The
addText
function adds some character to the current text node, and sets a timeout for itself – animation! In case everything is done, it invokes thecallback
function. childStep
runs the animation on a childnode, and passes itself as the callback until no children are left – then nvokes thecallback
function.- Both together,
animateNode
recursively runs over the node tree and animates the textnodes in thier order. - the
iterate
function callsanimateNode
(after unhinding them) on all input elements, by passing itself as the callback. After all input elements are finished, it invokes the outercallback
which is given as the second argument toanimate
.