Nested lists - selecting path using Javascript - javascript

I have Javascript function that is adding css class "current" to selected link <a>. This function looks like that:
function markActiveLink() {
var path = location.pathname;
var links = null;
if (path) {
links = $("a[href^='" + path + "']");
} else {
links = $("a[href='/']");
}
links.parents("li").each(function () {
$(this).addClass("current");
});
}
And I have nested list wchich looks similar to that list:
<ul class="menu">
<li>menu item
<ul>
<li>menu item </li>
<li>menu item</li>
<li>menu item</li>
</ul>
</li>
<li>menu item
<ul>
<li>menu item </li>
<li>menu item</li>
<li>menu item</li>
</ul>
</li>
</ul>
I want to add class "current" (using Javascript function) not to link <a> like it is now working but to <li> element and it's parent <li>. So it would look like that:
<ul class="menu">
<li class="current">menu item
<ul>
<li>menu item </li>
<li class="current">menu item</li>
<li>menu item</li>
</ul>
</li>
<li>menu item
<ul>
<li>menu item </li>
<li>menu item</li>
<li>menu item</li>
</ul>
</li>
</ul>
Question: How can I modify my function markActiveLink() so that it would add class "current" both to <li> element and it's parent <li> element?
Any help here much appreciated!

Try:
links.parent().addClass('current')
.closest('li').addClass('current');
jQuery Docs

Seems to me like it's working. I only changed the hrefs in your links so they wouldn't be duplicates and added some CSS to show the highlighted elements in this JSFiddle

Use the .parent() api to do it
Docs
EDIT
Something like this:
$(this).parent().parent().addClass("current")

Related

Trying to add buttons to dynamic sub-menu with insertBefore results in "Failed to execute 'insertBefore' on 'Node': parameter 2 is not of type 'Node'"

I'm trying to dynamically add buttons to toggle the visibility of sub-menus in a navigation system. The idea being if a user created a sub-menu in the CMS, JavaScript would dynamically add a button above this at the same level as the sub-menu's parent element to allow content to be toggled.
So static HTML like this
<nav>
<ul class="primary-nav">
<li class="nav-item">nav item
<!-- sub menu-->
<ul class="child-nav">
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
</ul>
</li>
<li class="nav-item">nav item
<!-- sub menu-->
<ul class="child-nav">
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
</ul>
</li>
</ul>
</nav>
Would be dynamically changed to something like the mark-up below, with the button to toggle visibility of the sub-menu appearing above the sub-menu's element
<nav>
<ul class="primary-nav">
<li class="nav-item">nav item
<button>Show show menu</button>
<!-- sub menu -->
<ul class="child-nav">
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
</ul>
</li>
<li class="nav-item">nav item
<button>Show sub menu</button>
<!-- sub menu -->
<ul class="child-nav">
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
</ul>
</li>
</ul>
</nav>`
However when I use querySelectorAll and insertBefore I get the following error message
Uncaught TypeError: Failed to execute 'insertBefore' on 'Node': parameter 2 is not of type 'Node'.
The code below results in the error:
const navItem = document.querySelectorAll(".nav-item");
const subNav = document.querySelectorAll(".child-nav");
const button = document.createElement("button");
button.textContent = "sub-menu";
// add button if sub-menu is present
for (i = 0; i < navItem.length; i++ ) {
if (navItem[i].hasChildNodes() === true) {
navItem[i].insertBefore(button, subNav);
}
}
However if I use querySelector(".child-nav") as below and amend navItem[i] to navItem[0] what I hoped to happen does, that is the button is added, obviously though this is only added to the first element and I need it to be added above all the the sub-menu elements if present.
const navItem = document.querySelectorAll(".nav-item");
const subNav = document.querySelector(".child-nav");
// add button if sub-menu is present
for (i = 0; i < navItem.length; i++ ) {
if (navItem[i].hasChildNodes() === true) {
navItem[0].insertBefore(button, subNav);
}
}
Can anyone help? I'm completely stumped.
I've tried swapping querySelectorAll(".child-nav") for getElementsByClassName("child-nav") thus switching from a nodeList to a HTMLCollection but I still get the error message about parameter 2 not being a node, though everything works fine if I just use querySelector.
You could rewrite your code like this (the Javascript part).
const navItem = document.querySelectorAll(".nav-item");
//const subNav = document.querySelector(".child-nav"); <== not here
// add button if sub-menu is present
for (i = 0; i < navItem.length; i++ ) {
if (navItem[i].hasChildNodes() === true) {
//navItem[i].insertBefore(button, subNav); <== not here
var button = document.createElement("button");
button.textContent = "sub-menu";
var subNav = navItem[i].querySelector(".child-nav");
navItem[i].insertBefore(button,subNav);
}
}
<nav>
<ul class="primary-nav">
<li class="nav-item">nav item
<!-- sub menu-->
<ul class="child-nav">
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
</ul>
</li>
<li class="nav-item">nav item
<!-- sub menu-->
<ul class="child-nav">
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
<li class="child-nav-item">child nav item</li>
</ul>
</li>
</ul>
</nav>
You'll notice that Ive removed the button and subnav definitions to be inside the for loop. The reason for this is:
each of the buttons you want to add to your navigation are separate DOM elements, and should be unique, even if their appearance and background code is the same. Think of it like this - let's say that your button is a person. With the original code, it would be as if you were trying to tell that person to be at multiple places at the same time
things are similar for the subnav - we are checking the children of the current navitem, and attaching the button before any of them
Try using AppendChild or Prepend.

Trying to show sub-menu on click and hide on click if already shown. Also remove show class on if clicked on a new item

I have a nav menu with list items and sub-menus under each. I am trying to make the submenu visible on click of parent and hide when clicked again or on another item in main menu. I have everything working except for hiding the submenu when clicking the parent a second time.
My question would be what am I doing wrong for the second click on parent not working to hide the submenu. I have tried replacing my if statement with a simple toggle class which gives the same result.
UPDATE: I now have the updated code below working to hide when clicking the parent item again. However I have lost the submenu hiding when clicking a different menu item.
- - UPDATED - -
let dropDowns = Array.from(document.querySelectorAll('#menu-menu li.menu-item-has-children'));
const handleClick = (e) => {
if (e.currentTarget.querySelector('ul.sub-menu').classList.contains('show')) {
e.currentTarget.querySelector('ul.sub-menu').classList.remove('show');
e.currentTarget.classList.remove('nav-active');
}else {
e.currentTarget.querySelector('ul.sub-menu').classList.add('show');
e.currentTarget.classList.add('nav-active');
}
}
dropDowns.forEach(node => {
node.addEventListener('click', handleClick)
});
- - ORIGINAL - -
let dropDowns = Array.from(document.querySelectorAll('#menu-menu li.menu-item-has-children'));
const handleClick = (e) => {
e.preventDefault();
e.stopPropagation();
dropDowns.forEach(node => {
node.querySelector('ul.sub-menu').classList.remove('show');
});
if (e.currentTarget.querySelector('ul.sub-menu').classList.contains('show')) {
console.log('click-with-show')
e.currentTarget.querySelector('ul.sub-menu').classList.remove('show');
}else {
console.log('click-no-show')
e.currentTarget.querySelector('ul.sub-menu').classList.add('show');
}
}
dropDowns.forEach(node => {
node.addEventListener('click', handleClick)
});
<ul id="menu-menu">
<li class="menu-item-has-children">Parent Item
<ul class="sub-menu">
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
</ul>
</li>
<li class="menu-item-has-children">Parent Item
<ul class="sub-menu">
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
</ul>
</li>
<li class="menu-item-has-children">Parent Item
<ul class="sub-menu">
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
</ul>
</li>
<li class="menu-item-has-children">Parent Item
<ul class="sub-menu">
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
<li>Sub Menu Item</li>
</ul>
</li>
</ul>

How to add link in tabs in Bootstrap?

I want to create a link in a Bootstrap tab. I have 3 tabs and I want one of them where I will click and a new page will be opened. Please let me know how I can do it.
Here is my HTML code.
<li><a data-toggle="tab" href="google.com" id="btnRent" style="background: #f4762a;">Rent/Lease/PG/Shop</a></li>
And I am using this jQuery...
<script type="text/javascript">
$(document).ready(function()
{
$(#btnRent).click(function()
{
var go_to_url = "www.google.com";
document.location.href = go_to_url;
});
});
</script>
But it's not working. I want to open a new page when someone clicks on the "Rent/Lease/PG/Shop" tab.
you can try inside your click function:
window.location = url;
or window.open(url);
it should do the trick..
you don't need javascript for that, just use the target parameter inside your 'a' tag like this:
<li><a data-toggle="tab" href="https://google.com" target="_blank" id="btnRent" style="background: #f4762a;">Rent/Lease/PG/Shop</a></li>
The best way to do this is to create another "" element inside your tab ""
<ul class="nav nav-tabs">
<li class="active">Home<a href="http://www.google.com>Click here</a></li>
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
</ul>
If you want a direct click just do this
<ul class="nav nav-tabs">
<li><a href="http://www.google.com>Click here</a></li>
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
</ul>

Exacly search in javascript

How I can search text in javascript only if is match perfectly. For example I have this structure:
<ul class="item-options">
<li>First Item</li>
<li>item Name</li>
<li>product 1</li>
<li>Item Description</li>
<li>product description</li>
<li>Additional Info</li>
<li>Red</li>
<li>Color</li>
<li>Red</li>
</ul>
<ul class="item-options">
<li>Second Item</li>
<li>item Name Blue</li>
<li>product 2</li>
<li>Item Description</li>
<li>product2 description</li>
<li>Additional Info</li>
<li>-----</li>
<li>Color</li>
<li>Blue</li>
</ul>
<ul class="item-options">
<li>Third Item</li>
<li>item Name Purple</li>
<li>product 3</li>
<li>Item Description</li>
<li>-------</li>
<li>Additional Info</li>
<li>-----</li>
<li>Color</li>
<li>------</li>
</ul>
For example I want to check if the text item Name exist in the page then if the item Name text exist remove associated children. So for the first item Name I need to remove only the color:
<li>Color</li>
<li>Red</li>
Then if item Name Blue text exist I need to remove associated children
<li>Additional Info</li>
<li>-----</li>
and if the third item Name Purple text exist then remove associated children:
<li>Item Description</li>
<li>-------</li>
<li>Additional Info</li>
<li>-----</li>
<li>Color</li>
<li>------</li>
I create this script but for example the name Color is deleted everywhere. Even if in the item Name or in item Name Purple must remain there. I think the problem is because the first product have the name item Name and in the second product the name is start with item Name Purple.
<script type="text/javascript">
jQuery(document).ready(function() {
if (jQuery('.items-class li:contains("item Name")').length > 0)
{
var parent = jQuery('.items-class');
var children = jQuery('.items-class').find("li");
jQuery(children).each(function(i,e){
if(~jQuery(e).text().indexOf("item Name Blue")){
jQuery(parent).find('li:contains("Additional Info")').html('');
jQuery(parent).find('li:contains("-----")').html('');
}
if(~jQuery(e).text().indexOf("item Name Purple")){
jQuery(parent).find('li:contains("Item Description")').html('');
jQuery(parent).find('li:contains("-------")').html('');
jQuery(parent).find('li:contains("Additional Info")').html('');
jQuery(parent).find('li:contains("-----")').html('');
jQuery(parent).find('li:contains("Color")').html('');
jQuery(parent).find('li:contains("-----")').html('');
}
else if(~jQuery(e).text().indexOf("item Name")){
jQuery(parent).find('li:contains("Color")').html('');
jQuery(parent).find('li:contains("Red")').html('');
}
});
}
});
</script>
I'm not exactly sure what you are asking, but I hope this helps.
function removeAdditionalInfo(searchCriteria){
var interestingTagSelector = "ul.item-options > li";
var additionInfoHeader = "Additional Info";
var getFilter = function(criteria){
return function(index, item){ return (item.innerHTML === criteria); };
};
// find the potentially interesting li tags
$(interestingTagSelector)
// filter out those that are not in fact interesting
.filter(getFilter(searchCriteria))
// get siblings following an interesting li tag
.nextAll()
// ignore those not starting the additional info section
.filter(getFilter(additionInfoHeader))
// get siblings following the AddInfo header
.nextAll()
// removed them
.remove();
}
removeAdditionalInfo("item Name");
the removeAdditionalInfo() function below and:
removeAdditionalInfo("item Name");
removeAdditionalInfo("item Name Blue");
removeAdditionalInfo("item Name Purple");
will remove the additional info items.

Trying to parse tag element

I am trying to parse a web page with JavaScript targeting list <li> without class.
<ul id="cartItems">
<li class="heading">
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
Use querySelectorAll to get all li elements without a class.
var liWithoutClass = document.querySelectorAll('#cartItems > li:not([class])');
console.log(liWithoutClass);
document.write(liWithoutClass[0].textContent); // Output the first li
document.write('<br />' + liWithoutClass[3].textContent); // Output the last li
<ul id="cartItems">
<li class="heading">Heading</li>
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
<li>Element 4</li>
</ul>
var elems = document.querySelector(".heading");
console.log(elems);
//Now we strip the class out like this:
elems.setAttribute(class, "none");
//Now we display the updated values below
console.log(elems);
// This won't work in Stack's editor, so you'll have to validate it against the page you want.
//Now, just grab the entire element by id from the page as below:
var updatedContent = document.getElementById("cartItems");
console.log(updatedContent);
//Again, stack's editor is limited, but overall this should work fine on your page.
<ul id="cartItems">
<li class="heading">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
</ul>

Categories