As referenced in the MDN documentation:
The issue with "this"
When code is executed by setTimeout()
, it operates within a separate execution context compared to the function where setTimeout
was called from. The standard rules for setting the this
keyword for the called function apply, and if this hasn't been defined in the call or via bind
, it defaults to the global (or window
) object outside of strict mode, or becomes undefined in strict mode. It does not retain the same value as the this in the function calling setTimeout
. (Emphasis added)
Due to functions passed to setTimeout
being executed in a different context, the binding of this
is lost. Therefore, this
actually points to window
(or undefined
in strict mode). Essentially, it equates to performing $(window).css(...)
, which is unintended.
To address this, one can utilize Function.prototype.bind
to bind the context of this
as previously discussed. According to the documentation:
The bind()
method generates a new function that, upon invocation, has its this
marked to the specified value
Given that this
external to the setTimeout
function is the element (given that jQuery handles this through explicit this
binding as seen here), utilizing $(this)
will target 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, you can pre-capture this
before entering the anonymous function, or explicitly name the element in the selector. If utilizing ES6, another technique involves leveraging arrow functions, which do not have their own this
bound, instead directly refering to the enclosing context's.