Miro
Miro

Reputation: 8650

that = $(this) for a setTimeout closure not working as expected

I want to use setTimeout in a button mouseout and refer to $(this)(the button) from the setTimeout. I'm using that = $(this), then use thatin the setTimeout but it doesn't work quite like expected.

If you go hover over each link slowly it works (1,2,3). But if you do it in fast, under 100ms, I get all 3s (3,3,3).

My guess is the closure is not working and the that var gets overwritten but i just can't figure out why.

Could you explain what's happening?

$("nav a")
  .mouseenter(function() {
    //
  })
  .mouseout(function(event) {
  	that = $(this);
  	setTimeout(function(){
        		console.log( that.data('uid') );
        }, 100);
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
  <ul>
    <li><a href="" data-uid="1">One</a></li>
    <li><a href="" data-uid="2">Two</a></li>
    <li><a href="" data-uid="3">Three</a></li>
  </ul>
</nav>

Upvotes: 0

Views: 49

Answers (2)

jjwilly16
jjwilly16

Reputation: 410

Your variable is being hoisted to a global without the var keyword. You should consider using strict mode to avoid this in the future.

Edit: As @Dekel pointed out in the comments, strict mode won't actually fix your problem, but would error out when you attempt to declare a variable without the var keyword. It's just a good practice to help avoid this type of unexpected behavior.

'use strict';

$("nav a")
  .mouseenter(function() {
    //
  })
  .mouseout(function(event) {
  	var that = $(this);
  	setTimeout(function(){
        		console.log( that.data('uid') );
        }, 100);
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
  <ul>
    <li><a href="" data-uid="1">One</a></li>
    <li><a href="" data-uid="2">Two</a></li>
    <li><a href="" data-uid="3">Three</a></li>
  </ul>
</nav>

Upvotes: 2

Dekel
Dekel

Reputation: 62616

You should set that to be a local variable (using var):

$("nav a")
  .mouseenter(function() {
    //
  })
  .mouseout(function(event) {
  	var that = $(this);
  	setTimeout(function(){
        		console.log( that.data('uid') );
        }, 100);
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
  <ul>
    <li><a href="" data-uid="1">One</a></li>
    <li><a href="" data-uid="2">Two</a></li>
    <li><a href="" data-uid="3">Three</a></li>
  </ul>
</nav>

In you current code, that is a global variable, and with each mouseout you override it to be the new element.

Upvotes: 3

Related Questions