Javascript: setTimeout Closure inside nested (double) for loops

泪湿孤枕 提交于 2019-12-08 09:33:47

问题


Hi I'm trying to recreate what http://austintexas.wayblazer.com/locations/austin-tx is doing for its search textbox - displaying typed character and rotating between several phrases.

My initial logic is to read the phrases from an array like so: [abc,123,XYZ] and then to use a split and forEach to separate the string into characters and create a typing effect using setTimeout.

However, when I attempt this logic, I seem to have trouble getting the desired effect. I've been reading through a lot of these questions on SO, but few of them address issues with nested for loops.

Desired results:

  • a b c (in a typing effect character by character)
  • clear textbox
  • 1 2 3 (in a typing effect character by character)
  • clear textbox
  • X Y Z

Actual results:

  • abc123XYZ (abc shows up together at once, followed by 123, then XYZ)

$(document).ready(function() {

  $('input#123').attr("placeholder", "");
  var phrases = ['abc', '123', 'XYZ'];

  for (var i = 0; i < phrases.length; i++) {
    $('input#123').attr("placeholder", "");
    (function(ind) {
      var sentence = phrases[ind];
      sentence.split("").forEach(function(char, index) {
        setTimeout(function() {
          $('input#123').attr("placeholder", $('input#123').attr("placeholder") + char);
        }, ind * 500);
      });
    })(i);
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input id="123" />

Is there an elegant solution for what I want to achieve, and hopefully some explanation to enlighten me would be very much appreciated!


回答1:


The great thing with javascript is closures, so you can create function closures, and push onto an array to act like a stack.. You can then pop these function off inside a setInterval..

$(document).ready(function(){
	var input = $('input#123'),
	    phrases = ['Stack Overflow','Is the place you come','To get help with coding stuff'],
        typedelay = 140,
        waitops = 5,
        cmds = [],
        ph = input.attr.bind(input, 'placeholder');
  
    function clear() {
      ph('');
    }
  
    function addLetter(a) {
      return function () {
        ph(ph() + a);
      }
    }
  
    function doLoop() {
      cmds = [];
      for (var i=0; i<phrases.length;i++) {
        cmds.push(clear);
        var sentence = phrases[i];
        sentence.split("").forEach(function(char,index) {
          cmds.push(addLetter(char));
        });
        //at the end of each sentence, there is a pause. lets do some no ops,.. 5*300 ms
        for (var nn = 0; nn < waitops; nn ++) cmds.push(0);
      }
      //lets make this thing go on forever.. :)
      cmds.push(doLoop);
    }
      
    doLoop();
  
    var icheck = setInterval(function () {
      //is there a cmd waiting, pop it and run it.
      var cmd = cmds.shift();
      if (cmd) { cmd(); }
    }, typedelay);
  
    input.focus(function () {
      clearInterval(icheck);
      cmds = [];
      ph('What your want');
    });
    
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input id="123" />



回答2:


Here's a recursive function that shifts the array and stops when array has no length

typePhrases(['Message #1','Another message','And the last one'], 300)

function typePhrases(phrases, speed){
  // shift array to get word/phrase
  var word = phrases.length && phrases.shift(), $input= $('#test').attr('placeholder','');
  // don't do anything if no phrase/word
  if(word){    
    word.split('').forEach(function(char, i){
       setTimeout(function(){
          // use attr(attributeName, function) to update
          $input.attr('placeholder', function(_, curr){
               return curr + char;
          });
          // on last letter call function again to get next word
          if(i=== word.length-1){
            setTimeout(function(){
              typePhrases(phrases, speed); 
              },speed);
            }
       },i*speed)
    });
  }   
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="test" placeholder=''>


来源:https://stackoverflow.com/questions/39885261/javascript-settimeout-closure-inside-nested-double-for-loops

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!