如何使.each()等到每次迭代完成

时间:2016-11-03 01:37:07

标签: javascript jquery asynchronous callback promise

我使用来自this answer on SO的打字机效果,目的是让第一个div类型输出,然​​后是第二个div(当第一个div完成输入时)。

<div class="text">
 Lorem ipsum dolor sit amet...
</div>
<div class="text">
 Cras eros augue, tempor...
</div>

JS:

// Create typewrite function as jQuery plugin:
// (From https://stackoverflow.com/a/22266737/3217306)
$.fn.typeText = function() {
    var keystrokeDelay = 10,

        self = this,
        str = self.html(),
        i = 0,
        isTag,
        text;

    self.html("");

    (function type() {
        text = str.slice(0, ++i);
        if (text == str) {
            console.log("completed typing");
            return;
        }
        self.html(text);

        var char = text.slice(-1);
        if (char == "<") {
            isTag = true;
        }
        if (char == ">") {
            isTag = false;
        }

        if (isTag) {
            return type();
        }
        setTimeout(type, keystrokeDelay);
    } ());
};

var textElements = $(".text");

// For each div.text, type them out
textElements.each(function() {
    $(this).typeText();
});

麻烦的是,两者同时输出。我尝试了很多不同的事情,包括承诺,回调等等。但我无法让他们为这种情况工作。

JSFiddle

4 个答案:

答案 0 :(得分:2)

有很多方法可以做到这一点,我将展示的方式只有一个选择:

... 
self.html("");

$.fn.typeText.queue = $.fn.typeText.queue || [] // make a shared variable between the elements that call this function. This will call them in the order that were added

$.fn.typeText.queue.push(function type() {
    text = str.slice(0, ++i);
    if (text == str) {
        console.log("completed typing");
        $.fn.typeText.queue.shift(); // remove first element which just ended
        if ($.fn.typeText.queue.length > 0) { // if there are more
            $.fn.typeText.queue[0]() // start next
        }
        return;
    }
    ...
   setTimeout(type, keystrokeDelay);
});

if ($.fn.typeText.queue.length == 1) { // if its the first element added or no elements are in queue, call it
    $.fn.typeText.queue[0]();
}

};

这适用于任何数量的元素,并将按顺序调用它们。如果您希望组中的所有组都在同一时间运行,但在每个组中它们按顺序排列,则很容易更改功能。一种方法是将数组传递给方法并使用它而不是$ .fn.typeText.queue

完整代码:

$.fn.typeText = function() {
    var keystrokeDelay = 10,
    
    		self = this,
        str = self.html(),
        i = 0,
        isTag,
        text;

    self.html("");

    $.fn.typeText.queue = $.fn.typeText.queue || []

$.fn.typeText.queue.push(function type() {
        text = str.slice(0, ++i);
        if (text == str) {
            console.log("completed typing");
						$.fn.typeText.queue.shift(); // remove first element which just ended
          if ($.fn.typeText.queue.length > 0) { // if there are more
              $.fn.typeText.queue[0]() // start next
          }
          return;
        }
        self.html(text);

        var char = text.slice(-1);
        if (char == "<") {
            isTag = true;
        }
        if (char == ">") {
            isTag = false;
        }

        if (isTag) {
            return type();
        }
        setTimeout(type, keystrokeDelay);
    })

  if ($.fn.typeText.queue.length == 1) { // if its the first element added or no elements are in queue, call it
      $.fn.typeText.queue[0]();
  }
    
};

var textElements = $(".text");

textElements.each(function() {
    $(this).typeText();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="text">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse condimentum erat non velit euismod, ac tristique orci hendrerit. Nullam varius in sem sed tempor. Maecenas tincidunt, nisi a accumsan maximus, lectus sem congue ligula, eget tempus neque lacus quis arcu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque at purus eleifend, euismod nibh ut, auctor nulla. Phasellus convallis nulla urna, porta egestas justo sollicitudin sit amet.
</div>
<div class="text">
  Cras eros augue, tempor ut velit ornare, dignissim sollicitudin mi. Duis accumsan diam sed nulla sodales mattis. Pellentesque nec efficitur nunc, vitae rutrum massa. Pellentesque metus metus, consequat in auctor non, lobortis eget ligula. Sed volutpat, dui vulputate congue iaculis, justo massa consequat turpis, id facilisis ex sapien eget ligula. Praesent eu erat lorem. Mauris id dui orci. Mauris vitae urna tortor. Proin luctus id purus non maximus.
</div>

答案 1 :(得分:0)

像在jQuery插件中一样使用setTimeout和一个计数器。

var textElements = $(".text");
var counter = 0;

function typeDiv(){ 
     if(counter < textElements.length -1){
           textElements[counter].typeText();
            counter += 1;
            setTimeout(typeDiv, 2000);
      }
 }

答案 2 :(得分:0)

请注意,如果您将要使打字机效果的div放在一个div中,它们将自动一个接一个地前进。

<div class="text">
  <p>Lorem ipsum dolor sit amet…</p>
  <p>Cras eros augue, tempor…</p>
</div>
$(".text").typeText();
/* Instead of
textElements.each(function() {
    $(this).typeText();
}); */

请参见this JSFiddle

答案 3 :(得分:0)

如果可以使用ES6,建议您使用for循环而不是.each()的{​​{3}}和Promises。一旦了解了这些内容,它将使解决方案变得非常简单(为简洁起见,省略了一些未更改的代码):

$.fn.typeText = function() {
    var keystrokeDelay = 10,
        self = this,
        ...
    return new Promise(function(resolve) { // <--- new
        self.html("");
        self.addClass("visible"); // <--- new, explained below

        (function type() {
            text = str.slice(0, ++i);
            if (text == str) {
                console.log("completed typing");
                resolve(); // <--- new
                return;
            }
            self.html(text);
            ...
    });
}

var textElements = $(".text");

(async function run() { // new
    for (var j = 0; j < textElements.length; j++) {
        await $(textElements[j]).typeText();
        // Waits for the promise in the .typeText() func to be fulfilled
        // before progressing to the next iteration of the for loop
    }
})();

您还将需要一点CSS,因为如果默认情况下未隐藏文本,则底部div将完全可见,而顶部div仍在键入(请注意上面的self.addClass("visible")):

.text {
  display: none;
}
.visible {
  display: block;
}

请参见async functions。 (由于JSFiddle没有完整的ES6提示,因此代码警告显示为异步)