0

Is there an easy way to remove all other classes except the eventarget in with JavaScript.

For example, if I have a UL:

<ul>
     <li>list element</li>
     <li>list element</li>
     <li>list element</li>
     <li>list element</li>
     <li>list element</li>
     <li>list element</li>
</ul>

And I add click event listener with an event handler:

var UL = document.querySelector('ul');

function eventHandler(e) {
    var thiz = e.target;
    document.querySelectorAll('ul li').classList.remove("active");
    thiz.classList.add("active");
}

UL.addEventListener('click', function (e) {
    eventHandler(e);
});

How can I add a class on the element being clicked, while removing all the other "active" classes.

Here's a fiddle http://jsfiddle.net/298ZV/

2
  • within the event handler function, this refers to the element that triggered the event... so why not set the class directly to this? Commented May 26, 2014 at 14:39
  • 2
    Unrelated, but you can pass the callable of eventHandler directly into the listener, like so: UL.addEventListener('click', eventHandler) since they take the same arguments
    – casraf
    Commented May 26, 2014 at 14:40

2 Answers 2

4

Without jQuery:

[].forEach.call(document.querySelectorAll('ul li'), function(a){
    a.classList.remove('active');
});

JS Fiddle demo.

This uses the Array.prototype.forEach() to iterate over the NodeList and executes the function on each element.

You could also 'simplify' (though do note the scare-quotes...) a little further and do everything within the same forEach():

function eventHandler(e) {
    var thiz = e.target;
    [].forEach.call(document.querySelectorAll('ul li'), function(a){
        a.classList[a == e.target ? 'add' : 'remove']('active');
    });
}

JS Fiddle demo.

With regards to concerns expressd by @adeneo, in comments to his answer, that should the li elements contain other elements they will become the event.target I've added a simple closest() function to find the element you're looking for:

var UL = document.querySelector('ul');

function closest(origin, elem) {
    var current = origin;
    elem = elem.toLowerCase();
    while (current.tagName.toLowerCase() !== 'body' && current.tagName.toLowerCase() !== elem) {
        current = current.parentNode;
    }
    return current;
}

function eventHandler(e) {
    var that = closest(e.target, 'li');
    [].forEach.call(document.querySelectorAll('ul li'), function(a) {
        a.classList[that === a ? 'add' : 'remove']('active');
    });
}

UL.addEventListener('click', function (e) {
    eventHandler(e);
});

JS Fiddle demo.

References:

3
  • 1
    I love forEach! Just note it's unavailable before IE 9. Also, this doesn't exclude the current node
    – casraf
    Commented May 26, 2014 at 14:43
  • can't you just use e.target.tagName and then e.target.parentNode instead? That's what I'm doing. Commented May 26, 2014 at 14:59
  • Is that to find the li element-node? Because if it is then I'd rather use the function I added, since it frees me from having to navigate to specific points in the ancestry which, over time, gets ridiculous (and yes, I have tried e.target.parentNode.parentNode.parentNode... before now, and having learned better I will never do that again). Commented May 26, 2014 at 15:03
1

Do it a little differently, attach the event handler to the LI's, and keep them in a variable, then iterate over the LI's and remove the class from the ones that aren't this

var LI = document.querySelectorAll('ul li');

for (var i=LI.length; i--;) {
    LI[i].addEventListener('click', eventHandler);
}

function eventHandler() {
    this.className = 'active';

    for (var i=LI.length; i--;) {
        if (LI[i] != this) LI[i].className = '';
    }
}

FIDDLE

8
  • I think that would be (however neglectfully) slower, also not as easy to do
    – casraf
    Commented May 26, 2014 at 14:41
  • @ChenAsraf - that's probably true, but the problem with the OP's code is that it relies on the event.target, and has the event handler on a different element than what is actually clicked. The OP will notice this the second the LI contains another element, and the event.target is suddenly something else.
    – adeneo
    Commented May 26, 2014 at 14:44
  • They could use this directly there in that case, since you used this as well...
    – casraf
    Commented May 26, 2014 at 14:44
  • I can use this as the event handler is attached to the LI, the OP can't as this would be the UL, so the OP has to use event.target, which is fine, until you add a span or anchor inside the LI, and you're screwed as the event.target is no longer the LI.
    – adeneo
    Commented May 26, 2014 at 14:46
  • 1
    @ChenAsraf - That's what I just said in my answer ?
    – adeneo
    Commented May 26, 2014 at 14:47

Not the answer you're looking for? Browse other questions tagged or ask your own question.