Node.js, Express, EJS - Active class on current page in navigation
Asked Answered
C

7

18

I'd like to render a 'current' class on each of my navigation links depending on which page I'm on in my layout.ejs template.

Currently, my express controller index looks like this:

// About
exports.about = function(req, res) {
    res.render('content/about.ejs', {
        title: 'About'
    });
};

And in my layout.ejs I have the following, which I'd like be rendered dynamically.

<ul class="nav" id="nav">
    <li><a href="/">Home</a></li>
    <li class="current"><a href="/about">About</a></li>
    <li><a href="/">Contact</a></li>
</ul>

Any ideas of how to achieve this?

Comprehensible answered 21/11, 2014 at 1:0 Comment(0)
S
40

You could include a page_name: 'about' in the res.render data and then in the template something like:

<li <% if (page_name === 'about') { %>class="current" <% } %> ><a href="/about">About</a></li>

I didn't test the syntax, but that's the gist.

Seif answered 21/11, 2014 at 20:6 Comment(5)
Thank you. It is indeed correct, including the syntax. Much appreciated. I don't have any voting power yet, so I'll just accepted the answer.Comprehensible
Any way to get page_name automatically from app.get()'s input?Gwenora
req.path is probably the closest thing. See here for docs. You could easily strip out the slashes from the path to get something close to the page_name. But nested path names (eg /about/fred) wouldn't work quite right then. You could probably change the slashes to underscores to create a convention where the underscored version of req.path (eg, req.path.replace(/\//g, '_')) represented a page nameSeif
Great answer, solved my issue as well for <select> option as conditionally selectedPentavalent
@Seif buts its removing existing classJudicatory
V
4

You can pass a variable to the page like this, and use a condition inside the class attribute.

<a
    class="nav-link <%= page_name === 'home' && 'active' %>"
    href="#"
>
    Home
</a>
Velate answered 18/5, 2020 at 19:6 Comment(0)
P
1

@thataustin's answer is correct! but I lost the default class, so I found a way to keep the default class when it's not active. Hope it helps someone someday

<li            
    <% if (page_name === 'about') { %>
        class="nav-item current"
        <%} else { %>
        class="nav-item"
        <% } %> >
    <a href="/about">About</a>
</li>
Pudding answered 5/11, 2020 at 11:47 Comment(0)
P
0

Firstly, I have passed an object {navSelectTitle : "your-required-page-to-be-selected"} while rendering each page in the main app.js. Then, you can create a list in your ejs template like following:

<% const navItems = ["home", "articles", "videos", "audios"] %>

Then, you can loop through the list and add a class to make the link appear selected. (Here, I have used the class class="selected" to make the link appear selected)

Also, since I need my home page to have the href as href="/" and not <a href="/home", I have created a separate if statement for the case of "home" to accomodate this special case.

<% navItems.forEach(navlink => { %>
    <% if(navlink == navSelectTitle) { %>
        <% if (navlink == "home") {%>
            <li class="links selected"><a href="/"><%= navlink %></a></li>
        <% } else {%>
            <li class="links selected"><a href="/<%= navlink %>"><%= navlink %></a></li>
        <% } %>
                
    <% } else { %>
        <% if (navlink == "home") {%>
            <li class="links"><a href="/"><%= navlink %></a></li>
        <% } else {%>
            <li class="links"><a href="/<%= navlink %>"><%= navlink %></a></li>
        <% } %>
    <% } %>
<% }) %>

The code logic goes as follows:

  1. Loop through the list.
  2. If the list item equals to the object value that you passed while rendering the page, then the li tag will have the class="selected". If not, the else statement will create a normal link without the selected class.
  3. To accomodate the case where the home link should have href="/", there's a nested if-else statement inside the if statement for the said case.

NOTE: Using partial for the navigation bar can help dry your code and makes this logic a little convenient.

Pleader answered 12/6, 2021 at 14:59 Comment(0)
J
0

[update] - It is also possible to use a ternary logical:

  1. First: Allow access to any variable/data in your views by setting the "server.js" file like so:

app.get('*', (req, res, next) => { res.locals = ({ req: req }); next(); });

  1. Second: then in your frontend, use this ternary syntax you link, ex.:

<li class="nav-item <%- ( req.url.search('about') == 1 ? ' active ' : ' ' ) %> ">

Justifiable answered 30/7, 2022 at 23:14 Comment(0)
T
0

I think it's terrible to add additional value like req.path when you render. You will have to add that value every single time you render a page that has a navbar.

res.render('home', { path: req.path });

I simply use jquery to change the active nav item's class. The code to change class will be included in a ejs file (template) which will be included by every page so there is no additional code at all when you render!

HTML:

<li>
    <a href="Home">Home</a>
</li>
<li>
    <a href="Docs">Docs</a>
</li>

Javascript:

$(`a[href=${window.location.pathname}`).addClass('active');

The ejs file included by other pages would look like this:

<script src="/javascripts/axios-1.6.7.min.js"></script>
<script src="/javascripts/jquery-3.2.1.min.js"></script>
<script src="/javascripts/moment-2.18.1.min.js"></script>
<script src="/javascripts/daterangepicker.min.js"></script>
<script>
    $(`a[href=${window.location.pathname}`).addClass('active');
</script>
Tosh answered 6/3 at 3:37 Comment(0)
D
0

You can simply use jquery code it will be more convinient

$(document).ready(function () {
    const path = window.location.pathname;
    const escapedPath = $.escapeSelector(path);
    $(`a[href="${escapedPath}"]`).addClass('active');
});
Doriandoric answered 4/6 at 8:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.