This element index - javascript

I need to get the this.element index on a eventListener click. I looked for a good index solution but i didn't understand most of them.
<div class="navbar">
<ul>
<li class="menu">Link 1</li>
<li class="menu">Link 2</li>
<li class="menu">Link 3</li>
<li class="menu">Link 4</li>
<li class="menu">Link 5</li>
</ul>
</div>
Now i need my loop var to match my index like this
var $menuItem = document.getElementsByClassName('menu');
for ( var x = 0; x < $menuItem.length; x++ ){
$menuItem[x].addEventListener('click', function(){
if (this.toString() + index == $menuItem[x]){
$menuItem[x].classList.add('active')
}
})
}
I know this may not be the best way to add a link to a this element but i need the index for another function.

You already have the index. The x variable. You can use a self-invoking function for remembering it in the corresponding handler:
for (var x = 0; x < $menuItem.length; x++) {
(function(index) {
$menuItem[index].addEventListener('click', function() {
// ...
})
})(x)
}
Another option is using Array#indexOf function:
// Convert the `HTMLCollection` into a regular array!
var $menuItem = [].slice.call(document.getElementsByClassName('menu'));
for (var x = 0; x < $menuItem.length; x++) {
$menuItem[x].addEventListener('click', function() {
var index = $menuItem.indexOf(this);
})
}
Another option is using Array#forEach method:
[].forEach.call(document.getElementsByClassName('menu'), function(el, index) {
el.addEventListener('click', function() {
// `index` refers to clicked element index in the collection
})
});

Related

Calling a function in mylibray onclick passing this

I've got this onclick call that works.
<ul id="ul_name" onclick="javascript:mylibrary.ul_action(this);">
I don't want to manually add the call on every ul so i tried this one:
window.onload = function() {
var u = document.querySelectorAll(".myclass ul");
var i;
for (i = 0; i < u.length; i++) {
u[i].onclick = mylibrary.ul_action(this);
};
};
But it doesn't work. It's this's scope wrong? Or what? Thank you.
You are calling the function immediately and assigning the return value as your click event handler.
You need to create a new function that calls your existing function with the argument you want (which isn't this because you want it to be the element):
window.onload = function() {
var mylibrary = {
ul_action: function(ul) {
console.log(ul.innerText);
}
};
var u = document.querySelectorAll(".myclass ul");
var i;
for (i = 0; i < u.length; i++) {
u[i].onclick = mylibrary.ul_action.bind(window, u[i]);
};
};
<main class="myclass">
<ul>
<li>One</li>
</ul>
<ul>
<li>Two</li>
</ul>
</main>

<ul> <li> list items adding onclick

I'm trying to add onclick to my list items and get the href with pure JavaScript.
I had it working from just a div but as soon as I added list items I was unable to get it to work.
var navBarDiv = document.getElementById("navbar");
var contentDiv = document.getElementById("content");
var navBarList = navBarDiv.getElementsByTagName("ul");
// Attach click listeners for each of the nav links.
for (var i = 0; i < navBarList.length; i++) {
var navBarItems = navBarList[i].getElementsByTagName("li");
for (var j = 0; j < navBarItems.length; j++) {
navBarItems[j].addEventListener('click', function (e) {
e.preventDefault();
var pageURL = this.attributes['href'].value;
GetFileDoc(pageURL, function (doc) {
contentDiv.innerHTML = doc.getElementById("content").innerHTML;
});
});
}
}
<div id="navbar">
<ul>
<li>Homepage</li>
<li>About Us</li>
<li>Gallery</li>
<li>Contact Us</li>
</ul>
</div>
This appears to be the problem:
var pageURL = this.attributes['href'].value;
You're adding the click handler to the li element, not the a element. So there is no href attribute. I imagine there are two options:
Drill into the element further to get the href of the child a element, or
Add the click handler to the a element instead
For the first option, you might do something like this instead:
var pageURL = this.getElementsByTagName("a")[0].attributes['href'].value;
You'd want to put in some error checking of course, unless you're very confident that there will always be that first a element that you want.
For the second option, you might just modify what you're looping over. Perhaps something like this:
var navBarItems = navBarList[i].getElementsByTagName("a");
Assuming there are no other a elements in the hierarchy that you don't want to use for this.
Try with
a tag is the children of li so you need use querySelectorfor target the child element
And simply use with element.href for getting href value
var navBarDiv = document.getElementById("navbar");
var contentDiv = document.getElementById("content");
var navBarList = navBarDiv.getElementsByTagName("ul");
for (var i = 0; i < navBarList.length; i++) {
var navBarItems = navBarList[i].getElementsByTagName("li");
for (var j = 0; j < navBarItems.length; j++) {
navBarItems[j].addEventListener('click', function(e) {
e.preventDefault();
var pageURL = this.querySelector('a').href;
console.log(pageURL)
//GetFileDoc(pageURL, function(doc) {
//contentDiv.innerHTML = doc.getElementById("content").innerHTML;
// });
});
}
}
<div id="navbar">
<ul>
<li>Homepage</li>
<li>About Us</li>
<li>Gallery</li>
<li>Contact Us</li>
</ul>
</div>
You are adding event to each li in the list. According to the rest of the code, espscialy the line this.attributes['href'].value you expect to have link element loaded. You can load all the link elements instead right away by using
navBarList[i].querySelectorAll("a");
which gives you all the inner links.
Try this one:
var navBarDiv = document.getElementById("navbar");
var contentDiv = document.getElementById("content");
var navBarLinks = navBarDiv.getElementsByTagName("a");
// Attach click listeners for each of the nav links.
for (var i = 0; i < navBarLinks.length; i++) {
navBarLinks[i].addEventListener('click', function (e) {
e.preventDefault();
var pageURL = this.attributes['href'].value;
GetFileDoc(pageURL, function (doc) {
contentDiv.innerHTML = doc.getElementById("content").innerHTML;
});
});
}
}
It's nearly the same except I filter directly to the anchor-tags and then there is a href attribute.
Without using ES6
Array.prototype.slice.call(document.querySelectorAll('#navbar ul li a')).map(function(item) {
item.addEventListener('click', function(e) {
e.preventDefault();
console.log("href", e.target.href);
});
});
<div id="navbar">
<ul>
<li>Homepage</li>
<li>About Us</li>
<li>Gallery</li>
<li>Contact Us</li>
</ul>
</div>
Using ES6
[...document.querySelectorAll('#navbar ul li a')]
.map((item) => {
item.addEventListener('click', (e) => {
e.preventDefault();
console.log("href", e.target.href);
});
});
<div id="navbar">
<ul>
<li>Homepage</li>
<li>About Us</li>
<li>Gallery</li>
<li>Contact Us</li>
</ul>
</div>
jQuery
jQuery('#navbar ul li').click(function() {
console.log("href", $(this).attr('href'))
});
Please try this code
var navBarDiv = document.getElementById("navbar");
var contentDiv = document.getElementById("content");
var navBarList = navBarDiv.getElementsByTagName("ul");
// Attach click listeners for each of the nav links.
for (var i = 0; i < navBarList.length; i++) {
var navBarItems = navBarList[i].getElementsByTagName("li");
for (var j = 0; j < navBarItems.length; j++) {
navBarItems[j].addEventListener('click', function (e) {
e.preventDefault();
var pageURL = this.getElementsByTagName('a')[0].href;
alert(pageURL);
GetFileDoc(pageURL, function (doc) {
contentDiv.innerHTML = doc.getElementById("content").innerHTML;
});
});
}
}

How to add event listener inside a for loop?

For some reason I need to loop through various elements and change their style when they are clicked. This is the code(https://jsfiddle.net/wd4z1kvs/1/) I am trying:
var myscroll = {};
myscroll.list = document.getElementsByClassName("navbar-right")[0].getElementsByTagName("li");
for( var j=0; j < 5; j++) {
myscroll.list[j].anchor = document.getElementsByClassName("navbar-right")[0].getElementsByTagName("li")[j].getElementsByTagName("a")[0];
myscroll.list[j].anchor.addEventListener("click", function() {
alert(j);
myscroll.list[1].anchor.innerHTML = "hello" + j;
myscroll.list[j].anchor.innerHTML = "yellow" + j;
});
}
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="active">Home</li>
<li>Artists</li>
<li>Songs</li>
<li>Beats</li>
<li>Contact</li>
</ul>
</div>
The problem seems to be that the event works with j's present value. It doesn't take the value of the parameter when the code was actually run.
The simplest solution is to use a forEach, or some other looping callback function:
myscroll.list.forEach(function(item, j) {
item.anchor = document.getElementsByClassName("navbar-right")[0]
.getElementsByTagName("li")[j]
.getElementsByTagName("a")[0];
item.anchor.addEventListener("click", function() {
alert(j);
myscroll.list[1].anchor.innerHTML = "hello" + j;
item.anchor.innerHTML = "yellow" + j;
});
});
Try this:
var myscroll = {};
myscroll.list = document.getElementsByClassName("navbar-right")[0].getElementsByTagName("li");
for( var j=0; j < 5; j++) {
(function(j) {
/* j variable is just a local copy of the j variable from your loop */
myscroll.list[j].anchor = document.getElementsByClassName("navbar-right")[0].getElementsByTagName("li")[j].getElementsByTagName("a")[0];
myscroll.list[j].anchor.addEventListener("click", function() {
alert(j);
myscroll.list[1].anchor.innerHTML = "hello" + j;
myscroll.list[j].anchor.innerHTML = "yellow" + j;
});
}(j));
}
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="active">Home</li>
<li>Artists</li>
<li>Songs</li>
<li>Beats</li>
<li>Contact</li>
</ul>
</div>
As noticed #Andreas there is a closure in a loop.
The events don't remember values, they only keep a link (reference) to
the environment where they were created. In this case, the variable j
happens to live in the environment where the three events were
defined. So, all events, when they need to access the value, reach
back to the environment and find the most current value of j. After
the loop, the j variable's value is 5. So, all five events point to
the same value.
use this:
var myscroll = {};
var list = document.getElementsByClassName("navbar-right")[0].getElementsByTagName("li");
myscroll.list = list;
for (var j = 0; j < 5; j++) {
var anchor = list[j].getElementsByTagName("a")[0];
anchor.setAttribute("index",j);
myscroll.list[j].anchor = anchor;
anchor.addEventListener("click", function () {
alert(this.getAttribute("index"));
this.innerHTML = "hello" + this.getAttribute("index");
//this.innerHTML = "yellow" + this.getAttribute("index");
this.style.backgroundColor = "yellow";
});
}

choose up to x number of items from a list in javascript

I have a list of items where I want the user to be able to select up to three items.
I have the below code.
var listItems = document.querySelectorAll('li');
var clickcnt = 0;
for(var i = 0; i < listItems.length; i++){
listItems[i].addEventListener('click', function(event) {
if(this.hasAttribute('class')){
var test = document.getElementsByClassName('clicked');
this.classList.remove('clicked');
clickcnt--;
}
else if(clickcnt < 3) {
this.classList.add('clicked');
clickcnt++;
}
console.log(clickcnt);
});
}
.clicked {
background-color:red;
}
<p>select up to 3 of your favourite items below.</p>
<ul id="mylist">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
<li>item 5</li>
</ul>
The issue I am having is that I am checking to see if the item has a class if so I am reducing the click count. But it keeps subtracting the items so if I for example click on item 1 4 times in a row I am then able to select the other four.
How can I set this up so the user can keep clicking on an item until they have a max of 3 selected but if they click on the same one twice it removes the item so that it is at its intial state?
var listItems = document.querySelectorAll('li');
var toggleClicked = function(event) {
var _class = 'clicked';
if (-1 < this.className.indexOf(_class)) {
this.classList.remove(_class);
} else if (3 > document.getElementsByClassName(_class).length) {
this.classList.add(_class);
} else {
console.warn('Oops !');
}
console.log('No. elements:', document.getElementsByClassName(_class).length, 'with class:', _class);
};
for (var i = 0; i < listItems.length; i++) {
listItems[i].addEventListener('click', toggleClicked);
}
.clicked {
background-color: red;
}
<p>select up to 3 of your favourite items below.</p>
<ul id="mylist">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
<li>item 5</li>
</ul>
Try this - > http://jsfiddle.net/sumeetp1991/nybf6kk6/
var listItems = document.querySelectorAll('li');
var clickcnt = 0;
for(var i = 0; i < listItems.length; i++){
listItems[i].addEventListener('click', function(event) {
if(this.className){
this.className = "";
clickcnt--;
}
else if(clickcnt < 3) {
this.className = "clicked";
clickcnt++;
}
console.log(clickcnt);
});
}
Like that?
var listItems = document.querySelectorAll('li');
var clickcnt = 0;
var myList = document.querySelector('#mylist');
myList.addEventListener('click', function(event) {
var itemClassList, selected, canAdd;
itemClassList = event.target.classList;
canAdd = clickcnt < 3;
if (itemClassList.contains('clicked')) {
itemClassList.remove('clicked');
clickcnt -= 1;
} else if (canAdd) {
itemClassList.add('clicked');
clickcnt += 1;
}
});
.clicked {
background-color:red;
}
<p>select up to 3 of your favourite items below.</p>
<ul id="mylist">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
<li>item 5</li>
</ul>
You can use clickcnt = Math.max(clickcnt - 1, 0); - this picks the maximum value of the two values. If clickcnt - 1 is below 0 it just returns 0 instead. See MDN reference.
Your checking whether there is a class or not will fail though after adding one class and remove it, as it doesn't remove the attribute, just the class. If you're using classList then you can use this.classList.contains('clicked').
See this fiddle.
var listItems = document.querySelectorAll('li');
var clickcnt = 0;
for (var i = 0; i < listItems.length; i++) {
listItems[i].addEventListener('click', function (event) {
if (this.classList.contains('clicked')) {
this.classList.remove('clicked');
clickcnt = Math.max(clickcnt - 1, 0);
} else if (clickcnt < 3) {
this.classList.add('clicked');
clickcnt++;
}
});
}
To clean up a bit you can just use one if and the toggle method from the classList object. See this fiddle:
var listItems = document.querySelectorAll('li');
for (var i = 0; i < listItems.length; i++) {
listItems[i].addEventListener('click', clicked);
}
function clicked() {
var clickCount = document.getElementsByClassName('clicked').length;
var el = this;
if (clickCount >= 3) {
this.classList.remove('clicked');
} else {
this.classList.toggle('clicked');
}
}
To have a global variable that's you can refer to for the click count in other functions you can change the code to:
var listItems = document.querySelectorAll('li');
var clickCount = 0;
for (var i = 0; i < listItems.length; i++) {
listItems[i].addEventListener('click', clicked);
}
function clicked() {
var el = this;
if (clickCount >= 3) {
this.classList.remove('clicked');
} else {
this.classList.toggle('clicked');
}
clickCount = document.getElementsByClassName('clicked').length;
}

Delete <li>'s that have the same value in another <li>

If there were two <ul>'s, one called list_a and the other called list_b, using javascript and not using any libraries like jQuery, how would you delete the <li>'s in list_a that have the same value as those in list_b?
Heres the example HTML:
<ul id="list_a">
<li value="1">list_a_0</li>
<li value="8">list_a_8</li>
<li value="9">list_a_9</li>
</ul>
<ul id="list_b">
<li value="8">list_b_8</li>
<li value="9">list_b_9</li>
<li value="2">list_b_2</li>
</ul>
The end result should be:
<ul id="list_a">
<li value="1">list_a_0</li>
<!-- DELETED TWO <li>'s -->
</ul>
<ul id="list_b">
<li value="8">list_b_8</li>
<li value="9">list_b_9</li>
<li value="2">list_b_2</li>
</ul>
The javascript so far that I can build (that doesn't work) is:
window.onload=function()
{
init();
function init()
{
var listA = document.getElementById("list_a");
for(var i in listA.childNodes)
{
var x = listA.childNodes[i];
var listB = document.getElementById("list_b");
for(var j in listB.childNodes)
{
var y = listB.childNodes[j];
if(x.innerHTML == y.innerHTML)
listA.removeChild(listA);
}
}
}
}
Thanks!
DEMO: http://jsfiddle.net/rmXrZ/
window.onload = function() {
var listA = document.getElementById("list_a");
var listB = document.getElementById("list_b");
for (var i = 0; i < listA.children.length; i++) {
var x = listA.children[i];
for (var j = 0; j < listB.children.length; j++) {
var y = listB.children[j];
if (x.value == y.value) {
listA.removeChild(x);
i--;
}
}
}
}
Don't use for-in for iteration of numeric indices
Cache the DOM selection instead of re-selecting in the loop
Use .children instead of .childNodes to avoid text nodes between elements
Compare .value instead of .innerHTML
Remove x instead of listA
When an element is removed from listA, decrement i, because removal from the DOM means removal from the .children collection.
function init() {
var listA = document.getElementById("list_a");
var listB = document.getElementById("list_b");
for(var i =0; i<listA.children.length;i++) {
var x = listA.children[i];
for(var j in listB.children) {
var y = listB.children[j];
if(x.value == y.value)
x.parentNode.removeChild(x);
}
}
}
Avoid hitting the DOM on multiple occasions, also in this case children is a better choice of data.

Categories