Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
RAILS 3: nav_link helper for adding 'selected' class to navigation elements

UPDATE: It's a gem!

This helper has finally been moved into a gem called nav_lynx!

https://github.com/vigetlabs/nav_lynx

http://rubygems.org/gems/nav_lynx

Thanks to @brianjlandau and @reagent for getting that set up and tested!

Behold, the nav_link:

The nav_link helper works just like the standard Rails link_to helper, but adds a 'selected' class to your link (or its wrapper) if certain criteria are met. By default, if the link's destination url is the same url as the url of the current page, a default class of 'selected' is added to the link.

<%= nav_link 'My Page', my_path %>

When my_path is the same as the current page url, this outputs:

<a class="selected" href="http://example.com/page">My Page</a>

For more options and full usage details, see: http://viget.com/extend/rails-selected-nav-link-helper

Drop nav_link_helper.rb into app/helpers in your Rails 3.x app and enjoy.

UPDATE: Now with block support!

Same usage as link_to:

<%= nav_link 'http://example.com/page' do %>
  <strong>My Page</strong>
<% end %>
@jem
jem commented

This obviously goes beyond it, but might be good to mention the simple link_to_unless_current method available from ActionView as well.

@greypants
Owner

Yeah, that was actually where I started when first looking into this issue, and is definitely good to note. The main problem with that is just doesn't output the <a> tag by default, but just the text inside it if the page is 'selected'. That tends to visually break things when css styling relies on that element's presence. You can specify specific markup to output instead, but it gets a little messy.

@murdoch

I learned a lot from reading through the code here, thanks for sharing, I imagine I will use it often.

@column111

nice code ...
Will it be possible to place an additional and/or Tag inside the Tag?

e.g.:

  • Home

  • or something like
  • More
  • @column111

    nice code ...
    Will it be possible to place an additional span-tag and/or b-tag inside the Link-Tag?

    e.g.:
    li --> a --> span --> b

    span und b-Tag können auch classes haben

    @cayblood

    I'm getting errors if I use the controller_segment option and the current url does not have the segment being looked for.

    @rubenrails

    It would be cool if this can accept a block as the last argument, just as link_to

    @proosakos

    Has anyone successfully used this with dropdowns? Twitter bootstrap styled dropdowns are lists nested inside the wrapper

  • tag, which is what needs the "active" class assigned to it.

  • @greypants
    Owner

    Just updated to accept blocks!

    @awakia

    Really nice helpers and I used this a lot. Though it cause ActionController::RoutingError if the request method is other than GET. Just updating two methods fix this issue, so please update this gist. Thanks.

          def current_controller
    -      controller_for(@request.path)
    +      controller_for(@request.path, @request.request_method)
         end
    
    -    def controller_for(path)
    -      Rails.application.routes.recognize_path(path)[:controller]
    +    def controller_for(path, method = "GET")
    +      Rails.application.routes.recognize_path(path, method: method)[:controller]
         end
    
    @leisti

    This is useful. I have a couple of suggestions, though:

    First, please copy the usage instructions from
    http://viget.com/extend/rails-selected-nav-link-helper to README.markdown. You
    never know when and if the article will become unavailable for some reason, and
    it's a good idea to keep everything to do with one gist in one place.

    Second, it would be nice if one could also give an option for defining a class
    name to be given for unselected links, in case the user wants to define
    properties for such links in a .css file.

    Third, I didn't really see the point of methods that are called from only one
    place in the code -- I think they can confuse rather than clarifying the code,
    so I merged the code of methods link_classes, html_options and link to method
    LinkGenerator::to_html.

    I also made a couple of changes to parameter and variable names, for better
    self-documentation and more clarity. These are the resulting changes to the
    code (tested to work):

      def to_html
    -   html = link
    -   if @options[:wrapper]
    -      html = content_tag(@options[:wrapper], html, :class => wrapper_classes)
    -   end
    -   html.html_safe
    +   linked_classes = nil
    +   if @html_options[:class]
    +     linked_classes = @html_options[:class] + " #{class_name_to_be_used}"
    +   elsif !@options[:wrapper_class]
    +     linked_classes = class_name_to_be_used
    +   end
    +   merged_html_options = @html_options.merge(class: linked_classes)
    +   the_link = link_to(@title, @path, merged_html_options)
    +   if @options[:wrapper_class]
    +     the_link = content_tag(@options[:wrapper_class], the_link, :class => all_wrapper_classes)
    +   end
    +   the_link.html_safe
      end
    
    - def link_classes
    -   if @html_options[:class]
    -     @html_options[:class] + " #{selected_class}"
    -   elsif !@options[:wrapper]
    -     selected_class
    -   end
    - end
    
    - def html_options
    -   selected? ? @html_options.merge(:class => link_classes) : @html_options
    - end
    
    - def link
    -   link_to(@title, @path, html_options)
    - end
    
    + def unselected_class
    +   @options[:unselected_class] || ''
    + end
    
    + def class_name_to_be_used
    +   name = selected? ? selected_class : unselected_class
    + end
    
    - def wrapper_classes
    -   if selected?
    -     "#{selected_class} #{@options[:wrapper_class]}"
    -   else
    -     @options[:wrapper_class]
    -   end
    
    + def all_wrapper_classes
    +   "#{class_name_to_be_used} #{@options[:other_wrapper_classes]}"
    + end

    And this is a usage example, from a .html.erb file:

    <%= nav_link "Good stuff", good_stuff_path, {}, {selected_class: 'active', unselected_class: 'inactive', wrapper_class: 'li'} %>
    @greypants
    Owner

    Thanks for the feedback guys! All good stuff. I'll be moving this into a full repo soon for better collaboration.

    @firedev

    I think it should be nav_link_to and please make it a gem.

    @meal

    I tried to used it with engine mounted, unfortunately it doesn't work in such case :(

    @rubenrails

    +1 @greypants for accepting blocks!

    @C-E-Rios

    How can I make this work to ensure the when a user lands on the home-page the home tab is 'selected'?

    Currently no tabs are selected when you land on the website.

    Thanks.

    @diegocouto

    It seems to be running smoothly also on Rails 4, so maybe the Gem dependency could be updated.

    And by the way, thanks for this! :-)

    @greendezine

    Just what I needed, and working great on Rails 4! In a similar fashion to how url_segment works, Is it possible to add the selected class to all pages under a sub-directory structure ?

    For example: admin/products/item, admin/products/list etc.. Automatically adding the "selected" class to all urls within "/products" (within the same controller)

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Something went wrong with that request. Please try again.