2

I have a simple task, when I click on a link, I want to add bg-success class into its child, delay 800ms then remove that class.

I can trigger addClass() after click on a link, like this, it works:

$('a').on('click', function() {
    $(this).find('span.code').addClass('bg-success');
});

I can also trigger removeClass after click too, it works (alone) too:

$('a').on('click', function() {
    $(this).find('span.code').removeClass('test-class');
});

I can make it delay, after addClass, let fadeOut, it works:

$('a').on('click', function() {
    $(this).find('span.code').addClass('bg-success').delay(800).fadeOut(400);
});

But when I want to addClass, delay, then removeClass, it does not work, it remains the same and does nothing. I even tried with long time like 8000ms but still can't make it works. If I replaced it with 2 addClass(), it adds 2 classes at the same time, and does not care about delay():

$('a').on('click', function() {
    $(this).find('span.code').addClass('bg-success').delay(8000).removeClass('bg-success');
});

I have tested with everything I can find on Stackoverflow. The weird part is, it does delay when I work with fadeIn, fadeOut and everything else. The delay() just be ignored when work with addClass/removeClass at the same time.

Anyone have issue like this, please suggest some ideas. Thank you.


Update:

Read comments and you guys will see the answer is here.

Btw, can anyone with deep knowledge about jQuery explain for me, why they decided to do that? I mean I see it is easy to make this way, addClass then delay then removeClass, what is the real reason makes the jQuery development team decided to make it won't work this way?

I would like to know because if I have the reason, I won't step into the trap like this again.

Sang Dang
  • 501
  • 1
  • 11
  • 26

3 Answers3

1

If you want to use .delay() then you need to use .queue() to specify the queue of functions that will be executed on the element after the delay.

Your code should be:

$('a').on('click', function() {
  $(this).find('span.code').addClass('bg-success').delay(800).queue(function() {
    $(this).removeClass('bg-success');
    $(this).dequeue();
  });
});

This is a DEMO snippet:

    $('a').on('click', function() {
      $(this).addClass('bg-success').delay(800).queue(function() {
        $(this).removeClass('bg-success');
        $(this).dequeue();
      });
    });
a {
  width: 100px;
  height: 100px;
  background-color: blue;
  cursor: pointer;
}
.bg-success {
  background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<a>A link</a>

But you can also simulate this effect with setTiemout():

$('a').on('click', function() {
  var myLink = $(this).find('span.code');
  myLink.addClass('bg-success');

  setTimeout(function() {
    myLink.removeClass('bg-success');
  }, 800);
});

delay() limitations:

To get further details why delay() is better used with effects only, you can see in the jquery documentation that unfortunately, it has some limitations over the native JavaScript setTimeout function:

The .delay() method is best for delaying between queued jQuery effects. Because it is limited. It doesn't, for example, offer a way to cancel the delay—.delay() is not a replacement for JavaScript's native setTimeout function, which may be more appropriate for certain use cases.

cнŝdk
  • 31,391
  • 7
  • 56
  • 78
  • Thank you, I can get the idea. Btw I updated my question, could you help me little further? Why they decided to do that? I mean I see it is easy to make this way, addClass then delay then removeClass, what is the real reason makes the jQuery development team decided to make it won't work this way? I would like to know because if I have the reason, I won't step into the trap like this again. – Sang Dang May 11 '16 at 14:33
  • 1
    @SangĐặng That's because `.delay()` function has some limitations over the native JavaScript `setTimeout` function, take a look at my EDIT. – cнŝdk May 11 '16 at 14:41
0

From the docs:

The .delay() method is best for delaying between queued jQuery effects. Because it is limited—it doesn't, for example, offer a way to cancel the delay—.delay() is not a replacement for JavaScript's native setTimeout function, which may be more appropriate for certain use cases.

(https://api.jquery.com/delay/)

I'd suggest using setTimeout like so:

$('a').on('click', function() {
  var span = $(this).find('span.code');
  span.addClass('bg-success');

  setTimeout(function() {
    span.removeClass('bg-success');
  }, 800);
});
user3297291
  • 22,592
  • 4
  • 29
  • 45
0

You can always try with plain old JS with setTimeout() function, to wait for certain period of time before doing something else:

$('a').on('click', function() {
    var myCode = $(this).find('span.code').addClass('bg-success');
    setTimeout(function(){
       myCode.removeClass('bg-success');
    }, 800);
});
Slavenko Miljic
  • 3,836
  • 2
  • 23
  • 36