Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.css() won't get applied after a delay

I wanted to change the background-color on a div dinamically using jQuery's 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's an MVC of it:

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/

like image 460
iuliu.net Avatar asked May 22 '16 20:05

iuliu.net


People also ask

Why is my CSS animation not working?

CSS animations work on most modern mobile and desktop browsers. However, your animations may not work if you're using an older browser or a version of your browser that hasn't been updated in several years, simply due to lack of browser support.

How do I add a delay in CSS?

The CSS animation-delay property has the following syntax: animation-delay: [time] | initial | inherit; As you can see, there are three possible values: time, initial, and inherit. The first option is [time], which is the number of seconds or milliseconds before the animation starts.

Is visibility Animatable CSS?

visibility is technically animatable, but in a round about way. You need to hold the property for as long as needed, then snap to the new value. visibility doesn't tween between keyframes, it just steps harshly. If you want to fade, you use opacity .


1 Answers

From the MDN documentation:

The "this" problem

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

Since functions passed to setTimeout are executed in a different context, this is not bound. That would mean this actually refers to window (or undefined in strict mode). You are essentially doing $(window).css(...) which is not intended.

To combat this, you may 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 its this keyword set to the provided value

Since this outside of the setTimeout function is the element (as jQuery does this for you via explicit this binding like we're doing here), using $(this) will refer to 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>

You can alternatively, as mentioned, capture this before you enter the anonymous function, or explicitly mention the element in the selector. If you're using ES6, another way to do this is to use arrow functions, which do not bind their own this, referring to that of the enclosing context.

like image 106
Andrew Li Avatar answered Oct 08 '22 12:10

Andrew Li