Shawn H
Shawn H

Reputation: 13

Need clean jQuery to change parent's class when children are clicked

I needed some method of adding/removing classes of a parent element when it's children are clicked to reflect which child is currently selected. In this case a UL parent and LI children in a tab scheme. I needed a way to mark the current tab on the UL so I could style a background sprite on the UL; since styling my LI's backgrounds would not work with the graphics in this case.

I am a jQuery/Javascript/DOM novice, but was able to piece together an ugly solution for starters,


HTML

<!-- tabs -->
      <ul class="tabs currenttab-info">
        <li id="tab-info" class="info"><strong><a href="#">Information</a></strong></li>
        <li id="tab-write" class="write"><strong><a href="#">Write Feedback</a></strong></li>
        <li id="tab-read" class="read"><strong><a href="#">Read Feedback</a></strong></li>
      </ul>      

Javascript

    // send '.currenttab-x' to '.tabs' and remove '.currenttab-y' + '.currenttab-z'

    // when LI #tab-X is clicked ...
$( '#tab-info' ).click(function() {
        // ... find the UL and remove the first possible conflicting class
    $('.tabs').removeClass("currenttab-read");
        // ... find the UL and remove the other possible conflicting class
    $('.tabs').removeClass("currenttab-write");
        // ... find the UL and add the class for this LI
    $('.tabs').addClass("currenttab-info");  
});

    // ... repeat ...
$( '#tab-write' ).click(function() {
    $('.tabs').removeClass("currenttab-info");
    $('.tabs').removeClass("currenttab-read");
    $('.tabs').addClass("currenttab-write");  
});

$( '#tab-read' ).click(function() {
    $('.tabs').removeClass("currenttab-info");
    $('.tabs').removeClass("currenttab-write");
    $('.tabs').addClass("currenttab-read");  
});

This actually seems to be working, BUT it's a fumbling solution and I am sure there is a better way. Some of you jQuery ninjas will know how to put this functionality together really elegantly, any help?

Also I would like to add onto this so that the clicked LI is also given a class to show it is selected while the other LIs are stripped of any such class. The same sort of thing I already am doing for the UL; I can see how to do that with my awkward approach, but it will mean even more and more lines of messy code. If your improvement also included a way to do change classes of the LIs I'd appreciate it

FYI: I'm using jQuery Tools Tabs with this so there is more jQuery then I showed, but only the bit I quoted seems relevant.

Upvotes: 1

Views: 9081

Answers (4)

Floyd
Floyd

Reputation: 1918

$("UL.tabs LI A").bind('click',function(e){ //bind a Click-Event handler to the Links
  var $target = $(e.target); //the jQuery-Object-Reference to the clicked target ( $(this) should work too)
  var LIClasses =  $target.parents('LI').attr('class'); //A list of all Classes the parrent LI of the clicked Link have

  $target
   .parents('UL.tabs')
     //now you can remove and add classes to the parent "UL.tabs"
     .removeClass('...')
     .addClass('...')
     .end() //after .end() the chain for .parents-match is broken
   .parents('LI')
     //here you can add and remove classes from the parent LI
     .removeClass('...')
     .addClass('...')
     .end() //after .end() the chain for .parents-match is broken
   ;
});

Notes:

  1. jQuery is chainable.
  2. .removeClass() and .addClass() can work with multiple classnames at the same time by speration with a space (like .removeClass('class1 class2'))

The full solution:

var relevantClasses = ['read','write','info'];

$("UL.tabs LI A").bind('click',function(e){
  var $target = $(e.target);

  var relevantClass = '';
  for( var cl in $target.parents('LI').attr('class').split(' ') )
    if( jQuery.inArray(relevantClasses , cl) > -1 ){
      relevantClass = cl;
      break;
    }

  $target
   .parents('UL.tabs')
     .removeClass(jQuery.map(relevantClasses , function (className) { return 'currenttab-' + className; }).join(' '))
     .addClass('currenttab-'+relevantClass )
     .end()
   ;
});

Upvotes: -1

Reigel Gallarde
Reigel Gallarde

Reputation: 65264

html

I will remove ids of li if you are not using it for other purposes.

<ul class="tabs currenttab-info">
    <li class="info"><strong><a href="#">Information</a></strong></li>
    <li class="write"><strong><a href="#">Write Feedback</a></strong></li>
    <li class="read"><strong><a href="#">Read Feedback</a></strong></li>
</ul>

jQuery

$('.tabs li').click(function() {

    var $li = $(this);

    $li.addClass('current').siblings().removeClass('current'); // adds a current class to the clicked li

    var $ul = $li.parent();

    $ul.removeClass("currenttab-info currenttab-read currenttab-write")
       .addClass("currenttab-" + this.class );  // assuming li only holds one class e.g. class="write"
});

Upvotes: 5

Yves M.
Yves M.

Reputation: 3318

First of all you can chain the method calls...

$('.tabs').removeClass("currenttab-read currenttab-write").addClass("currenttab-write"); 

This would make the code much cleaner...

EDIT: I'll try such things in Fiddle http://jsfiddle.net/JvtAz/

Upvotes: -2

reko_t
reko_t

Reputation: 56430

You can just do something like this:

$('.tabs > li').click(function() {
    $(this).parent().attr('class', 'tabs').addClass('currenttab-'+$(this).attr('class'));
});

Upvotes: 1

Related Questions