devopix
devopix

Reputation: 187

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

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?

Upvotes: 18

Views: 17321

Answers (7)

Ruslan
Ruslan

Reputation: 31

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');
});

Upvotes: 0

huy19th
huy19th

Reputation: 122

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>

Upvotes: 0

Fabiano Oliveira
Fabiano Oliveira

Reputation: 601

[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 ' : ' ' ) %> ">

Upvotes: 0

Saurav Bhattarai
Saurav Bhattarai

Reputation: 1

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.

Upvotes: 0

Bemsen Daniel
Bemsen Daniel

Reputation: 191

@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>

Upvotes: 1

LeanKhan
LeanKhan

Reputation: 101

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>

Upvotes: 4

thataustin
thataustin

Reputation: 2045

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.

Upvotes: 40

Related Questions