Karl Wilbur
Karl Wilbur

Reputation: 6217

Why is .addClass() and .removeClass() not working as expected?

I've googled and googled as well as search Stackoverflow. After looking though about 2 dozen other questions and not seeing the same situation, I decided to post a new question.

I don't need help getting this working. I just want to understand why it's not working. It just doesn't make any sense to me. So, my question is this:

Why would saving the jQuery object to a variable cause .addClass()/.removeClass() to not work?


What I'm trying to do

I have a span with class fa fa-eye and I want to change the fa-eye class to fa-eye-slash and back again with a button click.


What's not working

Here's my HTML:

<button id='visibility-toggle' data-status='hidden'>
  <span class='fa fa-eye'></span>
  Show
</button>

Here's my not working JavaScript:

jQuery('#visibility-toggle').click(function(e){
  e.preventDefault();
  var button = jQuery(this);
  var icon = button.find('.fa');
  if(button.data('status') === 'hidden') {
    show_stuff();
    button.html(button.html().replace('Show', 'Hide'));
    icon.addClass('fa-eye-slash');
    icon.removeClass('fa-eye');
    button.data('status', 'visible');
  } else {
    hide_stuff();
    button.html(button.html().replace('Hide', 'Show'));
    icon.addClass('fa-eye');
    icon.removeClass('fa-eye-slash');
    button.data('status', 'hidden');
  }
});

The string replacement is working and the show/hide functionality is working. The add/remove class functionality is not working.

I also tried making these changes.

HTML:

  <span class='show-icon fa fa-eye'></span>

JS:

  var icon = jQuery('.show-icon');

...still not working.


What is working

BUT here's the kicker. This works (not saving the jQuery object to a icon variable, but just looking it up each time):

jQuery('#visibility-toggle').click(function(e){
  e.preventDefault();
  var button = jQuery(this);
  if(button.data('status') === 'hidden') {
    show_stuff();
    button.html(button.html().replace('Show', 'Hide'));
    jQuery('.show-icon').addClass('fa-eye-slash');
    jQuery('.show-icon').removeClass('fa-eye');
    button.data('status', 'visible');
  } else {
    hide_stuff();
    button.html(button.html().replace('Hide', 'Show'));
    jQuery('.show-icon').addClass('fa-eye');
    jQuery('.show-icon').removeClass('fa-eye-slash');
    button.data('status', 'hidden');
  }
});

So, again, my question is: Why would saving the jQuery object to a variable cause .addClass()/.removeClass() to not work?

I have been working with jQuery for years and have never noticed this behavior before. It seems very strange. Am I just missing something super simple? Am I just too tired?


Additional info

I am seeing this behavior in Google Chome Version 45.0.2454.85 (64-bit) on Ubuntu 14.04.3. I have not tried other browsers or other systems.

I'm using jQuery 2.1.3 from: https://code.jquery.com/jquery-2.1.3.min.js

If I run this code in the console, it works as expected.

var icon = jQuery('.show-icon');
icon.addClass('fa-eye-slash');
icon.removeClass('fa-eye');
icon.addClass('fa-eye');
icon.removeClass('fa-eye-slash');

When I console.log(icon) and console.log(jQuery('.show-icon')) in the console, they look exactly the same.

[span.show-icon.fa.fa-eye, prevObject: n.fn.init[1], context: document, selector: ".show-icon"]

I am able to consistently toggle the code back and forth and one way it works, the other way it doesn't.


EDIT: This is what is now working as expected

HTML:

<button id='visibility-toggle' data-status='hidden'>
  <span class='fa fa-eye'></span>
  <span class='text'>Show</span>
</button>

JS:

jQuery('#visibility-toggle').click(function(e){
  e.preventDefault();
  var button = jQuery(this);
  var icon = button.find('.fa');
  var text = button.find('.text');
  if(button.data('status') === 'hidden') {
    show_stuff();
    text.text(text.text().replace('Show', 'Hide'));
    icon.addClass('fa-eye-slash');
    icon.removeClass('fa-eye');
    button.data('status', 'visible');
  } else {
    hide_stuff();
    text.text(text.text().replace('Hide', 'Show'));
    icon.addClass('fa-eye');
    icon.removeClass('fa-eye-slash');
    button.data('status', 'hidden');
  }
});

I know it's ugly. I'll refactor it in the morning.

Upvotes: 2

Views: 549

Answers (1)

doug65536
doug65536

Reputation: 6781

When you replace a chunk of the document with .html() you are throwing away all of the DOM objects that represent those elements and a bunch of new ones get created. Your saved copy of icon refers to the thrown-away object that got replaced by .html. Avoid .html like the plague - modify the object properly with .attr, .text, .empty .append, etc. .html is a security hole waiting to happen.

Upvotes: 4

Related Questions