3

I'm trying to set a time out with a value, looking at this question I found out that having any form of setTimeout(function(), int); will 'skip' the timer, how would I do it to my code below?

$(".addtimer div").click(function(){
  $(this).toggleClass("remove");
  setTimeout(hide(this),2000);
});
function hide(name) {
  $(name).remove();
}

Output

".addtimer div" disappears instantly
Community
  • 1
  • 1
Zed
  • 638
  • 5
  • 20

3 Answers3

4

Change from this:

setTimeout(hide(this),2000); 

to this:

var self = this;
setTimeout(function() {
    hide(self);
},2000);

You were calling the function immediately and passing its return value to setTimeout rather than passing a function reference to setTimeout that can be called later. Keep in mind that (xx) means execute now. So hide(this) means to execute it now, not later. On the other hand, function() { hide(this); } is a function reference that can be called later.


Or, if you aren't using the hide function anywhere else, then you can integrate it in like this:

$(".addtimer div").click(function(){
    $(this).toggleClass("remove");
    var self = this;
    setTimeout(function() {
         $(self).remove();
    },2000);
});

A useful language feature is .bind() which can be used to "hardwire" the value of this or of arguments to callback functions. For example, you could do this:

$(".addtimer div").click(function(){
    $(this).toggleClass("remove");
    setTimeout(function() {
         $(this).remove();
    }.bind(this),2000);
});

The .bind(this) sets the current value of this to be the value of this when the callback function is called.

Or, if you still have the function hide(item), then you could use .bind to set the argument to hide() like this:

$(".addtimer div").click(function(){
    $(this).toggleClass("remove");
    setTimeout(hide.bind(null, this)), 2000);
});

Or, you could use an animation and let it provide the timing for you:

$(".addtimer div").click(function(){
    $(this).toggleClass("remove").delay(1000).slideUp(1000, function() {
        $(this).remove();
    });
});

As you can see, there are lots of options and which to use depends a bit on the situation, what you think makes the clearest code in your situation and what you are most comfortable in using.

jfriend00
  • 580,699
  • 78
  • 809
  • 825
  • Thank you, knew there was a better way – Zed Feb 07 '15 at 17:57
  • Added two other options to the answer. – jfriend00 Feb 07 '15 at 18:04
  • Ussch to using `var self = this;` As he is using jQuery then I'd go for [`.proxy`](http://api.jquery.com/jquery.proxy/). Otherwise I'd use `bind` or a `closure` (which I believe `proxy` uses for cross browser purposes). – Xotic750 Feb 07 '15 at 18:56
  • @Xotic750 - I added a couple `.bind()` options. I don't see any reason to use `$.proxy()`. Its main benefit was for when `.bind()` was not well supported across browsers. I'd much rather use the actual language feature now than a copy of it now that `.bind()` is well supported. – jfriend00 Feb 07 '15 at 20:40
  • Totally agree with using actual well supported language features over jQuery. Let the old and broken die. :P – Xotic750 Feb 08 '15 at 09:10
4

The first issue is that you need to pass a function to setTimeout (you were not; you were calling the function and passing its return value to setTimeout), and the second is that you need to handle this correctly.

There are two main ways to get the this handling right.

With .bind():

$(".addtimer div").click(function(){
    $(this).toggleClass("remove");

    // you could also use   function () { hide(this); }.bind(this)
    setTimeout(hide.bind(null, this), 2000);
});
function hide(name) {
    $(name).remove();
}

Or with an alias variable:

$(".addtimer div").click(function(){
    $(this).toggleClass("remove");
    var self = this; 
    setTimeout(function () { hide(self) }, 2000);
});
function hide(name) {
    $(name).remove();
}

One more option would be to get rid of the use of this altogether and use e.target (since this is a jQuery DOM event handler):

$(".addtimer div").click(function (e){
    $(e.target).toggleClass("remove");
    setTimeout(function () { hide(e.target) }, 2000);
});
function hide(name) {
    $(name).remove();
}
JLRishe
  • 90,548
  • 14
  • 117
  • 150
0

As I mentioned jQuery's .proxy, I thought it best to give an example.

function hide(name) {
    $(name).remove();
}

$('.addtimer div').click(function () {
    $(this).toggleClass('remove');
    setTimeout($.proxy(function () {
        hide(this);
    }, this), 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="addtimer">
    <div>Hello</div>
</div>

And with native bind

function hide(name) {
    $(name).remove();
}

$('.addtimer div').click(function () {
    $(this).toggleClass('remove');
    setTimeout(function () {
        hide(this);
    }.bind(this), 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="addtimer">
    <div>Hello</div>
</div>

And with a closure.

function hide(name) {
    $(name).remove();
}

$('.addtimer div').click(function () {
    $(this).toggleClass('remove');
    setTimeout((function (element) {
        return function () {
            hide(element);
        };
    }(this)), 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="addtimer">
    <div>Hello</div>
</div>

But of course none of this is necessary if you dropped the this and used the event argument of the callback.

function hide(name) {
    $(name).remove();
}

$('.addtimer div').click(function (e) {
    $(e.target).toggleClass('remove');
    setTimeout(function () {
        hide(e.target);
    }, 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="addtimer">
    <div>Hello</div>
</div>

And in modern ECMA5

function hide(name) {
    name.parentNode.removeChild(name);
}

[].forEach.call(document.querySelectorAll('.addtimer div'), function (item) {
    item.addEventListener('click', function (e) {
        e.target.classList.toggle('remove');
        setTimeout(function () {
            hide(e.target);
        }, 2000);
    }, false);
});
<div class="addtimer">
    <div>Hello</div>
</div>
Xotic750
  • 20,394
  • 8
  • 50
  • 71