Skip to content

Instantly share code, notes, and snippets.

@greypants
Forked from reagent/nav_link.rb
Last active October 17, 2023 05:49
Show Gist options
  • Save greypants/3279194 to your computer and use it in GitHub Desktop.
Save greypants/3279194 to your computer and use it in GitHub Desktop.
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 %>
@proosakos
Copy link

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
    Copy link
    Author

    Just updated to accept blocks!

    @awakia
    Copy link

    awakia commented Feb 4, 2013

    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
    Copy link

    leisti commented Feb 6, 2013

    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
    Copy link
    Author

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

    @firedev
    Copy link

    firedev commented Mar 17, 2013

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

    @meal
    Copy link

    meal commented Mar 24, 2013

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

    @rubenrails
    Copy link

    +1 @greypants for accepting blocks!

    @firedev
    Copy link

    firedev commented Apr 4, 2013

    @C-E-Rios
    Copy link

    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.

    @greypants
    Copy link
    Author

    @diegocouto
    Copy link

    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
    Copy link

    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