1

Why Highlight Menu Item Doesn't Work in ASP.NET MVC?

I am testing the method for highlighting the selected menu item in a MVC program.

The test environment include: 1)ASP.NET core with MVC (using .NET 5) 2) Bootstrap V4.6

First, I created a small html file just for testing the "hightlight" mehtod with bootstrap with JQuery. (see html file content below). This html page works fine. It changes the text color to red for the active menu item.

Second, I created an ASP.NET core MVC project. In the _layout.cshtml file, I also use the bootstrap 4 and same JQuery script intended to highlight the active menu item. The top menu has same bootstrap classes for various navbar components. But, it does not work at all. (see content of _layout.cshtml at the very end of this post.) Why the "hightlight" doesn't work in MVC and how can I fix it.


HTML PAGE for testing

<!DOCTYPE html>
<html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8" />
        <meta name="viewport"
            content="width=device-width,
                    initial-scale=1,
                    shrink-to-fit=no" />

        <!-- Bootstrap CSS -->
        
        <!-- <link rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
            integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2"
            crossorigin="anonymous" />-->
            
        <!-- Bootstrap CSS file -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css">
        <!-- Bootstrap Font Icon CSS -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
        <!-- Font Awesome library -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
        
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"> </script>
        <script src="https://npmcdn.com/tether@1.2.4/dist/js/tether.min.js">    </script>
        
        <style>
            .font-bold {
                font-weight: bolder;
            }
        </style>

        <title>Active Link font color using jquery</title>
    </head>

    <body>
        <!-- <nav class="navbar navbar-expand-md navbar-light bg-light"> -->
        <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" >Testing</a>
            
            <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                <ul class="navbar-nav">
                    <li class="nav-item active">
                        <a class="nav-link font-bold" href="#">
                            Home
                        </a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link font-bold" href="#">
                            Product
                        </a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link font-bold" href="#">
                            Order
                        </a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link font-bold" href="#">
                            Shipping
                        </a>
                    </li>               
                    <li class="nav-item">
                        <a class="nav-link disabled" href="#">Disabled</a>
                    </li>
                </ul>
            </div>
            </div>
        </nav>
    
        <script type="text/javascript">
            $(document).ready(function () {
                $("ul.navbar-nav > li > a").click(
                function (e) {
                    $("ul.navbar-nav > li").removeClass(
                    "active");
                    $("ul.navbar-nav > li > a").css(
                    "color", "");

                    $(this).addClass("active");
                    $(this).css("color", "red");
                });
            });
        </script>
    </body>
</html>


_layout.cshtml in MVC

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - MVCWebUI</title>
    @*<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
        <link rel="stylesheet" href="~/css/site.css" />*@

    <!-- Bootstrap CSS file -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css">
    <!-- Bootstrap Font Icon CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
    <!-- Font Awesome library -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">

    <script src="~/lib/jquery/dist/jquery.min.js"></script>

    <script type="text/javascript">
        $(document).ready(function () {
            $("ul.navbar-nav > li > a").click(
                function (e) {
                    $("ul.navbar-nav > li").removeClass(
                        "active");
                    $("ul.navbar-nav > li > a").css(
                        "color", "");

                    $(this).addClass("active");
                    $(this).css("color", "red");
                });
        });
    </script>
</head>
<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-controller="Home" asp-action="Index">MVCWebUI</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-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 class="navbar-nav flex-grow-1">
                        <li class="nav-item" >
                            <a class="nav-link " asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link"  asp-area="" asp-controller="Home" asp-action="Privacy">Product</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link"  asp-area="" asp-controller="Home" asp-action="Privacy">Order</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2022 - MVCWebUI - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>

    <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: false)
</body>
</html>
bedrock
  • 263
  • 1
  • 2
  • 15

2 Answers2

0

$(this) in this context is referring to the clicked element which is the anchor element.

As such you are adding the active class to the anchor element and I think it needs to be on the list item element. I think you also need to prevent the default link click events,something like:

<script type="text/javascript">
    $(document).ready(function () {
        $("ul.navbar-nav > li > a").on("click", function (e) {
          
          e.preventDefault();
          
          var link  = $(this);
          
          $("ul.navbar-nav > li").removeClass("active");

          link.parent().addClass("active");

         // Redirect to URL
         window.location.href = link.attr("href");

        });
    });
</script>

Put an example here for you: https://codepen.io/treefishuk/pen/zYPKapP

Please note that on redirecting to the new page the active state will be lost becuase HTTP is a stateless protocol. To set the active state after the redirect you have a few options, here are three of them:

Option 1 : Use Javascript to match current URL against links

On the page load you can get the current url from window.location then loop through the links in the nav and add the active class if they match.

Example : https://webdesignerhut.com/active-class-navigation-menu/

Option 2 : Use Javascript and some sort of persistance mechanism

You could save the id of the clicked link in a cookie or local storage and on page load add the active state to the matching element. The example below is for sidebar state but the principle is the same.

Example : Bootstrap 4 Collapse active state in localstorage

Option 3 : Set it server side using a custom tag helper

This is the most elegant approach in my opinion.

Examples :

Jon Ryan
  • 1,497
  • 1
  • 13
  • 29
  • I made the change as you suggested. It is still unable to highlight the menu item. When I watched it closely and saw that the menu item text seemed to turn to red and immediately changed back to black. I tested with and without added ".parent()". It didn't make any difference. Any suggestions to make it work? – bedrock Feb 04 '22 at 14:56
  • Updated the JS and created a codepen example for you. Main thing is to add e.preventDefault(); Also I moved the CSS in the codepen into the CSS file. – Jon Ryan Feb 04 '22 at 19:07
  • Thank you so much for the code sample. Now the highlight is working now. – bedrock Feb 04 '22 at 21:50
  • @John Ryan: Sorry to bother you again with a related question. Each menu item uses an anchor such as: Product – bedrock Feb 06 '22 at 19:52
  • @John Ryan: Sorry to bother you again with a related question. Each menu item uses an anchor such as: Product. Once I have JQuery script in place to highlight active menu item, it will not go to the cshtml page specified by "asp-controller" & "asp-action". Does my JQuery script blocked the event generated by clicking the menu item? – bedrock Feb 06 '22 at 19:55
  • Have updated the answer for you. – Jon Ryan Feb 06 '22 at 21:26
  • Using "window.location.href = link.attr("href");" does fix the problem of unable to go to target page. But the menu item highlight stopped working. The active item changed to red and immediately reversed to black. Any suggestions? – bedrock Feb 07 '22 at 15:51
  • Is it just a case of the class is lost once the new page has loaded? – Jon Ryan Feb 07 '22 at 21:32
  • Correct. I did following tests to verify : (1) add a menu item with: Test. The highlight works on this menu item. (2) add another menu item: Product_href. This one will not keep the highlight, and works with loading a page. – bedrock Feb 08 '22 at 14:06
  • So just to confirm, are you querying why the active class is lost after redirecting to a new page? – Jon Ryan Feb 08 '22 at 15:31
  • The answer to you question is "yes", In the test program, all .cshtml pages are using the same main menu (_layout.cshtml). Why loading a new page will lost the track of which is the active menu item? – bedrock Feb 09 '22 at 21:11
  • I have updated my answer for you with a few options. – Jon Ryan Feb 10 '22 at 09:46
  • I think the option 2 will be the solution in my program. Since I have a couple of issues need to be fixed recently, I will come back to work on this one a bit latter. Thanks for your help. – bedrock Feb 12 '22 at 22:31
  • Happy to help. If you feel my answer has provided you with enough direction please consider marking it as accepted. – Jon Ryan Feb 15 '22 at 23:05
0

You can use Bootstrap's nav-pills to change the color of active menu item.

<ul class=navbar-nav nav nav-pills" role="tablist" id="manu-bar">
  <li class="nav-item">
    <a class="nav-link @(ViewData["Title"] == "Page1" ? "active" : "")" id="menu1" asp-area="" ....>Page1</a>
  </li>
    <a class="nav-link @(ViewData["Title"] == "Page2" ? "active" : "")" id="menu1" asp-area="" ....>Page2</a>    
  </li>
</ul>
Shangwu
  • 1,440
  • 12
  • 12