Select current link in menu - javascript

I'm using MentisMenu for my site, and I'm trying to get the active menu selection to be set to active. I can get the Menu and submenus to activate and expand, but the actual link selected will not be marked active. Anyone?
const currentLinkParents = currentLink => {
let target = currentLink
let parent = []
while (target) {
// Get only nav-link
if (target.classList.contains('nav-item')) parent.unshift(target.querySelector('.nav-link'))
// Stop on treeview
if (target.classList.contains('treeview')) break
target = target.parentNode
}
return parent
}
const updateMenu = currentLink => {
document.querySelectorAll('#menu .active').forEach(i => i.classList.remove('active', 'show'))
for (const i of currentLinkParents(currentLink)) {
i.classList.add('active')
i.classList.contains('treeview-toggle') && i.classList.add('show')
}
}
updateMenu(document.querySelector(`#menu a[href="${window.location.pathname.split('/').pop()}"]`))
Sample HTML here:
<li class="nav-item">
<a class="nav-link has-icon treeview-toggle" href="#"><i class="material-icons">construction</i>Engineering</a>
<ul class="nav">
<li>Active Renovations</li>
<li>Maintenance Requests</li>
<li>Projects</li>
<li>Recurring Events</li>
<li class="nav-item">
Inventories
<ul class="nav">
<li>Stock Inventory</li>
<li>Master Keys</li>
</ul>
</li>
</ul>
</li>
In this example, if I select "Master Keys" then the menus Engineering and Inventories both expand and are set to active, but the Master Keys selection is not marked active.
Hope someone can help!

Turns out the following line after the call to updateMenu() fixes it:
document.querySelector(#menu a[href="${window.location.pathname.split('/').pop()}"]).classList.add('active');

Related

Close dropdown upon clicking another

I a working on a to make a reponsive dropdown navigation bar with vanilla JavaScript. In mobile view I want that upon click one dropdown the another should close. JavaScript here:
dropbtns.forEach(link => {
link.addEventListener("click", function(e) {
e.currentTarget.nextElementSibling.classList.toggle("showdd");
});
});
and show dropdown:
.showdd {
height: auto;
}
html code:
<div class="nav-container">
<div class="brand">
Logo
</div>
<nav>
<div class="nav-mobile"><a id="nav-toggle" href="#!"><span></span></a></div>
<ul id="nav-list">
<li>
Home
</li>
<li>
About
</li>
<li class="dropdown">
</i>
<ul class="nav-dropdown">
<li>
Web Design
</li>
<li>
Web Development
</li>
<li>
Graphic Design
</li>
</ul>
</li>
<li>
Pricing
</li>
<li class="dropdown">
</i>
<ul class="nav-dropdown">
<li>
Web Design
</li>
<li>
Web Development
</li>
<li>
Graphic Design
</li>
</ul>
</li>
<li>
Contact
</li>
</ul>
</nav>
</div>
full code can be find here.
So, if you want to collapse all other .nav-dropdown when one is being clicked on, you simply will need to:
Store the reference of the .nav-dropdown of the current element (for comparison later)
Toggle its class (as you're doing already)
Go through all other .nav-dropdown in your DOM tree and iterate through them. If they do not match the current reference, then you know the dropdown belongs to another link and you can remove the class
With that in mind we arrive at the code below:
dropbtns.forEach(link => {
link.addEventListener('click', e => {
const ownDropdown = e.currentTarget.nextElementSibling;
ownDropdown.classList.toggle('showdd');
document.querySelectorAll('.dropbtn + .nav-dropdown').forEach(el => {
if (el !== ownDropdown)
el.classList.remove('showdd');
});
});
});
It works on your Codepen after I edit the following line.
links.forEach(link => {
link.addEventListener("click", function(e) {
links.forEach(link => {
link.nextElementSibling.classList.remove("showdd"); // Here
});
e.currentTarget.nextElementSibling.classList.toggle("show");
});
});
By the way, what is "showdd"?

Set parent list item of dropdown link to active with jQuery / JavaScript (ASP.NET)

This is my menu
<ul class="sidebar-menu" id="navigation-menu">
<li class="active"><a class="nav-link" href="/"><i class="fas fa-home"></i><span>Home Page</span></a></li>
<li class="nav-item dropdown">
<i class="fas fa-cog"></i><span>Configuration</span>
<ul class="dropdown-menu">
<li><a class="nav-link" href="/Configuration/AccountCodes">Account Codes</a></li>
<li><a class="nav-link" href="/Configuration/Branches">Branches</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<i class="fas fa-person-booth"></i><span>Directory</span>
<ul class="dropdown-menu">
<li><a class="nav-link" href="/Directory/Users?Role=Administrator">Administrators</a></li>
<li><a class="nav-link" href="/Directory/Users?Role=Manager">Managers</a></li>
<li><a class="nav-link" href="/Directory/Users?Role=Agent">Agents</a></li>
</ul>
</li>
</ul>
This is my Jquery, when I select AccountCodes which is under the Configuration dropdown, it should only set the parent list item active. However, the Directory parent is set to active as well. I'm not sure how to select it directly.
Also, due to the structure of my URL, I am unable to continue to use endWith. Is there an alternate method of setting an active class based on the url? Currently, if I select AccountCodes or Branches, it correctly sets the Configuration dropdown item as active. However, If I select agent, nothing is selected at all due to its url ending with a ?Agent instead of Users
<script>
$(document).ready(function () {
var current = location.pathname;
console.log(current);
$("#navigation-menu a").each(function () {
var $this = $(this);
console.log($this.attr('href'));
if ($this.attr('href').endsWith(current)) {
console.log($this.parent());
$this.parent().addClass('active');
console.log("matched");
}
});
if ($(".dropdown-menu li").hasClass("active")) {
console.log("Yes");
var $this = $(this);
$(".dropdown-menu li").prev().parent().parent().addClass('active');
console.log($(".dropdown-menu li").closest(".nav-item dropdown"));
}
});
</script>
There would be two ways to mark only the parent link as active.
1. With jQuery
The advantage of this is that it will be compatible with any back end platform.
<script>
$(document).ready(function () {
var current = location.pathname;
console.log(current);
$("#navigation-menu a").each(function () {
var $this = $(this);
var link = $this.attr('href');
console.log(link);
// Remove the query string parameters by finding their starting position
var indexOfQueryString = link.lastIndexOf("?");
link = link.substring(0, indexOfQueryString);
if (link.endsWith(current)) {
console.log("matched");
if ($this.parent().parent().hasClass('.dropdown-menu')) {
var parentLink = $this.closest('.dropdown');
console.log("Setting parent link as active:", parentLink);
// Find the closest parent link and add active class
parentLink.addClass('active');
} else {
console.log("Setting link as active: ", $this.parent());
$this.parent().addClass('active');
}
}
});
});
</script>
In this case, as you have mentioned you encountered problems with ?Agent being present in link – this is what we refer to as query string parameters, which we can safely remove by searching for the unencoded question mark. This is already considered in the solution.
2. With ASP.NET MVC
Doing this in the back end would be more robust and should JavaScript be disabled by the user, this functionality will still work in your website. The disadvantage though, as you might have guessed, is that it is platform-dependent.
There are many ways of going about this. For your specific use-case, as your dropdown links are related to the parent by area, I have adapted another SO answer to cover your specific use-case:
public static string IsSelected(this HtmlHelper html, string area = "", string cssClass = "active")
{
ViewContext viewContext = html.ViewContext;
RouteValueDictionary routeValues = viewContext.RouteData.Values;
string currentArea = routeValues["area"] as string;
return area.Equals(currentArea) ? cssClass : String.Empty;
}
Then use it this way:
<ul class="sidebar-menu" id="navigation-menu">
<li><a asp-area="" asp-controller="Home" asp-action="Index" class="nav-link"><i class="fas fa-home"></i><span>Home
Page</span></a></li>
<li class="nav-item dropdown #Html.IsSelected(area: "Configuration")">
<i class="fas fa-cog"></i><span>Configuration</span>
<ul class="dropdown-menu">
<li><a asp-area="Configuration" asp-controller="AccountCodes" asp-action="Index" class="nav-link">Account
Codes</a></li>
<li><a asp-area="Configuration" asp-controller="Branches" asp-action="Index" class="nav-link">Branches</a>
</li>
</ul>
</li>
<li class="nav-item dropdown #Html.IsSelected(area: "Directory")">
<i class="fas fa-person-booth"></i><span>Directory</span>
<ul class="dropdown-menu">
<li><a asp-area="Directory" asp-controller="Users" asp-action="Index" asp-route-Role="Administrator"
class="nav-link">Administrators</a></li>
<li><a asp-area="Directory" asp-controller="Users" asp-action="Index" asp-route-Role="Manager"
class="nav-link">Managers</a></li>
<li><a asp-area="Directory" asp-controller="Users" asp-action="Index" asp-route-Role="Agent"
class="nav-link">Agents</a></li>
</ul>
</li>
</ul>
Would this benefit you, please make sure to upvote the other linked answer as well.

Active one main menu at a time hide others

please take a look at the image, here I am tried to do
If I click on "Fan Panels" The other two menus will be visible. Which is currently working fine. and here three menus will be visible.
If I click on "Entertain Art" menu the "Score Art" and "Fan Panels" need to be hidden, but the "Entertain Art" still need to show.
If I click on "Score Art" the "Fan Panels" and "Entertain Art" menu need to hidden, but the "Score Art" still need to show.
If you think about the code here it is :
{{#each categories}}
<li class="nav-list__item">
<a class="nav-list__item-link" href="{{url}}" title="{{name}}">{{name}}</a>
</li>
{/each}}
The website is under preview mode
Tts Big-commerce so menu displaying frontend with the dynamic syntax I think we can do it with javascript and jquery to find text and hide them what you suggest?
FOR EXAMPLE CODE:
<ul id="primeNav" class="nav-list nav-list--prime">
<li class="nav-list__item">
<a class="nav-list__item-link" href="#" title="Fan Panels">Fan Panels</a>
<li class="nav-list__item">
<a class="nav-list__item-link" href="#" title="Entertain Art">Entertain Art</a>
<li class="nav-list__item">
<a class="nav-list__item-link" href="#" title="Score Art">Score Art</a>
</ul>
Thanks in Advance
You can try this code alternatively -
Edit 3 -
Here's the newly updated code
/* Conditional Page Options */
var url = window.location.href;
var url = url.replace(/\//g, '');
var sub_url = location.protocol + location.host;
var page = url.replace(sub_url, '');
/* conditional click actions */
if (page == "entertain-art") {
$("#primeNav .nav-list__item").hide();
$("#primeNav .nav-list__item:nth-child(2)").show(); // revised
} else if (page == "score-art") {
$("#primeNav .nav-list__item").hide();
$("#primeNav .nav-list__item:nth-child(3)").show(); // revised
} else {
$("#primeNav .nav-list__item-link").on('click', function() {
var index = $(".nav-list__item-link").index(this);
if (index == 0) {
$("#primeNav .nav-list__item").show();
} else {
$("#primeNav .nav-list__item").hide();
$(this).parent().show();
}
});
}
$("#primeNav .nav-list__item:nth-child(4)").show(); // revised
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="primeNav" class="nav-list nav-list--prime">
<li class="nav-list__item">
<a class="nav-list__item-link" href="#" title="Fan Panels">Fan Panels</a>
</li>
<li class="nav-list__item">
<a class="nav-list__item-link" href="#" title="Entertain Art">Entertain Art</a>
</li>
<li class="nav-list__item">
<a class="nav-list__item-link" href="#" title="Score Art">Score Art</a>
</li>
</ul>
try it.
**UPDATE* : without adding new class name
//Without any new class name
$("#primeNav li").click(function(){
if($(this).text().trim()!="Fan Panels") //For Point 1, if click on FanPanel then show all
{
$("#primeNav li").hide(); // hide all item
$(this).show(); // Show item which is clicked
}
})
//Added **menu** as classname to all anchor tag, also added another unique class name to handle individual.
//Older try
$(".menu2").click(function(){
console.log($(this).text());
if($(this).hasClass("menuFan")==false) //For Point 1, if click on FanPanel then show all
{
$(".menu").hide(); // hide all item
$(this).show(); // Show item which is clicked
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="primeNav" class="nav-list nav-list--prime">
<li class="nav-list__item">
<a class="nav-list__item-link menu menuFan" href="#" title="Fan Panels">Fan Panels</a>
<li class="nav-list__item">
<a class="nav-list__item-link menu menuEnt" href="#" title="Entertain Art">Entertain Art</a>
<li class="nav-list__item">
<a class="nav-list__item-link menu menuScore" href="#" title="Score Art">Score Art</a>
</ul>

Make all direct parent list entries selected in mmenu

I'm working on a project where I'm about to use the jQuery plugin mmenu (http://mmenu.frebsite.nl/).
I already had to do some customisations to fit my needs but I don't know what to do with my current issue. In mmenu, when i click on an list entry I will be navigated to the given href and the clicked item becomes active by mmenus css class ".mm-selected". So far so good.
Now I want to additionally mark the parent list item (and thats parent, and so on until menu root) as selected. This should be so when the user goes one level up in the menu he should be able to see in which category he currently is.
Below is an example of the menus html structure after mmenu was applied. This shows the code for a menu with 4 main pages (index, page1, page2 and page3) and 3 subpages (2.1, 2.2, 2.3).
<nav id="nav" class="mm-menu mm-horizontal mm-offcanvas mm-hasheader mm-current mm-opened">
<ul class="mm-list mm-panel mm-opened mm-current" id="mm-0">
<li class="mm-selected">
Index
</li>
<li>
Page 1
</li>
<li>
<a class="mm-subopen mm-fullsubopen" href="#mm-1"></a>
<span>Page 2</span>
</li>
<li>
Page 3
</li>
</ul>
<ul class="mm-list mm-panel mm-highest" id="mm-1">
<li class="mm-subtitle">
<a class="mm-subclose" href="#mm-0">Page 2</a>
</li>
<li>
Page 2.1
</li>
<li>
Page 2.2
</li>
<li>
Page 2-3
</li>
</ul>
</nav>
It would be great if you had some idea where and how I could achive such functionality.
So, for the moment I did some jQuery hacking. This seems to work for my case mentioned above. It should also work for deeper menus as it's using recursion. If there's a better way to achieve this, please let me know.
var nav = $("#nav");
nav.find("li > a:not(.mm-subopen)").click(function () {
nav.find("li.active").removeClass("active");
selectParentEntry($(this));
});
var selectParentEntry = function (a) {
var li = a.parent(),
ul = li.parent(),
back = ul.find("li > a.mm-subclose").first(),
cID = "#" + ul.attr("id"),
pID = back.length ? back.attr("href") : null;
li.addClass("active");
if (pID != null) {
var subOpen = nav.find("ul" + pID + " > li > a.mm-subopen").filter(function () {
var self = $(this);
if (self.attr("href") === cID) return self;
}).first();
if (subOpen) selectParentEntry(subOpen);
}
};

Vertical CSS slide menu pushing the site up on click

The submenu on each menu item slides underneath the main menu item instead of sliding out whenever I click on a menu item, which is what it's supposed to do. Problem is the site itself automatically scrolls up. Its as if the main menu items have a link to them that is anchored to the top of the site. I click on them, the submenu slide out, but the site itself scrolls up everytime.
How to make the code cross-browser compatible?
The javascript code:
<script type="text/javascript">
<!--//--><![CDATA[//><!--
startList = function() {
if (document.getElementById) {
navRoot = document.getElementById("nav");
for (i=0; i<navRoot.childNodes.length; i++) {
node = navRoot.childNodes[i];
if (node.nodeName=="LI") {
node.onclick=function() {
this.className = (this.className == "on") ? "off" : "on";
}
}
}
}
}
window.onload=startList;
//--><!]]>
</script>
The html code:
<ul id="nav">
<li>Home </li>
<li>About >
<ul>
<li>History </li>
<li>Team </li>
<li>Offices </li>
</ul>
</li>
<li>Services >
<ul>
<li>Web Design </li>
<li>Internet Marketing </li>
<li>Hosting </li>
<li>Domain Names </li>
<li>Broadband </li>
</ul>
</li>
<li>Contact Us >
<ul>
<li>United Kingdom</li>
<li>France</li>
<li>USA</li>
<li>Australia</li>
</ul>
</li>
</ul>
Based off of this menu: http://www.pmob.co.uk/temp/drop-down-expand.htm#
The reason is because you have "#" in your hrefs...this is telling the browser to return to the top. You need to return false on your onclick so that the default behavior (navigating to the href) doesn't happen on the items that are not truly "links".
You can always add e.preventDefault() to the event listener to remove all hyperlink-effects after clicked.
Using preventDefault is usually more recommended.
http://jsfiddle.net/Z8Uvj/
$("a").click(function(e){
//your stuff
e.preventDefault();
});

Categories