views:

53

answers:

2

I need to use prototype JavaScript library on a project and wanted to add tabbed box to HTML.

The click handler has a simple task - set selected on parent <li> element and show linked DIV id element (rel tag on <li> has element id name)

<div class="tabInterface">
  <ul class="tabSelector">
     <li class="selected" rel="searchLast"><a href="#">Popularna iskanja</a></li>
     <li rel="searchMine"><a href="#">Moje zadnje iskanje</a></li>
  </ul>
  <div class="tabContent selected" id="searchMine">
      box 1 content
  </div>
  <div class="tabContent" id="searchLast">
     box 2 content

  </div> 
</div>

Final result after 1 hour of hard labour.

initTabInterface = function() {
    //requires prototype
    var tabClick = function(event){
        Event.stop(event);
        var $el = Event.element(event);

        var $menu = $el.up('.tabSelector');
        var liList = $menu.descendants().filter(function(el){return el.match('li')});
        liList.invoke('removeClassName', 'selected');
        $el.up().addClassName('selected');

        var rel = $el.up().readAttribute('rel');
        var $interface = $menu.up('.tabInterface');
        var tabList = $interface.descendants().filter(function(el){return el.match('.tabContent')});
        tabList.invoke('removeClassName', 'selected');
        $interface.down('#'+rel).addClassName('selected');
    };

    $$('.tabInterface .tabSelector li a').each(function(el){
        var $el = $(el);
        Event.observe($el, 'click', tabClick);
    });
};
Event.observe(window,"load", function(){
    initTabInterface();
});

Is there an easier way of traversing in prototype than with the bunch of up, down, filter, match, invoke and each?

+1  A: 

This is pretty much you can get:

initTabInterface = function() {
    //requires prototype
    var tabClick = function(event){
        Event.stop(event);         
        var $el = Event.element(event);
        var rel = $el.up().readAttribute('rel');

        // remove old selected classes
        $el.up('.tabInterface').select('.selected')
                               .invoke('removeClassName', 'selected');

        // add new selected classes
        [ $(rel), $el.up() ].invoke('addClassName', 'selected');
    };

    $$('.tabSelector li a').each(function(el){
        Event.observe($(el), 'click', tabClick);
    });
};
Event.observe(window,"load", function(){
    initTabInterface();
});​
galambalazs
That is it, select method was what I was looking for, but instead got sidetracked with descendants, filter, findAll, grep and others. Prototype is just so method poluted ...
gregor
A: 

I do not know Prototype well enough to quickly refactor your code, but you can use Element.siblings instead of going up and then descending down. Alternatively, you can just enumerate by class names (doesn't work well if you have more than one tab control).

$el.siblings().invoke('removeClassName', 'selected');

Also

$interface.down('#'+rel).addClassName('selected');

is unnecessary because you can only have one element with an id in the whole document. Can change it to:

$(rel).addClassName('selected');
Chetan Sastry
I totaly agree with an #id remark - there can be only one :)
gregor