Pedro Hoehl Carvalho
Pedro Hoehl Carvalho

Reputation: 2433

Nested setTimeout not working with 'this' variable

I'm trying to check list items one by one and have a 1 second delay between them. There are four lists and I can 'check the items all at the same time on load by using:

$( document ).ready( function(){
  $('#featured').children('li').each(function() { 
    $(this.tagName + '> ul').children('li').each(function() {
        $(this.tagName  + '> i').attr('class', 'fa fa-check-square-o fa-lg');       
    });
  });
});

However, when I try to include setTimeout nothing happens.

$( document ).ready( function(){
  $('#featured').children('li').each(function() { 
    $(this.tagName + '> ul').children('li').each(function() {
      setTimeout(function() 
      {
        $(this.tagName  + '> i').attr('class', 'fa fa-check-square-o fa-lg');
      }, 1000); 

    });
  });
}); 

I suspect the this variable is not within the scope of the setTimeout handler.

UPDATE:

Since I posted the question I cleaned up my code a bit. By changing this to el I was able to make the code work, however the the setTimeout function delays only once and changes the classes for all list items at the same time.

Here is my HTML (There are more items on the top list, but I reduced it to one example):

<ul id="featured">
  <li><h3>Full Home Inspections</h3>
      <ul class="fa-ul">
        <li><i class="fa fa-square-o fa-lg"></i> Gutters &amp; Downspouts</li>
        <li><i class="fa fa-square-o fa-lg"></i> Eaves, Soffits &amp; Fascia</li>
        <li><i class="fa fa-square-o fa-lg"></i> Main Water Shut-off</li>
        <li><i class="fa fa-square-o fa-lg"></i> Heating and AC</li>
      </ul>
      <a href='inspections.html'> And more</a>
  </li>
</ul>

And here is my new Script:

$( document ).ready( function(){
    $('#featured > li > ul').children('li').each(function(i, el) {
      setTimeout(function(){
        $(el.tagName + '> i').attr('class', 'fa fa-check-square-o fa-lg');
      }, (i * 1000));       
    });
});

I realize this is no longer a matter of scope, but I still need help with it, so I updated the question.

Upvotes: 1

Views: 289

Answers (3)

Ian Clark
Ian Clark

Reputation: 9347

You're right it's not, and if you encounter this problem the fix is often to store the this variable in another local variable which remains in scope even in the nested function. However, perhaps you're looking for something like this (JSFiddle):

$(document).ready(function () {

    $('#featured').children('li').each(function () {
        lagAddingClasses($("i", this));
    });

    function lagAddingClasses($to) {
        var interval = setInterval(function() {
            if($to.length == 0) {
                clearInterval(interval);
                return;
            }
            $to.eq(0).attr('class', 'fa fa-check-square-o fa-lg');
            $to = $to.slice(1);
        }, 1000);   
    }
});

Having not seen your markup it's unclear actually what you're trying to achieve, but your selector usage seems perculiar.

Upvotes: 1

Yep, its a problem about loosing scope: What you can do is save a reference of the this value in a variable and then use it inside the setTimeout.

$( document ).ready( function(){
  $('#featured').children('li').each(function() { 
    $(this.tagName + '> ul').children('li').each(function() {
      var that = this
      setTimeout(function() 
      {
        $(that.tagName  + '> i').attr('class', 'fa fa-check-square-o fa-lg');
      }, 1000); 

    });
  });
}); 

Upvotes: 0

user1517081
user1517081

Reputation: 965

Why not:

$( document ).ready( function(){
  setTimeout(function() 
  {
    $('#featured').children('li').each(function() { 
      $(this.tagName + '> ul').children('li').each(function() {
        $(this.tagName  + '> i').attr('class', 'fa fa-check-square-o fa-lg');
      });
    });
  }, 1000);
}); 

Upvotes: 0

Related Questions