Free-for-all: Tab Helper (Summary)
The first RailsWay free-for-all came off quite well. Many of you posted your favorite solutions to the problem of tab-based navigation, as posed by Nate Morse.
Jamis’ Take
Of all the solutions posted, my personal favorite was the pragmatic and simple CSS-based solution given by Mr. eel (Nate Morse came to the same solution independently):
I take a completely different approach. I ID the body of the page with the name of the current controller. Then I use a descendent CSS selector to highlight the current tab based on the body id and an id given to each link. I don’t bother with replacing the current tab link with a span. If the user wants to click that link again… then it’s the same as refreshing. Totally up to them.
With html like:
1 2 3 4 5 6 <body id="users"> <ul> <li><a href="/users" id="usersNav">Users</a></li> <li><a href="/comments" id="commentsNav">Comments</a></li> <li><a href="/posts" id="postsNav">Posts</a></li> </ul>I would use CSS like this
1 2 3 4 5 6#users #usersNav, #comments #commentsNav, #posts #postsNav { background:red; font-weight:bold; }
What a great approach. Although I would make the choice of the body ID explicit (rather than depending on the controller name), it is otherwise really nice. It shrugs off the whole issue of “should the current tab be a link” by saying it just doesn’t matter—every tab is always a link. Such pragmatism gets right to the heart of the Rails Way: implement just what matters, and nothing more.
Koz’s Take
A number of solutions relied on tightly coupling the controller and tabs. While this may seem like a time-saver at first, I believe that it’s unlikely to remain useful as your application grows. You’ll find yourself moving functionality into strange locations in order to make your tabs highlight correctly.
The problem is amplified with a restful application where your choice of controllers are dictated by the resources that you’re managing. You may have a list of comments in several different sections of your application, but not want to highlight the ‘comment’ tab whenever you display them.
Personally, I prefer the really simple approach of a before filter and a navigation partial.
1 2 3 4 |
def set_current_tab @current_tab = :people end |
Thanks, everyone for your submissions!


I had the same issue as Nate and just five minutes ago I thought to myself… humm tabs… humm the guys from RailsWay came with some useful stuff … and I just open my rss reader and vuala … the rails way. Cheers guys!
After looking at a lot of the comments submitted in the original post, I have no doubt why so many Rails Apps are slow.
As someone struggling to “get” rails, I’d love to see a working example, if anyone has one to show.
This solution is also posted in the book titled CSS Mastery: http://www.friendsofed.com/book.html?isbn=1590596145
It is a great resource for all CSS related design needs, and has helped me many times.
Jamis’ solution really did seem good idea to me conceptually at first, but thinking about it further I’m not so sure. The content is linked to the presentation, as the css now has to know that “users”, “comments”, “posts” etc. exist.
Has the concept of separation of style from content been hammered home so hard that I now can’t see the forest for the trees? Or is using this method likely to be an actual problem in future development, or an accessibility issue?
Cheers,
L.
I just found this post to late, when search for good solution for TAB. This is what i came up with my self. This way use span if the current page is selected.
First of all i created a Object that would hold my sitemap.
Then I created a method in application.rb where we build the menu depending on logged on user and the roles. (This could also be an ApplcationHelper method.
The partial rendering on the menu. I render the menu in application.rhtml
<%= render :partial => 'shared/mainmenu' %>Notice the controller.navigation to be able to get the public method navigation from the application controller that is inherited in the controller.
Thus proving my point from above.
@NeoMike:
Instead of just being grumpy, please contribute something and explain why.
@OldMike
You’re right, I am grumpy, and I apologize.
I’m grumpy because I’m tried of having to work on other people’s code that is over-engineered, and it’s not just a Rails problem. Developers (for various reasons) over-engineering their code. Then people turn around and while that things are so damn slow, complex, or hard to maintain. This isn’t a new problem.
The CSS based solution listed in this post is a great example of how to tabs in a simple and elegant way and it would require only a single line of ruby (such as [%= params[:controller] %] )to put into a generic layout.
With the comment above as the exception (it support sub-menus – yes I was being grumpy), many of the tab implementations put into the original post performed the same functionality, but in dozens of lines of code.
Lets see… dozens of lines of code vs. one. Which one will be easier to troubleshoot? Which one will be higher performance?
Nobody is perfect, and we all need to learn something new. Next time you write a piece of code, stop and think about if there is a more pragmatic way to implement it.
I’m just being exceptionally grumpy lately because I’m stuck maintaining a rails application that is about 100% over engineered. I’m slowly re-writing it and reduced the code-size by about 20% so far while maintaining functionality.
Since the code is internal, I can’t post an example, but I can say that behaviour.js really does a bang up job in reducing the amount of embedded javascript hooks on an ajax enabled application. Also helps make the HTML so much more readable.
Moving a lot of logic from several controllers into the single model where it belongs also helped reduce a lot of redundant code.
Sorry NeoMike, I didn’t realize that my solution was so over engineered. There is no more functionality then I need added. I just came form C# och Delphi development and I can tell that my solution here is less code then just my xml sitemap file was i ASP.NET ;)
I often see ruby code that is not well design but from a OO perspective, that code is properly the code you call not over-engineered.
The CSS-based approach isn’t ideal (though pretty much everyone uses it) from a usability perspective. It’s just bad to have a live link (even one hidden by styling) pointing to the page it’s on. It breaks user expectation about how links work and think how confusing it could potentially be for a user with visual disabilities.
@Nick: actually, it doesn’t break expections at all. It’s still a link. You click on it, and you go where the link points to (which is here). Lots of sites do this, even big prominent ones (go to http://www.ibm.com and click the IBM logo, for instance). And I don’t see how that could be any more confusing for a visually disabled person than for a sighted person. The screenscraper reads through the links and includes one that points to the current page. Not a big deal.
In Mr. eel’s method how does the current tab highlighted? Suppose if I have a tabbed menu which updates some parts of the page with a XHR call how do we handle the highlighting? In this case the menu portion is never updated.
I just use the TabNav plugin. It works amazingly well http://blog.seesaw.it/pages/tabnav.
I do like the simplicity of the CSS solution, but I also agree with the concern of separation.
Thank you all for your ideas
my way of doing it, is like Mr. eel but with dynamic way and for many tabs under one action.
TestController<style type="text/css" media="screen"> #anything #<%= @current_id %> { background:red; font-weight:bold; } </style> <div id="anything"> <ul> <% @models.each do |model| -%> <li id="<%= model.label %>"><%= link_to model.label, :action => "action_with_many_tabs",:id => model.label %></li> <% end -%> </ul> </div>that’s all, sure you’ve to apply your special CSS style it’s just an example so, when @current_id equals model.label the effect will appear on this list element.
enjoy!
@Jamis:
It’s probably not something that would throw an experienced user off, but it does break expectations—we expect that links take us somewhere other than where we are right now. If the expectation is weakening because it’s so often violated now, it’s a shame, because it’s a useful expectation to have.
Jakob Nielsen talks about some related issues here: http://www.useit.com/alertbox/within_page_links.html
Great discussion, here’s what I ended up doing: http://www.vaporbase.com/postings/A_tab_helper_that_works_for_me
This won’t work in all situations, but I think it works nicely for simple tab navigation. It merges several ideas I’ve seen around. Obviously you can change the generated HTML to use a span instead of a link if you are so inclined.
In application_helper.rb:def layout_link_to(link_text, path) curl = url_for(:controller => request.path_parameters['controller'], :action => request.path_parameters['action']) html = '' options = path == curl ? {:class => 'current'} : {} html << content_tag("li", link_to(link_text, path, options)) end<ul> <%= layout_link_to "Users", users_path %> <%= layout_link_to "Comments", comments_path %> <%= layout_link_to "Recent Posts", recent_posts_path %> </ul>I am torn! I like the simplicity and compactness of the recommended approaches.
However, I prefer TabNav (part of the Widgets plugin). I like having one file declaratively handle my tabs, what they say, what they link to, and when they highlight.
Take a look and judge for yourself: http://blog.seesaw.it/articles/2007/09/03/what-changes-in-the-new-widgets-tabnav
If someone could cook up a solution with the same advantages as Tabnav (a sweet DSL, one declarative file to describe the tabs’ behavior) with the compactness of the Mr. eel-like solution, I would like to see it!