0

If I write the html:

<script  src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<h1 id="message">
</h1>

and the JS:

messages = ["Here", "are", "some", "messages."]

$(function() {

  for (var i = 0; i < messages.length; i++) {
    $('#message').html(messages[i]).delay(1000);
  }

});

and load the page, I expect to see each string in the array show up with a delay in between. However, all I see is "messages." appear. It seems that the for loop iterates immediately through each value in the array before performing any delay.

I have seen another method for getting the desired visual result (How can I change text after time using jQuery?), but I would like to know why the earlier method does not work. What is going on when this code is executed?

Community
  • 1
  • 1
Jacquen
  • 986
  • 8
  • 18
  • delay() is asynchronous but the loop is not, so the loop is finishing before the delay and showing you the last element in the array. You need to use a callback function – The One and Only ChemistryBlob Aug 08 '16 at 21:58
  • The reason for 'messages' appearing is because it is the last item when the loop finishes iterating over the object. You need to create another variable outside the loop to save the whole thing. – Robert Aug 08 '16 at 22:08

4 Answers4

2

This is how i would delay my message changing.

function delayLoop(delay, messages) {
  var time = 100;

  $(messages).each(function(k, $this) {
      setTimeout(function()
      {
          $("#message").html($this); 
      }, time)
      time += delay;
  });
}
delayLoop(1000, ["Here", "are", "some", "messages."]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="message">
</div>

All I did was for each message delay by an additional delay time. It works in async mode so its not ui blocking and the messages will display a second after one another.

EDIT:

Removed the .delay from the .html it is redundant.

JQluv
  • 244
  • 1
  • 6
  • It's unnecessary to use both `setTimeout` and `delay`, as both are already async. You can simply do something like `$(messages).each(function(i, message) { setTimeout(function() { $('#message').html(message); }, delay * i); });` – Hamms Aug 08 '16 at 22:19
  • Thank you I didn't notice I did that, I actually just copy and pasted his message function because there was no point to re-write the entire thing but its fixed now, thank you. – JQluv Aug 08 '16 at 22:34
  • You're also passing `this` to the html call, even though `this` is undefined within the setTimeout callback. – Hamms Aug 08 '16 at 23:54
  • yeah I know, fixed. Didn't really expect it to run I dont like giving someone an answer I believe they need to learn so if you point them in the right direction...... Anyways above is fixed up you can run it. – JQluv Aug 09 '16 at 16:26
1

Note that jQuery's delay is specifically for effects; methods like html do not use the effects queue and are therefore not affected by delay.

This is a problem better solved with JavaScript's native setTimeout function. There are many ways to do this; in fact, you don't even need jQuery!

let messages = ["Here", "are", "some", "messages."];
let delay = 1000;
let header = document.getElementById("message");

messages.forEach(function(message, i) {
  setTimeout(function() {
    header.innerText = message;
  }, delay * i);
});
<h1 id="message" />
Hamms
  • 5,016
  • 21
  • 28
0

You would need something along the lines of

$(function() {
  for (var i = 0; i < messages.length) {
    var done=false;
    $('#message').html(messages[i]).delay(1000).queue(function(){
     done=true;
     $(this).dequeue();
    });
     if(done==true){ 
      i++;
     }
   }
});
mmkranz7
  • 139
  • 10
0

Thank you for the answers and comments--very helpful.

I also found this post helpful: Node.js synchronous loop, and from it wrote this (which also works):

function changeText() {
    var msg = messages.shift();
  $('#message').html(msg).show(0).delay(1000).hide(0, function() {
    if (messages.length > 0) {
        changeText();
    }
  });
}

(I used .show and .hide because without them only one of the array values appeared. I'm not sure why that is, but that's a question for another time.)

Community
  • 1
  • 1
Jacquen
  • 986
  • 8
  • 18