I have the following menu in my masterpage:
This should NOT be done with Javascript! Deciding which page you are on is the job of the server side code, it's not a UI behaviour.
Have the menu as a user control and pass a value to it to indicate which part of the menu should be highlighted. I'm front end person, not a .NET developer but something like this:
<yourControl:menuControl runat="server" ID="menu" selectedPage="4" />
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");
I had the same problem. Using the following answers, I managed to put together the solution below.
https://stackoverflow.com/a/4733394/280972
https://stackoverflow.com/a/5650735/280972
In HtmlHelpers.cs:
public static class HtmlHelpers
{
public static HtmlString MenuLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName
)
{
var currentAction = htmlHelper.ViewContext.RouteData.GetRequiredString("action");
var currentController = htmlHelper.ViewContext.RouteData.GetRequiredString("controller");
var link = htmlHelper.ActionLink(linkText, actionName, controllerName);
var prefix = (actionName == currentAction && controllerName == currentController) ? String.Format("<li class=\"current\">") : String.Format("<li>");
const string suffix = "</li>";
return new HtmlString(prefix + link + suffix);
}
}
In the layout:
<ul class="nav nav-list">
<li class="nav-header">Statistics</li>
@Html.MenuLink("Number of logins", "Logins", "Statistics")
</ul>
Note that the MenuLink-helper creates both the li-tag and the a-tag.
Feedback on this solution is very welcome!
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 */ }
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.