2

I need to change the navbar link color when I'm navigating through my Razor-Pages. I tried to use JavaScript, but it seems that the pages are getting rerendered every time when I go to another page and I'm not able to toggle my elements.

Here is what I have tried so far:

Nav bar links

<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between ml-5">
                 <ul class="navbar-nav flex-grow-1">
                    <li class="nav-item">
                        <a class="nav-link active-link" asp-area="" asp-page="/Index">Home</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" asp-area="" asp-page="/Privacy">Privacy</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" asp-page="/Movies/Index">Movies</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" asp-page="/Genres/Index">Genres</a>
                    </li>
                </ul>
</div>

site.js

const links = document.querySelectorAll('.nav-link');
let currentActive = document.querySelector('.active-link');

const changeClass = (e) => {
    //e.preventDefault();
    currentActive.classList.remove('active-link');
    e.target.classList.add('active-link');
};

links.forEach((el) => el.addEventListener('click', changeClass));

Here I save the current active link in my js and when I navigate to another page, the page gets rerendered and my js file is reloaded again so that the value that I have previously is set back to the default active link.

What I have: current

What I want: desired

I found one possible solution, but it seems to be a bit of hard-coding and working around, I believe there should be a better way.

P.S. Ideally, a solution using JavaScript would be best, however, I'm not sticking to anything here.

Miraziz
  • 509
  • 7
  • 15
  • Are you using some frontend framework? Or is it plain HTML and JS, where the header is in each html page implemented? – Jozott Apr 10 '21 at 20:24
  • Well, I'm using the default UI setup which is `bootstrap`, and there is also `jquery`, but for this problem, I'm using plain `js` because I'm just playing around and want to see how it will work for my upcoming project so that I can decide whether I will move on with `pure js` or something else. – Miraziz Apr 10 '21 at 21:41

3 Answers3

6

Use the DOMContentLoaded event rather than the click event. That way the change will be applied to the actual page after it has rendered. At the moment, you are applying the change to the links on the page that you are navigating from:

document.addEventListener('DOMContentLoaded', () => {
    document.querySelectorAll('.nav-link').forEach(link => {
        if (link.getAttribute('href').toLowerCase() === location.pathname.toLowerCase()) {
            link.classList.add('active-link');
        } else {
            link.classList.remove('active-link');
        }
    });
})
Mike Brind
  • 28,238
  • 6
  • 56
  • 88
0

Doesn't necessarily need javascript.
TagHelpers can be used for a server-sided solution, see my answer on a similar question here.

For example, the tag helper uses a custom attribute on the nav bar elements to describe which page each link corresponds to. Then when the tag helper processes the attribute, it compares the value with the current address, and if it matches, it adds the active class.

It also removes the attribute so that it's not available to the user in the response HTML. Doing it server sided also ensures that the highlight will be available for users with javascript disabled.

TeamDman
  • 649
  • 6
  • 26
0

I had the same problem and started with this approach: Create a different layout for each page. Every layout has highlighted the link of interest. The issue was that you have many layouts with small changes, only in the nav bar links.

So, I decided to improve the solution, I finally it was much more simple than expected:

Here the snippet of the second approach:

// _Layout.cshtml

<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">VS22Netcore01</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul id="mainMenu" class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            **<a id="linkHome"** class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            **<a id="linkPrivacy"** class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                        <li class="nav-item">
                            **<a id="linkAbout"** class="nav-link text-dark" asp-area="" asp-page="/About">Acerca de</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()  <!-- inserta la pagina en el container} -->
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - VS22Netcore01 - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    **@await RenderSectionAsync("Scripts", required: true)**
</body>

Please, note the code betwwen double asteriks.

Then, In each content page I added something like this:

//Privacy.cshtml, a sample page
@page
@model PrivacyModel
@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>



@section Scripts 
{
<script>        
    **$('#linkPrivacy').removeClass('text-dark');
    $('#linkPrivacy').addClass('text-info');**
</script>
}

As you can see, The script in each content page changes the style of the links. In that way you have different "slightly" pages.

Regards

  • This solution seems to work. However, if the problem would include all the pages for instance, you'd probably needed to use another approach as it's against DRY and often can be easily forgotten. Let's say you have 20-25 pages, it would increase the amount of code and decrease the readability. For me the solution provided by @Mike Brind seems to be perfect for such cases because you use one code for every pages. – Miraziz Oct 29 '21 at 23:18