Keyboard navigation on nested list using only javascript - javascript

I have a ul list as follows. I am new to JS and trying to do a keyboard navigation, just the arrow keys using only javascript.
<ul id= nav>
<li class =subnav id =sub1> Companies
<ul id = hidden>
<li> item 1 </li>
<li> item 2 </li>
<li> item 3 </li>
</ul>
</li>
<li class =subnav id =sub2> LINKS
<ul id = hidden>
<li> item 4 </li>
<li> item 5 </li>
<li> item 6 </li>
</ul>
</li>
</ul>
my JS:
ul = document.getElementById("nav");
li = ul.getElementsByClassName("subnav");
ul2 = document.getElementById("hidden");
li = ul.getElementsByTagName("li");
function keyPress(e)
{
var e = e||window.event;
e =e.which||e.keyCode;
for( var i=0; i<li.length; i++)
{
var f = li[i].childNodes[0];
if(li[i].children.length > 0)
{
for(var j=0; j<li2.length; j++)
{
var x = li2[j].childNodes[0];
}
}
else
{
alert("no child nodes");
}
}
}
I am trying to set focus on the first item and then moving to each nodes using keys.

I suggest using jwerty, awesome keyboard events library.
I used jQuery and jWerty plugin.
Here is a quick JSFiddle: (Click the preview window and start hitting the down key)
http://jsfiddle.net/8QZrV/
As a basic idea, you should create an object with all the elements and then iterate through them, my basic example was like this:
var i = 0;
j = jQuery('.navigator li').length;
And then you hook it up in jwerty, I guess you want to make some actions there, so I guess you should also .focus() the current element.
Enjoy!

Related

How do I fix adding multiple items based on their name to an array list using the same function?

I have created a system where if you click a button it adds the buttons textContent to an array list. The problem is I made sure you could not click the same button multiple times to add it to the list. If you click a different button though, it does not add it to the list but instead the console outputs 'item already in list'. Along with adding the text content of the first item of the li list.
Code:
const cartItems = [];
function cartAmount() {
var itemsInCart;
itemsInCart = document.getElementById('cartamount');
if(cartItems.length >= 1) {
itemsInCart.style.visibility = "visible";
}
itemsInCart.innerHTML = cartItems.length;
}
function addCart() {
var itemVal, aVal, a;
a = document.getElementById('item');
aVal = a.value;
itemVal = a.textContent;
if(cartItems.includes(a.textContent)) {
console.log('item already in cart');
} else {
cartItems.push(itemVal);
// cartAmount();
console.log('added Item to cart');
}
}
<ul id="wrapper">
<li>
Drum
</li>
<li>
Stand Off
</li>
<li>
Seat
</li>
<li>
Drum Sticks
</li>
<li>
Symbols
</li>
<li>
Keyboard
</li>
<li>
High Hat
</li>
<li>
Bass Drum
</li>
<li>
Snare Drum
</li>
<li>
Jake Inator
</li>
</ul>
You need to use different ids for the items so you can distinguishes between items:
Run the next snippet which show a solution to your problem.
const cartItems = [];
function addCart(id) {
var itemVal, aVal, a;
a = document.getElementById(id);
aVal = a.value;
itemVal = a.textContent;
console.log(itemVal);
if(cartItems.includes(a.textContent)) {
console.log('item already in cart');
} else {
cartItems.push(itemVal);
console.log('added Item to cart');
}
}
<ul id="wrapper">
<li>
Drum
</li>
<li>
Stand Off
</li>
<li>
Seat
</li>
<li>
Drum Sticks
</li>
<li>
Symbols
</li>
<li>
Keyboard
</li>
<li>
High Hat
</li>
<li>
Bass Drum
</li>
<li>
Snare Drum
</li>
<li>
Jake Inator
</li>
</ul>
Its because all of your "a" tags have the same id. An id is supposed to be unique so you want to give a different id to each of the items you want users to be able to add to the cart.
Then inside your js function you need to update it so you get the event object as a parameter and get the element that was clicked on through it
function addCart(e) {
var itemVal, aVal, a;
aVal = e.target.value;
itemVal = e.target.textContent;
if(cartItems.includes(itemVal)) {
console.log('item already in cart');
} else {
cartItems.push(itemVal);
cartAmount();
console.log('added Item to cart');
}
}```

Uncaught TypeError: Cannot read property 'classList' of undefined - Trying to toggle a class in JS

I am simply trying to toggle a class on and off with a click for a todo list app I am creating with vanilla JS only (no jQuery). What am I missing or doing wrong? Here is the code I have now
HTML
<div id="container">
<h1> Todo List </h1>
<input type="text" placeholder="New todo">
<ul>
<li> item 1</li>
<li> item 2 </li>
<li> item 3 </li>
</ul>
</div>
JS
var h1 = document.querySelector("h1")
var li = document.getElementByTagName("li")
for ( var i = 0 ; i < li.length ; i++){
li[i].addEventListener("click", function(){
li[i].classList.toggle("done");
})
}
When I click on an item in the list, it gives me this error in the console - "Uncaught TypeError: Cannot read property 'classList' of undefined"
What am I doing wrong?
The scope/value of i was the problem. When clicking on the element the value of i is incremented to 3(in this case) because of i++.
You can use let instead of var
var h1 = document.querySelector("h1")
var li = document.getElementsByTagName("li")
for (let i = 0; i < li.length; i++) {
li[i].addEventListener("click", function() {
// console.log(i) // will log i =3 if let is replaced by var
li[i].classList.toggle("done");
})
}
.done{
color:red;
}
<div id="container">
<h1> Todo List </h1>
<input type="text" placeholder="New todo">
<ul>
<li> item 1</li>
<li> item 2 </li>
<li> item 3 </li>
</ul>
</div>
There are two problems in your code.
Instead of getElementByTagName, it should be getElementsByTagName:
li = document.getElementsByTagName("li")
Inside the event handler definied on the li elements, you could refer to the li elements using this instead of li[i].
Basically the problem is the i is incremented by your for loop, and when the click handler actually executes the value of i is 3. So referring to li[i] is an error because there are only three li elements (so up to li[2]).
Here's the updated snippet:
var h1 = document.querySelector("h1")
var li = document.getElementsByTagName("li")
for (var i = 0; i < li.length; i++) {
li[i].addEventListener("click", function() {
this.classList.toggle("done");
})
}
.done {
color: red;
}
<div id="container">
<h1> Todo List </h1>
<input type="text" placeholder="New todo">
<ul>
<li> item 1</li>
<li> item 2 </li>
<li> item 3 </li>
</ul>
</div>
Responding to ikrabbe in comments, you could create the indexing variable i in local scope using let keyword: let i = 0. But note that it won't work with older browsers.
var h1 = document.querySelector("h1")
var li = document.getElementsByTagName("li")
for (let i = 0; i < li.length; i++) {
li[i].addEventListener("click", function() {
li[i].classList.toggle("done");
})
}
.done {
color: red;
}
<div id="container">
<h1> Todo List </h1>
<input type="text" placeholder="New todo">
<ul>
<li> item 1</li>
<li> item 2 </li>
<li> item 3 </li>
</ul>
</div>

Add classes to parent nodes (<a>) in a nested list

The <a> inside the last <li> of the innermost <ul> has a class which is named current. It highlights the active link.
I want to highlight all links that are parents from this link.
HTML
<ul>
<li>
Samsung
<ul>
<li>
Galaxy
<ul>
<li>
Galaxy Note 4
</li>
<li>
Galaxy S 5
</li>
</ul>
</li>
</ul>
</li>
</ul>
Galaxy S 5 has the class current, but I also want to add the class current to the parent links (Samsung, Galaxy).
Define an outermost parent element by giving it an id.
Get the element with the current class and assign it to a temp var.
Loop over the elements starting with your current element and change the value of the temp var to temp.parentNode. If the elements parent node is a list item add your current class to the first child element of the list element.
var until = document.getElementById("list"),
crnt = document.getElementsByClassName('current'),
tmp = crnt.item(0);
while(tmp !== until) {
if(tmp.tagName === "LI") {
tmp.children.item(0).classList.add("current");
}
tmp = tmp.parentNode;
}
.current {
color: tomato;
}
<ul id="list">
<li>
Samsung
<ul>
<li>
Galaxy
<ul>
<li>
Galaxy Note 4
</li>
<li>
Galaxy S 5
</li>
</ul>
</li>
</ul>
</li>
</ul>
var els = document.getElementsByClassName('current')
for(var i = 0; i < els.length; i++) {
if(els[i].parentNode != null)
els[i].parentNode.className += ' current';
}
HTML :
<ul>
<li>
Samsung
<ul>
<li>
Galaxy
<ul>
<li>
Galaxy Note 4
</li>
<li>
Galaxy S 5
</li>
</ul>
</li>
</ul>
</li>
</ul>
JavaScript :
<script>
ele = document.getElementById("s5");
par1 = ele.parentNode.parentNode.parentNode;
par1.children[0].className+="current";
par2 = par1.parentNode.parentNode;
par2.children[0].className+="current";
</script>
I've used parentNode to select parent element.starting from the last children.Hope this will work for you. :)
There are six parents from the inside class="current". Suppose there are no other classes that are named with current.
Take the inside node and go from there to the outside node. Add the parentNode to the current node.
node = node.parentNode
As been said there are six nodes from there.
var count = 0;
var max = 6;
while(count < max) { ...; count += 1 }
Check if the node name is a <li> and not the inside <li>. Add to the first child, which is the <a> the class name.
if(node.nodeName === "LI") {
if(count > 1) {
node.children[0].className = "myClass";
}
}
// If there are no other classes that are named with "current"
// There are six parents from inside node
var node = document.getElementsByClassName("current")[0];
var count = 0;
var max = 6;
var cN = "current";
while (count < max) { // 6 times (ul li ul li ul li)
node = node.parentNode;
if (node.nodeName === "LI") {
if (count > 1) { // Not <li> inside
node.children[0].className = cN; // <a>
}
}
count += 1;
}
.current {
background: pink;
}
<ul>
<li>
Samsung
<ul>
<li>
Galaxy
<ul>
<li>
Galaxy Note 4
</li>
<li>
<a href="c.html" class='current'>Galaxy S 5</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>

Next child LI that is wrapped in a different UL

I have the following list:
<ul>
<li class="item">One</li>
<li class="item">Two</li>
<li class="item">Three
<ul>
<li class="item">Something Original</li>
<li class="item selected">Something</li>
</ul>
</li>
<li>Four
<ul>
<li class="item">I want this selected next</li>
<li class="item">Good</li>
</ul>
</li>
</ul>
Using jQuery, how do I find the next li with the class="item" since it is wrapped in a different container. Obviously I cannot do $(".selected").next(".item") so how else can I do it?
JSFiddle: http://jsfiddle.net/q3f6v7zz/
Since the li elements are nested and you know that you want the next appearing li with a particular class, you can use .index() and do something like this
var $li = $('.item'); // <--- get the list of all lis with class .item
var index = $li.index($('.selected')); // <--- find the index of the one with .selected amongst all the lis
console.log($li.eq(index+1).html()); // <--- index+1 because you need the next appearing li after selected
If you want to move the selected class on keydown something like this should do
var $li = $('.item');
$(document).on('keydown', function(e) {
if (e.keyCode == 40) {
var index = $li.index($('.selected'));
$li.eq(index).removeClass('selected');
index = (index+1) % $li.length; // <--- to rotate the values from 0 to count of li.item elements
$li.eq(index).addClass('selected');
}
});
var $li = $('.item');
$(document).on('keydown', function(e) {
if (e.keyCode == 40) {
var index = $li.index($('.selected'));
$li.eq(index).removeClass('selected');
index = (index+1) % $li.length;
$li.eq(index).addClass('selected');
}
});
.selected {
background: green;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<ul>
<li class="item">One</li>
<li class="item">Two</li>
<li>Three
<ul>
<li class="item">Something</li>
<li class="item selected">Something Else</li>
</ul>
</li>
<li>Four
<ul>
<li class="item">I want this selected next</li>
<li class="item">Good</li>
</ul>
</li>
</ul>
You can get the index of the selected element within all lis, and then increment that index to get the next one.
$("ul").on("click", "li.item.selected", function() {
var all_li = $("li.item");
var selected_index = all_li.index(this);
var next_li = all_li.eq((selected_index + 1) % all_li.length);
$(this).removeClass("selected");
next_li.addClass("selected");
});
.item.selected {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="item">One</li>
<li class="item">Two</li>
<li class="item">Three
<ul>
<li class="item selected">Something</li>
</ul>
</li>
<li>Four
<ul>
<li class="item">I want this selected next</li>
<li class="item">Good</li>
</ul>
</li>
</ul>
I used the modulus so it will wrap around at the end.
Not sure what you are exactly looking for but you can use $(Element").parent().parent().find("li");
So in other words .parent() may be what you are looking for there is also .sibling() to find or you may want $('li').closest('ul').find('li')
which will go up the tree to find the nearest ul to the one you are looking for
https://api.jquery.com/closest/
You may also use:
Vanilla JS to do something similar to what was discussed by others with $index if it makes more sense to you:
Again this isn't as efficient but that is basically what JQuery is doing:
var myLis = document.getElementsByTagName('li');
var wantedIndex;
for(var i = 0;i<myLis.length; i++){
if(myLis[i].className === "active"){
wantedIndex = i+1; //gets the li which is next when selecting all lis
}
}

hide/ remove menu from menuitem

I have Joomla menu as below.
<li class="level1 parent">
<a href="/www.dd.com/index.php/donations" class="level1 parent">
<span>Donations</span>
</a>
</li>
<li class="level1 parent">
<a href="/www.dd.com/index.php/fund" class="level1 parent">
<span>Fund</span>
</a>
</li>
What I am able to do is find the list menu that I have.
var texts = [], lis = document.getElementsByTagName("span");
var im=lis.length;
var textFound;
for(var i=0; im>i; i++) {
textFound = lis[i].firstChild.nodeValue
texts.push(lis[i].firstChild.nodeValue);
}
What I want to do is if menu is Donations, hide it
I tried with this.style.display='none';, however it is not working.
var texts = [], lis = document.getElementsByTagName("span");
var im=lis.length;
var textFound;
for(var i=0; im>i; i++) {
textFound = lis[i].firstChild.nodeValue
texts.push(lis[i].firstChild.nodeValue);
this.style.display='none';
}
Any idea how to get this done?
Note, I want this to be done in JAVASCRIPT ONLY. NO JQUERY
Instead of this, it should be like lis[i].style.display = 'none'
Also you will need to compare the text you get.. I don't see any comparision condition to hide specific texts.

Categories