.css () will not be applied after a delay

I wanted to change the background color to a div using jQuery css() and it worked, but then I tried to add some delay to it and for some reason it stopped working. What am I missing? Here is its MVC:

HTML:

 <div id="nodelay"></div> <div id="delay"></div> 

JS:

 $("#nodelay").hover(function() { $(this).css("background-color", 'gray'); }); $("#delay").hover(function() { setTimeout(function() { $(this).css("background-color", 'gray'); }, 500); }); 

https://jsfiddle.net/8eabfa2t/1/

+7
javascript jquery html css
source share
5 answers

From the MDN Documentation :

The " this " problem

The code executed by setTimeout() is called from a separate execution context for the function from which setTimeout was called . The usual rules apply for setting the this for the function being called, and if you did not set this in a call or using bind , it will by default be a global (or window ) object in non-strict mode or undefined in strict mode. It will not be the same as this value for a function called setTimeout . (Emphasis mine)

Since the functions passed to setTimeout are executed in a different context, this not bound. This means that this actually refers to window (or undefined in strict mode). You essentially make $(window).css(...) , which is not intended.

To combat this, you can use Function.prototype.bind to bind this context, as mentioned above. From the documentation:

The bind() method creates a new function that, when called, has the this keyword set to the provided value

Since this outside the setTimeout function is an element (since jQuery does this for you by explicitly binding this , as we do here), using $(this) will reference the #delay element

 $("#nodelay").hover(function() { $(this).css("background-color", 'gray'); }); $("#delay").hover(function() { setTimeout(function() { $("#delay").css("background-color", 'gray'); }.bind(this), 500); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="nodelay">Test</div> <div id="delay">Test</div> 

Alternatively, as already mentioned, grab this before introducing an anonymous function, or explicitly specify an element in the selector. If you are using ES6, another way to do this is to use arrow functions that do not bind their own this , referring to the context of the environment.

+7
source share

This is due to the scope of this in the function after the delay. There are many ways to get this, and each has its own preferences. My preferred method uses the Function.prototype.bind() method. It binds the this link to what you pass to it as the first object.

The reason why I succeed is difficult to explain (it makes a lot of sense in my head), but in the simplest way I can say that this is passed .bind() - this is the same this you are calling .hover() .

 $("#nodelay").hover(function() { $(this).css("background-color", 'gray'); }); $("#delay").hover(function() { setTimeout(function() { $(this).css("background-color", 'gray'); }.bind(this), 500); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="nodelay">t</div> <div id="delay">t</div> 

Another way people do this is to declare a variable outside the function in which the this call is confused. Due to how the Javascript scope works, any functions declared after this variable can access it.

 $("#nodelay").hover(function() { $(this).css("background-color", 'gray'); }); $("#delay").hover(function() { var self = this; setTimeout(function() { $(self).css("background-color", 'gray'); }, 500); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="nodelay">t</div> <div id="delay">t</div> 

Hope this helps.

+3
source share

helpfule: https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout#The_this_problem (if this link does not suit you with the "this" problem, find it on the page)

This works for you:

 $("#delay").hover(function() { var target = $(this); setTimeout(function() { target.css("background-color", 'gray'); }, 500); }); 
0
source share

This is not due to a timeout, but is related to the volume of this .

https://jsfiddle.net/8eabfa2t/4/

0
source share

Great answers from others. Since everyone else has pointed out a problem using the 'this' context, I will no longer explain this. However, if possible, I would like to introduce you to an alternative and very useful jQuery $ function . Proxy () . This allows you to set the context of your choice. The first argument to the proxy function is the function to be executed. The second argument is the context, and then which argument do you need to parse the function. Try the example below.

 $("#nodelay").hover(function() { $(this).css("background-color", 'gray'); }); $("#delay").hover(function() { setTimeout($.proxy(function() { $(this).css("background-color", 'gray'); }, this) , 500); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="nodelay">t</div> <div id="delay">t</div> 
0
source share

All Articles