1

I have an ul list where every list-item (li) updates dynamically. On every new data for a specific li I want to apply a css effect via jQuery.

Current HTML is:

<ul>
 <li class="list-item">data0</li>
 <li class="list-item">data1</li>
</ul>

When <li>data0</li> is changed, I want to apply a css effect which changes that li's background color from white to green and remains green. Until that same li is updated again when I would like to change the background back to white then to green again.

My current jQuery logic is this, but doesn't seem to work:

if (!$('.list-item').hasClass('effect')){
     $('.list-item').addClass('effect');
} else {
     $('.list-item').removeClass('effect');
     $('.list-item').addClass('effect');
}

effect css:

.effect { background-color: rgba(45, 171, 64, 0.1); transition: 0.2s; } 

list-item css class doesn't set any background settings.

Here's a jsfiddle for current code: http://jsfiddle.net/dwmahagq/

9
  • try this api.jquery.com/toggleclass toggleClass Commented Nov 2, 2017 at 12:05
  • I hope it just because you haven't included it in the code example, but its .effect {} not effect {} Commented Nov 2, 2017 at 12:08
  • The transition must be set on the .list-item class, not the .effect class so that all li elements have it by default. Commented Nov 2, 2017 at 12:08
  • Both the logic and the syntax are wrong. For example, you need a dot in .effect to indicate it's a class Commented Nov 2, 2017 at 12:08
  • To put what @ScottMarcus said another way: the transition is on .effect so if you remove that class, it won't have a transition applied. Commented Nov 2, 2017 at 12:09

4 Answers 4

4

OK, starting with the CSS: to do a transition, you need to specify what you're transitioning from as well as what you're transitioning to, and the transition time should be on the "before" state (you have it on the "after" state).

So your CSS could look like this:

li {background-color: #FFF; transition: all 0.2s}
.effect { background-color: rgba(45, 171, 64, 0.1) } 

(the 'no effect' li has a background color and the transition time; the 'with effect' has a different background color.)

Now on to the javascript:

if (!$('.list-item').hasClass('effect')){
     $('.list-item').addClass('effect');

That "if" is harmless but unnecessary here. You can just call addClass to add a class; if the element already has the class nothing will happen.

} else {
     $('.list-item').removeClass('effect');
     $('.list-item').addClass('effect');

I assume you're trying to trigger the transition again even on elements that already have the effect --- this won't work the way you have it, because the javascript will batch those DOM changes together, so as far as the DOM is concerned nothing changed at all.

Instead you need to remove the effect and then do a setTimeout to bring it back.

Putting all that together results in:

$('.test').on("click",function() {
  $('.list-item').removeClass('effect');
  window.setTimeout(function() {
      $('.list-item').addClass('effect');
  }, 200); // <-- same duration as the transition
});
li {transition: all 0.2s; background-color:#FFF}

.effect { background-color: rgba(45, 171, 64, 0.1); } 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
 <li class="list-item">data0</li>
 <li class="list-item">data1</li>
</ul>

<div class="test">Click</div>

(Naturally in real life you'd probably want to apply this to a single list item instead of all of them as shown here; sounds from the comments like you have that part under control already).

Another method

A commenter points out that instead of setTimeout you can trigger a DOM reflow by reading a property from the DOM between removing and re-adding the class, e.g.

$('.test').on("click", function() {
  $('.list-item').removeClass('effect');
  void this.clientWidth; // read any dom property, doesn't matter which
  $('.list-item').addClass('effect');
});

This works well, but with one limitation: there can't be any transition duration set on the effect's exit. (In this case, if there's a transition duration set on li the animation will not restart; if the duration is set only on li.effect then it will work.) To demonstrate:

$('button').click(function() {
  var li = $(this).prev('li');

  li.removeClass('effect');
  void this.clientWidth;
  li.addClass('effect');
});
li {
  background-color: #FFF; padding: 0.5em
}

li.effect {
  background-color: #FFC;
  transition: all 1s;
}

.b {
  transition: all 1s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Here you can repeat the animation:</p>
<li>Item</li>
<button>This will work</button>

<p>...but here you can't (because the transition time on the 'li' prevents the effect from being visible):
<li class="b">Item</li>
<button>This will not work</button>

Sign up to request clarification or add additional context in comments.

18 Comments

I don't understand what you're saying, @WaldemarIce?
setTimeout() approach is not ideal.
@freedomn-m Hm... I don't remember. Google for "jquery add remove class to restart transition". I think you will find many links recommending setTimeout. Including stackoverflow answers.
Yeah, the not-so-vague insinuation that I couldn't possibly have remembered the simple setTimeout technique without "copying another answer", or that glitterio couldn't possibly have thought of cloning and replacing without google, was kind of weird and unnecessary.
Exactly, there's a huge difference between copying another answer verbatim (as implied) and providing a new answer based on vaguely similar principles.
|
1

Ideal solution, no unnecessary setTimeouts, element cloning, etc. One must just force the reflow process in browser, because browsers are batching - the have "lazy approach" - to dom writes.

$('button').on('click', function () {
  $('.list-item').each(function () {
    $(this).toggleClass('effect')
    if (!$(this).hasClass('effect')) {

      // Between two consequent DOM writes,
      // two .toggleClass() method calls,
      // insert command which reads DOM.
      // In other words, give browser no
      // chance to optimize (batch)
      // consequent DOM writes.
      // This creates something called
      // "layout trashing", which itself
      // is undesirable effect in most
      // cases. But as you can see,
      // sometimes is "the evil"
      // the simpliest solution :)
      void this.clientWidth

      $(this).toggleClass('effect')
    }
  })
})
.effect {
  transition: all 1s;
  background-color: yellow;
}
<ul>
  <li class="list-item">ITEM 1</li>
  <li class="list-item">ITEM 2</li>
</ul>

<button>run/restart effect</button>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

2 Comments

Yes except every list-item shouldn't be updated, which is already handled. The second part of my jQuery code doesn't work. } else { $('.list-item').removeClass('effect'); $('.list-item').addClass('effect'); }
@gitterio In other words, you wants to restart the transition?
0

Here is an example of switching the list item classes back and forth using a button click event. See the snippet below to see how you can remove or add the green background class:

$(function(){
  $("#switchBtn").click(function(){
    if (!$('.list-item').hasClass('effect')){
        $('.list-item').addClass('effect');
    } else {
         $('.list-item').removeClass('effect');
         //$('.list-item').addClass('effect');
    }
  });
});
.effect { 
  background-color: rgba(45, 171, 64, 0.1); transition: 0.2s; 
} 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
 <li class="list-item">data0</li>
 <li class="list-item">data1</li>
</ul>
<br /> 
<button id="switchBtn">Switch</button>

Comments

0

Please try this, by just click on li and it will show you effect.

    <!DOCTYPE html>
    <html>
    <head>
    <style>
    .effect { background-color: rgba(45, 171, 64, 0.1); transition: 0.2s; }
    </style>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script>
    $(document).ready(function(){
        $("li").click(function(){
      	$(this).toggleClass("effect");
       	});
    });
    </script>
    </head>
    <body>
    <ul>
     <li class="list-item">data0</li>
     <li class="list-item">data1</li>
    </ul>
    </body>
    </html>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.