How to highlight a current link in a navbar? algorithm

Say, I have this:

    <div class="navbar">
      <a href="/" class="active">Home</a>
      <a href="/link1">Link1</a>
      <a href="/link2">Link2</a>
      <a href="/link3">Link3</a>
    </div>

What’s the best way to highlight the current link depending on the current url? For link3, it’ll lool like this:

    <div class="navbar">
      <a href="/" class="">Home</a>
      
      <!-- 
        or without attribute 'class' at all
        <a href="/">Home</a>

      -->

      <a href="/link1">Link1</a>
      <a href="/link2">Link2</a>
      <a href="/link3" class="active">Link3</a>
    </div>

I figure I need to create a dictionary and pass it to html. How would I go about this? I can’t figure out.

My navbar is a list of data entries (with some dropdown that are just further lists) and they define what is active or note, built up from the Views themselves so they know that they are being called or not and of which template. :slight_smile:

1 Like

Check https://stackoverflow.com/questions/31572653/apply-css-class-on-element-if-current-page-in-phoenix.

Here’s how I did it in my project.

First, define a function in layout_view.ex,

  @doc """
  Set css class for active link.
  """
  @spec css_class(Plug.Conn.t(), String.t(), String.t(), String.t()) :: String.t()
  def css_class(conn, path, defaultClass, activeClass \\ "active") do
    if path == Phoenix.Controller.current_path(conn) do
      defaultClass <> " " <> activeClass
    else
      defaultClass
    end
  end

Then in my app.html.eex call the function:

<%= link "Dashboard", to: Routes.user_path(@conn, :show, @current_user), class: css_class(@conn, Routes.user_path(@conn, :show, @current_user), "nav-link") %>
6 Likes

Saw your code and thought I would ask about this here.

Would it make sense in this case to replace the string concatenation with IO Lists?

[defaultClass, " ", activeClass]
or even:
~E(<%= defaultClass %> <%= activeClass %>)

This should be an order of magnitude faster. See:

The static part in this case is merely a space, but is this still a performance win, however small?

phoenix_html’s content_tag (which is used by link) might not work with attrs (like :class) as iolists since it would call nested_attrs on them. And the “performance win” would probably be completely negligible.

Afaik the biggest performance difference comes from not joining everything into really big binaries and modifying those. I doubt class names fall anywhere close into that category of “big”.

I have improved this to suit my case which having /action_name/slug/etc

  @doc """
  Sets additional css class when for the current page.
  """
  def css_class(conn, path, default_class \\ nil, active_class \\ "active") do
    if is_nav_item_actvie?(conn, path) do
      "#{default_class} #{active_class}"
    else
      default_class
    end
  end

  @doc """
  Determine if the nav item is active or not.
  """
  def is_nav_item_actvie?(conn, path) do
    input_action_name = Enum.at(String.split(path, "/"), 1)
    current_action_name = List.first(conn.path_info)
    IO.inspect(%{c: current_action_name, p: input_action_name})
    current_action_name === input_action_name
  end

Just sharing~

1 Like