【问题标题】:How to make .each() wait until each iteration has completed如何让 .each() 等到每次迭代完成
【发布时间】:2017-03-16 12:07:10
【问题描述】:

我正在使用来自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

【问题讨论】:

  • 尝试使用递归函数而不是each

标签: javascript jquery asynchronous callback promise


【解决方案1】:

如果您可以使用ES6,我建议使用Promisesasync functions,使用for 循环而不是.each()。一旦你理解了这些,它就会使解决方案变得非常简单(为简洁起见,省略了一些未更改的代码):

$.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;
}

this JSFiddle。 (代码警告显示异步,因为 JSFiddle 没有完整的 ES6 提示)

【讨论】:

    【解决方案2】:

    注意,如果你把你想要打字机效果的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】:

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

      ... 
      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>

      【讨论】:

      • 那是在$.fn.typeText = function() { }里面吗?
      • @binaryfunt 添加了 sn-p
      • 我对@9​​87654325@ 有点困惑。 || [] 是做什么的?
      • @binaryfunt 第一次调用函数时,$.fn.typeText.queue 未定义。 undefined 在 js 上是一个假值,所以用 ||您正在评估第二个选择,它给出了 [] 这是一个真实的值。所以 $.fn.typeText.queue 基本上被分配给 []。在任何其他后续调用中,$.fn.typeText.queue 已定义,因此不会评估第二选择,这样数组对于所有元素始终保持相同
      • if ($.fn.typeText.queue == undefined) {$.fn.typeText.queue = []}
      【解决方案4】:

      使用setTimeout 和一个计数器,就像在 jQuery 插件中所做的那样。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-08
        • 2014-07-01
        • 2021-09-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-22
        • 1970-01-01
        相关资源
        最近更新 更多