0

I might just blame this on lack of sleep, but I'm having a hell of a time removing a class from some links.

Here's what's happening: I'm adding a class to a bunch of links when I check a checkbox, and then I hope to remove remove the class by unchecking the checkbox.

Also involved: Changing a class on the checkbox, which does work when I uncheck it. It just doesn't also remove the classes.

Here it is on jsFiddle

$(document).ready(function() {
    var revealer = $('.revealer .reveal');
    var hider = $('.revealer .hide');
    var days = $('.days li a');
    var revealed = $('.days li a.revealed');

    $(revealer).change(function() {
        $(this).toggleClass('reveal hide');
        $(days).each(function(i) {
            var day = $(this);
            setTimeout(function() {

                // $(day).toggleClass('revealed'); Toggle would work, but I don't want the delay when removing the class
                $(day).addClass('revealed');
            }, 300 * i);
        });
    });

    $(hider).change(function() {
        $(this).toggleClass('reveal hide');
        $(revealed).removeClass('revealed');
    });

});

In the jsFiddle, each link's background fades to black, one at a time. That much works. Unchecking should fade them all back to normal at once, but it doesn't.

4 Answers 4

2

Your problem is that there is no .revealer .hide when this runs:

var hider = $('.revealer .hide');

So hider is an empty list. You have similar problems with your other selectors: selectors match elements in the DOM as the DOM is when you use the selector. So when you say $('.revealer .hide').change(...) you're binding a change handler to whatever elements currently match .revealer .hide, if the elements that match change in the future then your change handler will not be bound to them (but see below for the standard solution to this sort of problem); similarly, if you remove the hide class from your checkbox and then put it back later (i.e. toggleClass) then your original change handler will still be bound to your checkbox: changes to the element's classes do not affect which handlers are or are not bound to it.

If we get rid of your cached selectors and switch your change handlers to use live then things will work:

$('.revealer .reveal').live('change', function() {
    $(this).toggleClass('reveal hide');
    $('.days li a').each(function(i) {
        var day = $(this);
        setTimeout(function() {
           day.addClass('revealed');
        }, 300 * i);
    });
});

$('.revealer .hide').live('change', function() {
    $(this).toggleClass('reveal hide');
    $('.days li a.revealed').removeClass('revealed');
});

Live version: http://jsfiddle.net/ambiguous/FaZkR/

The live and delegate functions act as proxies, they capture and redirect events based on the current state of the DOM rather than the state of the DOM when you are initially called.

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

2 Comments

Perfect, thanks! I thought live() might come into play, but I think I was under the impression that it was only for elements that were created. I guess this counts as that. Is it possible to stop the reveal function if you activate the hide function? If I'm reading it right, event.stopPropagation() might do it, but I'm having no luck.
@Thrillho: You'd have to store the timer IDs that setTimeout returns and then call clearTimeout with each one. JavaScript is single threaded so the two callbacks would execute in sequence.
1

The problem is that you're trying to cache states, rather than elements.

At the start of your script you cache $('.revealer .reveal') and $('.revealer .hide'), which are states that one element toggles between. It just doesn't work this way.

What happens is that you cache the matched element as revealer, and bind the first change function to it. What you think you're caching as hider is an empty set, as there is no matching element at the time your script loads. So your 2nd bind does nothing.

What you should do instead is change your change function to react differently based on the current class of the .revealer, or use live in place of typical binding, which does essentially what you are trying to do (essentially it watches for selector patterns, rather than binding to elements).

Comments

0

As others have pointed out, your code has the following issue:

  1. You are caching the jquery objects and binding the functions to them. So this is a problem because at the time of function binding, there is no element in the dom which satisfies the selector e.g $('.revealer .hide'). If you want to use these selectors, then you would have to use live function in jquery. http://api.jquery.com/live/

2.Else you can use an id selector on the checkbox and bind your functions accordingly.

Comments

0

There is no element with .hide as a class, so hider is never populated. As a quick and dumb fix, change

$(day).addClass('revealed');

to

$(day).toggleClass('revealed');

and delete your $(hider).change function

Then the checkbox in the fiddle works to some extent, and I'm sure you can figure it out from there.

2 Comments

Actually the hide class shows up when you first click it. If you inspect the checkbox, you'll see it appears. The checkbox starts with the reveal class. $(this).toggleClass('reveal hide');
Which happens after hider is assigned

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.