Colin
Colin

Reputation: 434

How to change class with jQuery at different times?

I have a hidden div that is being toggled. When it appears a span gets its class changed.

$('#websites').click(function(){
   $('#webthumbs').slideToggle('fast');
   $('#websites span').removeClass('icon-chevron-right').addClass('icon-chevron-down');
   $(this).addClass('open');
});

When the div is collapsed and hidden again, I need to change the span's class back to what it was originally. I tried this but it does not work:

$('#websites.open').click(function(){
   $('#websites span').removeClass('icon-chevron-down').addClass('icon-chevron-right');
   $(this).removeClass('open');
});

I have a feeling the second piece of code is not working because .open is not in the DOM when the script is loaded. How can I get the second chunk of code to work?

Upvotes: 0

Views: 78

Answers (4)

adeneo
adeneo

Reputation: 318212

You just need to toggle the classes to toggle the icon

$('#websites').click(function(){
   $('#webthumbs').slideToggle('fast');
   $('#websites span').toggleClass('icon-chevron-right icon-chevron-down');
});

The reason the code doesn't work, is because event handlers are added to elements matching the selector at execution time, adding the class later makes no difference.

If you need to toggle things that jQuery doesn't provide built-in methods for, you can use a flag

$('#websites').click(function(){

    var flag = $(this).data('flag');

    if ( flag ) {
        // do anything when the state is "open"
        $('#webthumbs').slideUp('fast');
    } else {
        // do anything when the state is "closed"
        $('#webthumbs').slideDown('fast');        
    }

    $(this).data('flag', !flag);
});

Upvotes: 2

zzzzBov
zzzzBov

Reputation: 179056

Use a delegate

You can use separate state-based event handlers via event delegation. Instead of binding either event to the #websites element, bind the event handlers to the nearest stable parent (I'm using 'body' here, but you may want to use a different selector):

$('body').on('click', '#websites:not(.open)', function () {
  ...executed when not open...
}).on('click', '#websites.open', function () {
  ...executed when open...
});

The delegate syntax will allow the click callback to be called on the #websites.open element despite it not existing in the DOM at the time of binding.

This form is particularly useful when you have significantly different behaviors between the two states.


Use a single callback

If you're just toggling everything, I'd recommend making use of jQuery's toggle functions and do it all in one go:

$('#websites').on('click', function () {
  $(this)
    .slideToggle('fast')
    .toggleClass('open')
    .find('span')
      .toggleClass('icon-chevron-right icon-chevron-down');
});

If you have separate functionality between the two states, you can use a simple if .is check:

$('#websites').on('click', function () {
  if ($(this).is('.open')) {
    $(this)
      .removeClass('open')
      ...
  } else {
    $(this)
      .addClass('open')
      ...
  }
});

Toggle multiple callbacks

You can expose callbacks as function references, and toggle which ones are active at any given time:

function openWebsites() {
  $(this)
    .off('click', openWebsites)
    .on('click', closeWebsites)
    .addClass('open')
    ...
}
function closeWebsites() {
  $(this)
    .off('click', closeWebsites)
    .on('click', openWebsites)
    .removeClass('open')
    ...
}
$('#websites').on('click', openWebsites);

Upvotes: 1

AGE
AGE

Reputation: 3792

  • When you click and the open class is not part of your clicked element, do something.
  • When you click and the open class is part of your clicked element, do otherwise.

By putting it into words you can see the similarity: Your click event is common among both actuons, this is what needs to happen only once. Within this click event you can use jQuery's hasClass() function to separate the purpose of your click events.

I took the liberty of optimizing your code a little by using caching for your variables. Let me know if you have any questions in the comments below.

JQuery:

$(document).ready(function() {
  $('#websites').click(function() {
    var websites = $(this),
        webthumbs = $("#webthumbs"),
        websitesSpan = $("#websites span");

    webthumbs.slideToggle('fast');

    if (websites.hasClass("open")) {
      websitesSpan.removeClass('icon-chevron-down').addClass('icon-chevron-right');
      websites.removeClass('open');
    } else {
      websitesSpan.removeClass('icon-chevron-right').addClass('icon-chevron-down');
      websites.addClass('open');
    }
  });
});

Upvotes: 0

alex
alex

Reputation: 7443

Echoing the previous answers, you could consolidate the two click handlers into one:

$('#websites').click(function() {
    if ($(this).hasClass('open')) {
        $('#websites span')
            .removeClass('icon-chevron-down')
            .addClass('icon-chevron-right');
    } else {
        $('#webthumbs').slideToggle('fast');
        $('#websites span')
            .removeClass('icon-chevron-right')
            .addClass('icon-chevron-down');
    }

    $(this).toggleClass('open');
});

Upvotes: 0

Related Questions