Convert recursive function to asynchronous implementation of CPS (javascript)

Here is my function.

function duplicate_step_through_highlighted (element_jq, target_jq, char_cb) { console.log( element_jq); var contents = element_jq.contents(); for (var i = 0 ; i < contents.length; ++i) { // if text node, step if (contents[i].nodeType === 3) { // insert empty text node var new_tn = document.createTextNode(''); target_jq.append(new_tn); // iterate it var text = contents[i].nodeValue; for (var j = 0; j < text.length; j++) { char_cb(text[j],new_tn); new_tn.nodeValue += text[j]; // *** I want an async delay here *** } } else { // type should be 1: element // target_jq gets a duplicate element inserted, copying attrs var new_elem = $(contents[i].cloneNode(false)).appendTo(target_jq); duplicate_step_through_highlighted($(contents[i]),$(new_elem),char_cb); // then a recursive call is performed on the newly created element as target_jq // and the existing one as element_jq. char_cb is passed in } } } 

What I am doing is rebuilding the HTML element by restoring it in one character. There is a good reason for this, I want the visual effect of this to โ€œpick up."

So now there are no delays, so my element is instantly duplicated. I checked that the result is consistent, but it becomes clear to me that I probably need to completely rewrite the functionality so that I can set the asynchronous delay after entering each character.

Do I need to rewrite it and have a stack to track my position inside the elements?

+2
javascript jquery dom asynchronous callback
Jul 26 '12 at 8:40
source share
2 answers

Maybe you should take a look at my recent answer or this older one ( Demo ) on how to implement such an effect.




Tip. Do not clone elements into new ones, just hide them and make them partial.

Also, it would be easier not to deal with jQuery instances at all, except for the built-in DOM elements. So yes, rewriting can do :-) And I think he needs a stack.

 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()); 

Demo on jsfiddle.net

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 calls the callback function.
  • childStep starts the animation in the child node and passes itself as a callback until there are no children left - then nvokes performs the callback function.
  • Both together, animateNode recursively executed on the node tree and animates the text fields in the specified order.
  • The iterate function calls animateNode (after it is untied) in all input elements, passing itself as a callback. After all input elements are finished, it calls an external callback , which is specified as the second argument to animate .
+4
Jul 26 2018-12-12T00:
source share

I made a simple script for use on my website, this can help those who want to achieve this effect.

Here is the link for the repo on Github, here is the explanation:

 class Typer { constructor(typingSpeed, content, output) { this.typingSpeed = typingSpeed; // Parses a NodeList to a series of chained promises this.parseHtml(Array.from(content), output); }; makePromise(node, output) { if (node.nodeType == 1) // element { // When a new html tag is detected, append it to the document return new Promise((resolve) => { var tag = $(node.outerHTML.replace(node.innerHTML, "")); tag.appendTo(output); resolve(tag); }); } else if (node.nodeType == 3) // text { // When text is detected, create a promise that appends a character // and sleeps for a while before adding the next one, and so on... return this.type(node, output, 0); } else { console.warn("Unknown node type"); } } parseHtml(nodes, output) { return nodes.reduce((previous, current) => previous .then(() => this.makePromise(current, output) .then((output) => this.parseHtml(Array.from(current.childNodes), output))), Promise.resolve()); } type(node, output, textPosition) { var textIncrement = textPosition + 1; var substring = node.data.substring(textPosition, textIncrement); if (substring !== "") { return new Promise(resolve => setTimeout(resolve, this.typingSpeed)) .then(() => output.append(substring)) .then(() => this.type(node, output, textIncrement)); } return Promise.resolve(output); } } 
+1
Feb 09 '17 at 12:47 on
source share



All Articles