Best way to add "current" class to nav in Rails 3

NavigationRuby on-Rails-3

Navigation Problem Overview


I have some static pages in a navigation menu. I want to add a class like "current" to the item which is currently displaying.

The way I am doing so is to add tons of helper methods (each for one item) to check the controller and action.

def current_root_class
  'class="current"' if controller_name == "homepage" && action_name == "index" 
end

<ul>
  <li <%= current_root_class %>><%= link_to "Home", root_path %>

Is there any better way to do so!? My current way is so stupid......

Navigation Solutions


Solution 1 - Navigation

I made a helper called nav_link:

def nav_link(link_text, link_path)
  class_name = current_page?(link_path) ? 'current' : ''

  content_tag(:li, :class => class_name) do
    link_to link_text, link_path
  end
end

used like:

nav_link 'Home', root_path

which will produce HTML like

<li class="current"><a href="/">Home</a></li>

Solution 2 - Navigation

Use the current_page? helper to determine whether or not you should assign the "current" class. For example:

<%= 'active' if current_page?(home_about_path) %>

Note you can also pass a path (not only a hash of options), e.g: current_page?(root_path).

Solution 3 - Navigation

Not truly an answer here, because I'm using quite the same way as you are. I've just defined helper methods to test for multiple controller or actions:

In application_helper.rb

  def controller?(*controller)
    controller.include?(params[:controller])
  end
  
  def action?(*action)
    action.include?(params[:action])
  end

Then you can use if controller?("homepage") && action?("index", "show") in your views or other helper methods…

Solution 4 - Navigation

I use this nav_link(text, link) function in application_helper.rb (Rails 3) to get the job done and it rolls my bootstrap twitter 2.0 nav bar links for me.

def nav_link(text, link)
    recognized = Rails.application.routes.recognize_path(link)
    if recognized[:controller] == params[:controller] && recognized[:action] == params[:action]
        content_tag(:li, :class => "active") do
            link_to( text, link)
        end
    else
        content_tag(:li) do
            link_to( text, link)
        end
    end
end

Example:

<%=nav_link("About Us", about_path) %>

Solution 5 - Navigation

The way I've done it is to add a helper function in the application_helper

def current_class?(test_path)
  return 'current' if request.request_uri == test_path
  ''
end

Then in the nav,

<%= link_to 'Home', root_path, :class => current_class?(root_path) %>

This tests the link path against the current page uri and returns either your current class or an empty string.

I've not tested this thoroughly and I'm very new to RoR (moving over after a decade with PHP) so if this has a major flaw I'd love to hear it.

At least this way you only need 1 helper function and a simple call in each link.

Solution 6 - Navigation

To build off @Skilldrick 's answer...

If you add this code to application.js it will make sure that any dropdown menus with active children will also be marked as active...

$('.active').closest('li.dropdown').addClass('active');

To recap supportive code > Add a helper called nav_link:

def nav_link_to(link_text, link_path)
  class_name = current_page?(link_path) ? 'active' : ''

  content_tag(:li, :class => class_name) do
    link_to link_text, link_path
  end
end

used like:

nav_link_to 'Home', root_path

which will produce HTML like

<li class="active"><a href="/">Home</a></li>

Solution 7 - Navigation

I think the best way is

application_helper.rb:

def is_active(controller, action)       
  params[:action] == action && params[:controller] == controller ? "active" : nil        
end

And in menu:

<li class="<%= is_active('controller', 'action') %>">

Solution 8 - Navigation

I know it is a out dated answer, but you can easily ignore all these current page check by using a link_to helper wrapper, called active_link_to gem, it works exactly what you want, add a active class to current page link

Solution 9 - Navigation

Here is the full example, on how to add an active class on bootstrap menu page in rails view.

    <li class="<%= 'active' if current_page?(root_path) %>"><%= link_to 'Home', controller: "welcome" %></li>
    <li class="<%= 'active' if current_page?(about_path) %>"><%= link_to 'About us', about_path %></li>
   <li class="<%= 'active' if current_page?(contact_path) %>"><%= link_to 'Contact us', contact_path %></li>

Solution 10 - Navigation

I use an awesome gem called [Tabs on Rails][1].

[1]: http://github.com/weppos/tabs_on_rails "Tabs On Rails"

Solution 11 - Navigation

I have a more succinct version of nav_link that works exactly like link_to, but is customized to output a wrapping li tag.

Put the following in your application_helper.rb

def nav_link(*args, &block)
    if block_given?
      options      = args.first || {}
      html_options = args.second
      nav_link(capture(&block), options, html_options)
    else
      name         = args[0]
      options      = args[1] || {}
      html_options = args[2]

      html_options = convert_options_to_data_attributes(options, html_options)
      url = url_for(options)

      class_name = current_page?(url) ? 'active' : nil

      href = html_options['href']
      tag_options = tag_options(html_options)

      href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href
      "<li class=\"#{class_name}\"><a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a></li>".html_safe
    end
  end

If you look at the above code and compare it to the link_to code in url_helper.rb, the only difference is that it checks if the url is the current page, and adds the class "active" to a wrapping li tag. This is because I'm using the nav_link helper with Twitter Bootstrap's nav component which prefers links to be wrapped inside li tags and the "active" class applied to the outer li.

The nice thing about the above code is that it allows you to pass in a block into the function, just like you can do with link_to.

For example, a bootstrap nav list with icons would look like:

Slim:

ul.nav.nav-list
  =nav_link root_path do
    i.icon-home
    |  Home
  =nav_link "#" do
    i.icon-user
    |  Users

Output:

<ul class="nav nav-list">
  <li class="active">
    <a href="/">
      <i class="icon-home"/>
      Home
    </a>
  </li>
  <li>
    <a href="#">
      <i class="icon-users"/>
      Users
    </a>
  </li>
</ul>

In addition, just like the link_to helper, you can pass in HTML options into nav_link, which will be applied to the a tag.

An example of passing in a title for the anchor:

Slim:

ul.nav.nav-list
  =nav_link root_path, title:"Home" do
    i.icon-home
    |  Home
  =nav_link "#", title:"Users" do
    i.icon-user
    |  Users

Output:

<ul class="nav nav-list">
  <li class="active">
    <a href="/" title="Home">
      <i class="icon-home"/>
      Home
    </a>
  </li>
  <li>
    <a href="#" title="Users">
      <i class="icon-users"/>
      Users
    </a>
  </li>
</ul>

Solution 12 - Navigation

For me personally i used a combination of answers here

<li class="<%= 'active' if current_page?(inventory_index_path) %>"><a href="#">Menu</a></li>

I am using materialize css and my way of making the main categories collapsible is by using the code below

$('.active').closest(".collapsible.collapsible-accordion")
            .find(".collapsible-header")
            .click();

hope it helps someone

Solution 13 - Navigation

The current_page? method isn't flexible enough for me (say you set a controller but not an action, then it'll only return true on the controller's index action), so I've made this based on the other answers:

  def nav_link_to(link_text, link_path, checks=nil)
    active = false
    if not checks.nil?
      active = true
      checks.each do |check,v|
        if not v.include? params[check]
          active = false
          break
        end
      end
    end

    return content_tag :li, :class => (active ? 'active' : '') do
      link_to link_text, link_path
    end
  end

Example:

nav_link_to "Pages", pages_url, :controller => 'pages'

Solution 14 - Navigation

Yep! Check out this article: A Better Way to Add a ‘selected’ Class to Links in Rails

Drop nav_link_helper.rb into app/helpers and it can be as easy as:

<%= nav_link 'My_Page', 'http://example.com/page' %>

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.

There's a gist here: https://gist.github.com/3279194

UPDATE: This is now a gem: http://rubygems.org/gems/nav_link_to

Solution 15 - Navigation

I use a simple helper like this for top level links so the /stories/my-story page highlights the /stories link

def nav_link text, url

  active = (url == request.fullpath || (url != '/' && request.fullpath[0..(url.size-1)] == url))

  "<li#{ active ? " class='selected'" : '' }><a href='#{url}'>#{text}</a></li>".html_safe

end

Solution 16 - Navigation

Let me show my solution:

_header.html.erb:

  <ul class="nav">
    <%= nav_tabs(@tabs) %> 
  </ul>

application_helper.rb:

 def nav_tabs(tabs=[])
    html = []
    tabs.each do |tab| 
      html << (content_tag :li, :class => ("current-page" if request.fullpath.split(/[\??]/)[0] == tab[:path]) do
        link_to tab[:path] do
          content_tag(:i, '', :class => tab[:icon]) +
          tag(:br) +
          "#{tab[:name]}"
        end
      end)        
    end
    
    html.join.html_safe
  end

application_controller.rb:

before_filter :set_navigation_tabs

private
def set_navigation_tabs
  @tabs = 
    if current_user && manager?
      [
        { :name => "Home", :icon => "icon-home", :path => home_index_path },
        { :name => "Portfolio", :icon => "icon-camera", :path => portfolio_home_index_path },
        { :name => "Contact", :icon => "icon-envelope-alt", :path => contact_home_index_path }
      ]
    elsif current_user && client?
      ...
    end

Solution 17 - Navigation

According to the answer by Skilldrick, I'll change it to the following:

def nav_link(*args, &block)
  is_active = current_page?(args[0]) || current_page?(args[1])
  class_name = is_active ? 'active' : nil

  content_tag(:li, class: class_name) do
    link_to *args, &block
  end
end

to make it much more useful.

Solution 18 - Navigation

This version is based on @Skilldrick's one but allows you to add html content.

Thus, you can do:

nav_link "A Page", a_page_path

but also:

nav_link a_page_path do
  <strong>A Page</strong>
end

or any other html content (you can add an icon for instance).

Here the helper is:

  def nav_link(name = nil, options = nil, html_options = nil, &block)
    html_options, options, name = options, name, block if block_given?
    options ||= {}

    html_options = convert_options_to_data_attributes(options, html_options)

    url = url_for(options)
    html_options['href'] ||= url

    class_name = current_page?(url) ? 'current' : ''
    content_tag(:li, :class => class_name) do  
      content_tag(:a, name || url, html_options, &block)
    end
  end

Solution 19 - Navigation

I think I came up with a simple solution that might be helpful for a lot of use cases. This lets me:

  • Support not only plain text but HTML inside link_to (e.g. add an icon inside the link)
  • Add just few lines of code to application_helper.rb
  • Append active to the whole class name of the link element instead of it being the sole class.

So, add this to application_helper.rb:

def active_class?(class_name = nil, path)
  class_name ||= ""
  class_name += " active" if current_page?(path)
  class_name.strip!
  return class_name
end

And on your template you can have something like this:

<div class="col-xs-3">
  <%= link_to root_path, :class => active_class?("btn btn-outline-primary", root_path) do %>
    <i class="fa fa-list-alt fa-fw"></i>
  <% end %>
</div>

As bonus you can specify or not a class_name and use it like this: <div class="<%= current_page?(root_path) %>">

Thanks to previous answers 1, 2 and resources.

Solution 20 - Navigation

Create a method in ApplicationHelper as below.

def active controllers, action_names = nil
  class_name = controllers.split(",").any? { |c| controller.controller_name == c.strip } ? "active" : ""
  if class_name.present? && action_names.present?
    return action_names.split(",").any? { |an| controller.action_name == an.strip } ? "active" : ""
  end
  class_name
end

Now use it in view as below use cases.

1. For all action of any specific controller

<li class="<%= active('controller_name')%>">
....
</li>

2. For all action of many controllers (with comma seperated)

<li class="<%= active('controller_name1,controller_name2')%>">
....
</li>

3. For specific action of any specific controller

<li class="<%= active('controller_name', 'action_name')%>">
....
</li>

4. For specific action of many controllers (with comma seperated)

<li class="<%= active('controller_name1,controller_name2', 'action_name')%>">
....
</li>

5. For some specific actions of any specific controller

<li class="<%= active('controller_name', 'index, show')%>">
....
</li>

6. For some specific actions of many controllers (with comma seperated)

<li class="<%= active('controller_name1,controller_name2', 'index, show')%>">
....
</li>

Hope it helps

Solution 21 - Navigation

all these work with simple nav bars, but what about drop down sub-menu ? when a sub-menu is selected the top menu item should be made 'current' in this case tabs_on_rails me be the solution

Solution 22 - Navigation

This is how I solved in my current project.

def class_if_current_page(current_page = {}, *my_class)
    if current_page?(current_page)
      my_class.each do |klass|
        "#{klass} "
      end
    end
  end

Then..

li = link_to company_path 
    class: %w{ class_if_current_page( { status: "pending" }, "active" ), "company" } do  
      Current Company

Solution 23 - Navigation

My easy way -

application.html.erb,

<div class="navbar">
	<div class="<%= @menu1_current %> first-item"><a href="/menu1"> MENU1 </a></div>
	<div class="<%= @menu2_current %>"><a href="/menu2"> MENU2 </a></div>
	<div class="<%= @menu3_current %>"><a href="/menu3"> MENU3 </a></div>
	<div class="<%= @menu4_current %> last-item"><a href="/menu4"> MENU4 </a></div>
</div>

main_controller.erb,

class MainController < ApplicationController
	def menu1
		@menu1_current = "current"
	end

	def menu2
		@menu2_current = "current"
	end

	def menu3
		@menu3_current = "current"
	end

	def menu4
		@menu4_current = "current"
	end
end

Thanks.

Solution 24 - Navigation

If also you want to support html options hash in the view. For example if you want to call it with other CSS class or id, you can define the helper function like this.

def nav_link_to(text, url, options = {})
  options[:class] ||= ""
  options[:class] += " active"
  options[:class].strip!
  link_to text, url, options
end

So in the view, call this helper the same way you'd call link_to helper

<%= nav_link_to "About", about_path, class: "my-css-class" %>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeterWongView Question on Stackoverflow
Solution 1 - NavigationSkilldrickView Answer on Stackoverflow
Solution 2 - NavigationjpemberthyView Answer on Stackoverflow
Solution 3 - NavigationYannisView Answer on Stackoverflow
Solution 4 - NavigationPeytonView Answer on Stackoverflow
Solution 5 - NavigationfullybakedView Answer on Stackoverflow
Solution 6 - NavigationTheEricMillerView Answer on Stackoverflow
Solution 7 - NavigationIPIvlievView Answer on Stackoverflow
Solution 8 - NavigationfuyiView Answer on Stackoverflow
Solution 9 - NavigationLittle PhildView Answer on Stackoverflow
Solution 10 - NavigationicecreamView Answer on Stackoverflow
Solution 11 - NavigationJoe ChenView Answer on Stackoverflow
Solution 12 - NavigationPetros KyriakouView Answer on Stackoverflow
Solution 13 - NavigationunrelativityView Answer on Stackoverflow
Solution 14 - NavigationDan TelloView Answer on Stackoverflow
Solution 15 - NavigationcomplisticView Answer on Stackoverflow
Solution 16 - NavigationMike AndrianovView Answer on Stackoverflow
Solution 17 - NavigationTom ChenView Answer on Stackoverflow
Solution 18 - NavigationBenjamin J. BenoudisView Answer on Stackoverflow
Solution 19 - NavigationJuan Diego GonzalesView Answer on Stackoverflow
Solution 20 - NavigationLalit Kumar MauryaView Answer on Stackoverflow
Solution 21 - Navigationuser762579View Answer on Stackoverflow
Solution 22 - NavigationGN.View Answer on Stackoverflow
Solution 23 - NavigationLwin Htoo KoView Answer on Stackoverflow
Solution 24 - NavigationKen HibinoView Answer on Stackoverflow