Implementing wayfinding on dynamic menu - javascript

I need help with homework. I need to create a Wayfinder by highlighting an active menu item in a dynamically generated navigation. My code does not respond when a menu item is changed on a button click. Here are my rendered HTML and JavaScript codes.
var urlLink = "http://localhost/acme/products/?action=category&categoryName=Rocket";
var path = urlLink.split("=").pop();
console.log(path);
liContainer = document.getElementById("navMenu");
const navAnchor = liContainer.getElementsByClassName('mainMenu');
/*
navAnchor.forEach(anchor => {
anchor.addEventListener('click', addActive);
});
*/
for (var i = 0; i < navAnchor.length; i++) {
navAnchor[i].addEventListener("click", addActive);
}
function addActive(e) {
var liAnchor = liContainer.target.tagName("a").getAttribute("href");
if (liAnchor.split("=").pop() === path) {
const current = document.querySelector('li.active');
current.className = current.className.replace(" active", "");
e.target.className += " active";
}
}
.active {
background-color: red;
color: white;
padding: 10px
}
<ul id="navMenu">
<li class="mainMenu active">Home</li>
<li class="mainMenu">Cannon</li>
<li class="mainMenu">Explosive</li>
<li class="mainMenu">Misc</li>
<li class="mainMenu">Rocket</li>
<li class="mainMenu">Trap</li>
</ul>
I want the active menu to be highlighted.

I resolved my own issue using the following code:
var path = window.location.href.split("=").pop();
var liContainer = document.getElementById("navMenu");
const navAnchor = liContainer.getElementsByClassName('mainMenu');
for (var i = 0; i < navAnchor.length; i++) {
var liAnchor = navAnchor[i].getElementsByTagName("a");
for (var j = 0; j < liAnchor.length; j++) {
linkPath = liAnchor[j].getAttribute("href").split("=").pop();
if (linkPath === path) {
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
navAnchor[i].className += " active";
}
}
}

Related

Generate dinamically a <ul> <li> listing from a input array in jQuery

My web app has the following tree view in order to show some files and files.
ul {
list-style-type: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" />
<ul id="myTreeSelector">
<li>
<span class="caret folder-selector"><i class="fa fa-folder"></i> first_folder</span>
<ul class="nested">
<li class="file"><i class="fa fa-file"></i> app1.dat</li>
<li class="file"><i class="fa fa-file"></i> app2.dat</li>
<li>
<span class="caret folder-selector"><i class="fa fa-folder"></i> second_folder</span>
<ul class="nested">
<li class="file"><i class="fa fa-file"></i> ret.dat</li>
<li><span class="caret folder-selector"><i class="fa fa-folder"></i> third_folder</span></li>
</ul>
</li>
</ul>
</li>
</ul>
This is just an static example, I want to create this folder dinamically from an input array using jQuery.
Following the example above (and adding one last element as new empty directory), my input array is as follows:
arrayOfFiles= ["/first_folder/app1.dat","/first_folder/app2.dat","/first_folder/second_folder/ret.dat", "/first_folder/second_folder/third_folder"]
How can I, using jQuery, iterate that array and create the proper folder structure? This is my try so far, I know I can use appendTo to add content to the HTML, but it's so hard for me to manage the file-folder stuff.
general_ul = $("#myTreeSelector")
general_li = $('</li>').appendTo(general_ul);
arrayOfPaths.forEach( function(pathOrFile) {
var li = $('<li/>')
.html('<span class="caret folder-selector"><i class="fa fa-folder"></i> first_folder</span>')
.appendTo(general_li);
});
Thanks in advance.
I reworked my project so that it is as close as possible to what you need.
This is not jQuery but I hope this helps you!
JS / HTML / CSS
var arrayOfFiles = [
"/first_folder/app_01.dat",
"/first_folder/app_02.dat",
"/first_folder/second_folder/ret_01.dat",
"/first_folder/second_folder/ret_02.dat",
"/first_folder/second_folder/third_folder",
"/other_first_folder/other_app_01.dat",
"/other_first_folder/other_app_02.dat",
"/other_first_folder/other_sec_folder/other_app_03.dat",
"/other_first_folder/other_sec_folder/other_app_04.dat",
"/new_folder/new_other_folder/last_folder",
];
var wrap = document.getElementById("myTreeSelector");
var lines = "----";
var res = {};
function makeObj(obj) {
var splme = obj.split('/').slice(1);
var f = res;
for (i = 0; i < splme.length; i++) {
obName = { name: splme[i] };
f = f[splme[i]] = f[splme[i]] || obName;
}
}
arrayOfFiles.map(makeObj);
var tmp = [];
function prsObj(x, y) {
for (var k in x) {
if (typeof x[k] === 'object' && x[k] !== null) {
prsObj(x[k], y);
}
else if (x.hasOwnProperty(k)) {
y(x[k]);
}
tmp.push(lines);
}
};
prsObj(res, function (ftmp) { tmp.push(ftmp); });
var redy = [];
for (var i = 0; i < tmp.length - 1; i++) {
if (tmp[i] !== lines) {
redy.push(tmp[i]);
}
else if (tmp[i] === lines && tmp[i + 1] === lines) {
redy.push("file");
i++;
}
else if (tmp[i] === lines && tmp[i + 1] !== lines) {
redy.push("folder");
} else { }
}
redy.pop();
var text = '';
var x = 0;
var y = 0;
for (var i = 0; i < redy.length; i++) {
if (y === 1) {
var text = document.createTextNode(redy[i]);
addThis(0);
x = 0;
y = 0;
}
else if (redy[i + 1] === "file" && redy[i + 2] === "file") {
if (redy[i].indexOf(".") > -1) {
var text = document.createTextNode(redy[i]);
addThis(1);
i = i + 2;
x = 0;
y = 1;
} else {
var text = document.createTextNode(redy[i]);
addThis(2);
i = i + 2;
x = 0;
y = 1;
}
}
else if (redy[i + 1] === "folder" && i === 0) {
var text = document.createTextNode(redy[i]);
addThis(0);
x++;
y = 0;
}
else if (redy[i + 1] === "folder" && x >= 1 || redy[i + 1] === "folder") {
var text = document.createTextNode(redy[i]);
addThis(2);
x++;
y = 0;
}
else if (redy[i + 1] === "file") {
if (redy[i] === '') { continue; }
else if (redy[i].indexOf(".") > -1) {
var text = document.createTextNode(redy[i]);
addThis(1);
y = 0;
} else {
var text = document.createTextNode(redy[i]);
addThis(2);
y = 0;
}
}
else { }
}
function addThis(x) {
if (x === 0) {
var li = document.createElement("li");
var span = document.createElement("span");
span.setAttribute("class", "caret folder-selector");
var iel = document.createElement("i");
iel.setAttribute("class", "fa fa-folder");
var ul = document.createElement("ul");
ul.setAttribute("class", "nested");
span.appendChild(iel);
span.appendChild(text);
li.appendChild(span);
wrap.appendChild(li);
li.appendChild(ul);
}
if (x === 1) {
var get = wrap.getElementsByClassName("nested");
var ul = get[get.length - 1];
var iel = document.createElement("i");
iel.setAttribute("class", "fa fa-file");
var li = document.createElement("li");
li.setAttribute("class", "file");
ul.appendChild(li);
li.appendChild(iel);
li.appendChild(text);
}
if (x === 2) {
var get = wrap.getElementsByClassName("nested");
var ul = get[get.length - 1];
var li = document.createElement("li");
var span = document.createElement("span");
span.setAttribute("class", "caret folder-selector");
var iel = document.createElement("i");
iel.setAttribute("class", "fa fa-folder");
ul.appendChild(li);
li.appendChild(span);
span.appendChild(iel);
span.appendChild(text);
var get = wrap.querySelectorAll("li")
var li = get[get.length - 1];
var ul = document.createElement("ul");
ul.setAttribute("class", "nested");
li.appendChild(ul);
}
}
ul {
list-style-type: none;
}
i {
margin-right: 5px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" />
<ul id="myTreeSelector">
</ul>

Style and change text content of children based on click

I am trying to style a single div and change the text content based on whether the user clicks on the item or not.
At the moment the code is extremely repetitive and I am looking for a method that will help me rewrite it to both work more efficiently and look cleaner.
Thank you.
let lvl0,
lvl1,
lvl2;
lvl0 = document.querySelector('.level-wrapper').children[0];
lvl1 = document.querySelector('.level-wrapper').children[1];
lvl2 = document.querySelector('.level-wrapper').children[2];
lvl0.addEventListener('click', changeStyle0);
lvl1.addEventListener('click', changeStyle1);
lvl2.addEventListener('click', changeStyle2);
function changeStyle0() {
document.querySelector('.text-header').textContent = levelTitle[0];
var showStyle = document.querySelector('.level-wrapper').children[0];
showStyle.style.opacity = '1';
showStyle.style.backgroundColor = '#95a5a6';
showStyle.style.border = '2px solid white';
showStyle.style.boxSizing = 'border-box';
console.log(showStyle);
}
function changeStyle1() {
document.querySelector('.text-header').textContent = levelTitle[1];
var showStyle = document.querySelector('.level-wrapper').children[1];
showStyle.style.opacity = '1';
showStyle.style.backgroundColor = '#95a5a6';
showStyle.style.border = '2px solid white';
showStyle.style.boxSizing = 'border-box';
console.log(showStyle);
}
function changeStyle2() {
document.querySelector('.text-header').textContent = levelTitle[2];
var showStyle = document.querySelector('.level-wrapper').children[2];
showStyle.style.opacity = '1';
showStyle.style.backgroundColor = '#95a5a6';
showStyle.style.border = '2px solid white';
showStyle.style.boxSizing = 'border-box';
console.log(showStyle);
}
var levelTitle = ["Question about Drinks/Soda/Water.", "Question about Portion Control/Meals.", "Question about Salt/Sugar."];
The method document.querySelector returns you only the first element in the document that fit the selector. To get an array of all the fits elements in the document, you have to use document.querySelectorAll method. For example:
const elements = document.querySelectorAll('.level-wrapper');
elemets[2].children[0].style = ...;
I think that this code is taking care of your issue:
const elements = document.querySelectorAll('.level-wrapper');
function showStyle(index, childIdx) {
elements[index].children[childIdx].style.opacity = '1';
elements[index].children[childIdx].style.backgroundColor = '#95a5a6';
elements[index].children[childIdx].style.border = '2px solid white';
elements[index].children[childIdx].style.boxSizing = 'border-box';
//console.log(levelStyle[index]);
}
function hideStyle(index, childIdx) {
elements[index].children[childIdx].style.opacity = '1';
elements[index].children[childIdx].style.backgroundColor = 'transparent';
elements[index].children[childIdx].style.border = 'none';
elements[index].children[childIdx].style.boxSizing = 'unset';
//console.log(levelStyle[index]);
}
/*for (let i = 0; i < elements.length; i++) {
for (let j = 0; j < elements[i].children.length; j++) {
showStyle(i,j);
}
}*/
for (let i = 0; i < elements.length; i++) {
for (let j = 0; j < elements[i].children.length; j++) {
(function (index) {
elements[i].children[j].onclick = function () {
elements[i].children[j].is_show = !elements[i].children[j].is_show;
if (elements[i].children[j].is_show) {
showStyle(i,j);
} else {
hideStyle(i,j);
}
}
})(j + i*elements[i].children.length);
}
}
<div class="level-wrapper">
<div>aaa</div>
<div>bbb</div>
<div>ccc</div>
<div>ddd</div>
</div>
<br><br>
<div class="level-wrapper">
<div>eee</div>
<div>fff</div>
<div>ggg</div>
<div>hhh</div>
</div>
This method ended up working for me.
Here is the Javascript:
function styleChange() {
const elements = document.getElementsByClassName("level");
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = function () {
let el = elements[0];
while (el) {
if (el.tagName === "LI") {
el.classList.remove("active");
}
el = el.nextSibling;
}
this.classList.add("active");
};
}
}
styleChange();
Here is the html:
<ul class="level-wrapper">
<li class="level">
</a>1</li>
<li class="level">
</a>2</li>
<li class="level">
</a>3</li>
</ul>
And finally the css:
.active {
background-color: #95a5a6;
opacity: 1;
box-sizing: border-box;
border: 2px solid #fff;
}

Assign max-height to inner element after toggle

many thanks to anyone who can help me solving the issue
Here is HTML and JS:
var acc = document.getElementsByClassName("razvernut");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("razvorot");
var panel = document.getElementsByClassName('sub-menu');
if (panel.style.maxHeight){
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
}
}
<li class="razvernut">
<span class="span"></span>
<span class="otherspan"></span>
<a>Show ul.sub-menu with 0 max-height</a>
<ul class="sub-menu">...</ul>
</li>
My skill in js is quite low, please tell me where is the mistake as script cant define the .sub-menu within the page :\
getElementsByClassName returns a list of elements, you can either iterate over the list and use each element inside the loop, or in your case you can use panel[0].
var acc = document.getElementsByClassName("razvernut");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("razvorot");
var panel = document.getElementsByClassName('sub-menu');
if (panel[0].style.maxHeight){
panel[0].style.maxHeight = null;
} else {
panel[0].style.maxHeight = panel[0].scrollHeight + "px";
}
}
}
<li class="razvernut">
<span class="span"></span>
<span class="otherspan"></span>
<a>Show ul.sub-menu with 0 max-height</a>
<ul class="sub-menu">...</ul>
</li>

onmouseover show only one submenu ( Javascript )

Good day,
trying to make a small menu only with Javascript and got a problem that onmouseover event shows all submenus and not only one.
this is the part of the code that suppose to change style.display to block.
var ul = document.getElementById("navMainId"),
var liDropdown = ul.getElementsByClassName("dropdown");
for (var i = 0; i < liDropdown.length; i++) {
liDropdown[i].style.display = "inline-block";
liDropdown[i].onmouseover = function () {
var ul = document.getElementById("navMainId"),
dropdown = ul.getElementsByClassName("dropdown-content");
for (var i = 0; i < dropdown.length; i++) {
dropdown[i].style.display = "block";
}
}
liDropdown[i].onmouseleave = function () {
var ul = document.getElementById("navMainId"),
dropdown = ul.getElementsByClassName("dropdown-content");
for (var i = 0; i < dropdown.length; i++) {
dropdown[i].style.display = "none";
}
}
}
How can i change the code to make it work ?
here is whole code on Fiddle ( ssry it is a mess ) : https://jsfiddle.net/apvsnzt5/1/
I've updated the fiddle:
https://jsfiddle.net/apvsnzt5/3/
All you needed to do was change
dropdown = ul.getElementsByClassName("dropdown-content");
to
dropdown = this.getElementsByClassName("dropdown-content");
So that it targets the "this" (being the LI you are hovered over) rather than finding the "dropdown-content" inside the UL.
Also do it on the onmouseleave.
Seems you have incorrect reference to container when mouser over. You need concrete content based on your mosue position.
var dropdown = this.getElementsByClassName("dropdown-content");
try updated fiddle
add the following to your css part
.dropdown-content{
display:none ! important;
}
a:hover+.dropdown-content{
display:block ! important;
}
it will works fine.
var menuCont = document.createElement("div");
document.body.appendChild(menuCont);
var ulMain = document.createElement("ul");
menuCont.appendChild(ulMain);
ulMain.className = "navMain";
ulMain.id = "navMainId";
/****** MENU ******/
// Software
var liSoftware = document.createElement("li");
liSoftware.className = "menu dropdown";
ulMain.appendChild(liSoftware);
var aSoftware = document.createElement("a");
aSoftware.className = "menuName dropbtn";
aSoftware.href = "#";
aSoftware.textContent = "Test1";
liSoftware.appendChild(aSoftware);
// GeCoSoft
var liGeco = document.createElement("li");
liGeco.className = "menu dropdown";
ulMain.appendChild(liGeco);
var aGeco = document.createElement("a");
aGeco.className = "menuName dropbtn";
aGeco.href = "#";
aGeco.textContent = "Test2";
liGeco.appendChild(aGeco);
/******* Submenu *******/
// Software Sub
var divSubSoft = document.createElement("div");
divSubSoft.className = "dropdown-content";
liSoftware.appendChild(divSubSoft);
var aSub1 = document.createElement("a"),
aSub2 = document.createElement("a");
aSub1.className = "menuSubName";
aSub1.textContent = "SubMenu1";
aSub1.href = "#";
aSub2.className = "menuSubName";
aSub2.textContent = "SubMenu2";
aSub2.href = "#";
divSubSoft.appendChild(aSub1);
divSubSoft.appendChild(aSub2);
// Gecosoft Sub
var divSubGeco = document.createElement("div");
divSubGeco.className = "dropdown-content";
liGeco.appendChild(divSubGeco);
var aSub3 = document.createElement("a"),
aSub4 = document.createElement("a");
aSub3.className = "menuSubName";
aSub3.textContent = "Submenu3";
aSub3.href = "#";
aSub4.className = "menuSubName";
aSub4.textContent = "SubMenu4"
aSub4.href = "#";
divSubGeco.appendChild(aSub3);
divSubGeco.appendChild(aSub4);
/****** MENU STYLE ******/
var i = "";
ulMain.style.listStyleType = "none";
ulMain.style.margin = "0px";
ulMain.style.padding = "0px";
ulMain.style.overflow = "hidden";
ulMain.style.backgroundColor = "rgb(232, 225, 225)";
var ul = document.getElementById("navMainId"),
li = ul.getElementsByTagName("li");
for (var i = 0; i < li.length; i++) {
li[i].style.cssFloat = "left";
}
var a = ul.getElementsByTagName("a");
for (var i = 0; i < a.length; i++) {
a[i].style.display = "inline-block";
a[i].style.color = "black";
a[i].style.textAlign = "center";
a[i].style.padding = "14px 16px";
a[i].style.textDecoration = "none";
a[i].onmouseover = function () {
this.style.backgroundColor = "rgb(174, 170, 170)";
}
a[i].onmouseleave = function () {
this.style.backgroundColor = "rgb(232, 225, 225)";
}
}
/************ Dont know what to do here **************/
var liDropdown = ul.getElementsByClassName("dropdown");
for (var i = 0; i < liDropdown.length; i++) {
liDropdown[i].style.display = "inline-block";
liDropdown[i].onmouseover = function () {
var ul = document.getElementById("navMainId"),
dropdown = ul.getElementsByClassName("dropdown-content");
for (var i = 0; i < dropdown.length; i++) {
dropdown[i].style.display = "block";
}
}
liDropdown[i].onmouseleave = function () {
var ul = document.getElementById("navMainId"),
dropdown = ul.getElementsByClassName("dropdown-content");
for (var i = 0; i < dropdown.length; i++) {
dropdown[i].style.display = "none";
}
}
}
var divDropCont = ul.getElementsByClassName("dropdown-content");
for (var i = 0; i < divDropCont.length; i++) {
divDropCont[i].style.display = "none";
divDropCont[i].style.position = "absolute";
divDropCont[i].style.backgroundColor = "#f9f9f9";
divDropCont[i].style.minWidth = "160px";
divDropCont[i].style.boxShadow = "0px 8px 16px 0px rgba(0,0,0,0.2)";
}
var aDropCont = ul.getElementsByClassName("menuSubName");
for (var i = 0; i < aDropCont.length; i++) {
aDropCont[i].style.color = "black";
aDropCont[i].style.padding = "12px 16px";
aDropCont[i].style.textDecoration = "none";
aDropCont[i].style.display = "block";
aDropCont[i].style.textAlign = "left";
aDropCont[i].onmouseover = function () {
this.style.backgroundColor = "rgb(174, 170, 170)";
}
aDropCont[i].onmouseleave = function () {
this.style.backgroundColor = "rgb(249, 249, 249)";
}
}
.dropdown-content{
display:none ! important;
}
a:hover+.dropdown-content{
display:block ! important;
}

Onclick event on an array of list items

I have a list and I want to create a function that will bold the clicked list item. So far I managed to create a function and assign it to list items. Onclick, it bolds, but just one item. I don't know how to set it for each item.
I could have used id to bold the items but there will be a lot of items in the list. I can't deal with each one of them.
html
<ul>
<li>apple</li>
<li>orange</li>
<li>banana</li>
</ul>
javascript
var list = document.getElementsByTagName("li");
function markSelection() {
if(list[0].style.fontWeight !== "bold") {
list[0].style.fontWeight = "bold";
} else {
list[0].style.fontWeight = "normal";
}
}
for (i = 0, len = list.length; i < len; i++){
list[i].onclick = markSelection;
}
It bolds only list[0]. How can I set it to bold the clicked list item?
jsfiddle
function markSelection() {
if(this.style.fontWeight !== "bold") {
this.style.fontWeight = "bold";
} else {
this.style.fontWeight = "normal";
}
}
for (i = 0, len = list.length; i < len; i++){
list[i].onclick = markSelection;
}
You need to use the context of the click event like this.
function markSelection(evt) {
var tar = evt.target;
if(tar.style.fontWeight !== "bold")
tar.style.fontWeight = "bold";
else
tar.style.fontWeight = "normal";
}
See fiddle here
Why not using a class and toggle it whenever you want ( for example adding it on click, and let css handle the style )
http://jsfiddle.net/blackjim/by9bP/2/
var list = document.getElementsByTagName("li");
function markSelection() {
if(this.className.indexOf('boldFruit')===-1){
this.className += " boldFruit";
}
}
for (i = 0, len = list.length; i < len; i++){
list[i].onclick = markSelection;
}
CSS
li {
font-size: 20px;
}
li.boldFruit {
font-weight: bold;
}

Categories