Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC: Masterpage: How to set css class on active menu item

I have the following menu in my masterpage:

<ul id="menu" class="lavaLampBottomStyle">
    <li>
        <%= Html.ActionLink("Employees", "Index", "Employees")%></li>
    <li>
        <%= Html.ActionLink("Customer", "Details", "Account")%></li>
</ul>

I need a way to set the css class of the current active li to "current".

My first guess it to do this with the assistance of javascript.

I would include something like this in the masterpage:

  $("#menu li a").each(){
    if($(this).attr("href") == '<%= *GET CURRENT PAGE* %>'){
       $(this).parent("li").addClass("current");
    }
  }

Is this a good approach?

If it is, how can I get the current URL part like in the href?

If it isn't, what's your suggestion? :-)

FYI, the generated html I'm after:

<ul id="menu" class="lavaLampBottomStyle">
    <li>
        <a href="/KszEmployees/Index">Employees</a></li>
    <li>
        <a class="current" href="/">Customer</a></li>
</ul>
like image 872
Thomas Stock Avatar asked Jun 03 '09 14:06

Thomas Stock


3 Answers

If you want to do it all server-side, I've done this before. Create an action filter attribute:

public class PageOptionsAttribute : ActionFilterAttribute
{
    public string Title { get; set; }
    public string Section { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var controller = filterContext.Controller as ControllerBase;
        if (controller != null)
        {
            controller.SetPageSection(this.Section);
            controller.SetPageTitle(this.Title);
        }

        base.OnActionExecuting(filterContext);
    }
}

This calls two methods in my ControllerBase class that all my controllers inherit from:

public class ControllerBase : Controller
{
    public void SetPageSection(string section)
    {
                    // use the section defined or the controller name if none
        ViewData["PageSection"] = section != null ? 
                        section : this.RouteData.Values["controller"].ToString();
    }

    public void SetPageTitle(string title)
    {
        ViewData["PageTitle"] = title;
    }
}

Set the title and page section on you controller methods:

public class HomeController : ControllerBase
{
    [PageOptions(Title="Home Page", Section="Home")]
    public ActionResult Index()
    { }
 }

Then I call the ViewData value from my master page (this won't interfere with ViewData.Model):

<body class="<%=ViewData["PageSection"] %>">

Then to reference via CSS, instead of calling .current, give each nav item an ID and then use the body class in combination with that ID to determine the current page.

body.home #HomeNav { /* selected */  }
body.about #AboutNav { /* selected */  }
like image 98
John Sheehan Avatar answered Oct 15 '22 09:10

John Sheehan


Extract the current location from window.location. Then use a selector that specifies the value of the href attribute to choose just those elements that match (presumably only one).

 var currentLocation = window.location.href;
 // probably needs to be massaged to extract just the path so that it works in dev/prod

$("#menu li a[href$="+currentLocation+"]").addClass("current");
like image 9
tvanfosson Avatar answered Oct 15 '22 08:10

tvanfosson


That's probably the least intensive way of doing it. If you can count on the users to have javascript enabled, I see nothing wrong with this, and have done it myself on occasion.

Request.Url is the object you are interested in to get the current page on the server side. The suggestion to use window.location.href by tvanfosson isn't bad either if you want to keep it entirely clientside.

The advantage of using serverside, is that Request.Url has easily accessible parts of the url, such as Request.Url.Host, etc to help with your link-munging needs.

like image 6
Chad Ruppert Avatar answered Oct 15 '22 09:10

Chad Ruppert