1

I have always missed a traverse function in jQuery, and I thinking that there might be one but I missed it. Say we have the following markup:

<ul>
    <li><a href="#">Foo</a></li>
    <li><a href="#">Bar</a></li>
    <li><a href="#">John</a></li>
    <li><a href="#">Doe</a></li>
</ul>

Now, if I want to add an active class to the anchor I clicked and deactivate the others, I would normally have to do this:

$('a').click(function() {
    $(this).addClass('active')
        .parent()
        .siblings()
        .find('a.active')
        .removeClass('active');
});

But what I want is to do is this:

var relation = function() {
    return $(this).parent().siblings().find('a.active');
};

$('a').click(function() {
    $(this).addClass('active').find(relation).removeClass('active');
});

See the difference? Now, find doesn’t take a function as argument, so I wrote something like this:

$.fn.traverse = function(fn) {
    return $.isFunction(fn) ? fn.call(this) : this;
};

As an example, this simple method makes it possible to create an "activate" plugin that lets the author specify the relationship to the deactivations:

$.fn.activate = function(fn) {
    return this.each(function() {
        $(this).addClass('active').traverse(fn || function() {
            return $(this).siblings();
        }).removeClass('active');
    });
});

And use it like this:

$('a').activate(function() {
    return $(this).parent().siblings().find('.active');
});

// or

$('a').activate(function() {
    return $(this).closest('ul').find('a.active').not(this);
});

// or

$('li').activate();

This will do the job. But my question is that there really should be something like this included in jQuery somewhere, no? I was looking at .map but it doesn’t work that way.

6
  • 2
    I don't understand. Why do you want to do it that way? Commented Sep 27, 2012 at 14:47
  • The find example makes no sense, did you mean removeClass? Commented Sep 27, 2012 at 14:47
  • I edited with an implementation example. The idea is to be able to traverse through an anonymous function instead of just selectors or traverse methods. Commented Sep 27, 2012 at 14:49
  • $("ul a").each(function() { doStuff(this); }); ? Commented Sep 27, 2012 at 14:50
  • 2
    This seems like a solution in search of a problem. The way you describe as the normal way looks much clearer to me than your new way, especially if you have a well-structured DOM. Commented Sep 27, 2012 at 14:59

1 Answer 1

1

The usual pattern in jQuery is:

  • find the elements you want to change, then

  • call a method to do something to them.

There are many other ways you could do things, but this way works fine and it is the way the library is designed to work. I would need a compelling reason to depart from that. (Not just “But I want to do it this way.”)

I would write it like this:

$('a').click(function() {
    $(this).closest('ul').find('a.active').removeClass('active');
    $(this).addClass('active');
});

Chaining is overrated. If you want to do two things, try writing two lines of code. You might like it!

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

5 Comments

There are a lot of traverse methods, such as .parent(), and most of them can take a selector for further filtering. But no traverse method takes a function, even though they added functions as arguments for many other things, such as .css(). That was my point.
I agree, and I’m starting to think that I failed to provide a clear purpose with my question...
@David I see what you mean. But I still think that’s just because it wouldn’t be useful to take a function there. Any time you write $(x).traverse(function () { return $(this).PIPELINE(); }); it would be shorter and clearer to just write $(x).PIPELINE().
How about $(this).traverse(function() { return hasChildren ? $(this).children() : $(this).parents() ).addClass('foo'); VS if (hasChildren) { $(this).children().addClass('foo'); } else { $(this).parents().addClass('foo')}? It would follow the "usual pattern" you describe.
@David That seems a little contrived, but in that case you’d write (hasChildren ? $(this).children() : $(this).parents()).addClass('foo'); or better still, use two lines of code: var targets = hasChildren ? $(this).children() : $(this).parents(); targets.addClass('foo');

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.