Im trying to get rid of jquery from my page and rewriting some functionalities to pure js. There are 2 lists with class work, containing few li elements. Each li element should have an action on click to add class 'active' to it. In jquery it is very simple:
$('.work li').on('click', function () {
var that = $(this);
that.parent().find('li.active').removeClass('active');
$(this).addClass('active');
})
Is there a nicer solution in pure js rather than making something like this with nested loops:
var lists = document.getElementsByClassName('work');
for(var i=0; i<lists.length; i++){
var children = lists[i].querySelectorAll('li');
for(var j=0; j<children.length;j++){
children[j].addEventListener();
}
}
Adding event listener to multiple elements To add the event listener to the multiple elements, first we need to access the multiple elements with the same class name or id using document. querySelectorAll() method then we need to loop through each element using the forEach() method and add an event listener to it.
Adding an Event Listener to Multiple Elements Using a for...of Loop + IIFE. Open the console and click any of the buttons. The same event listener is added to each button using a for...of loop along with an immediately invoked function that passes the current element of the loop back into the function.
To add an event listener to all elements with class: Use the document. querySelectorAll() method to select the elements by class. Use the forEach() method to iterate over the collection of elements.
We can add multiple event listeners for different events on the same element. One will not replace or overwrite another. In the example above we add two extra events to the 'button' element, mouseover and mouseout.
There are 2 lists with class work, containing few li elements. Each li element should have an action on click to add class 'active' to it.
You could create that entire functionality by adding an event listener to all li
s returned by the querySelectorAll
. The querySelectorAll
returns a nodeList and not an array, so need to map it in order to iterate it. However, note that we are still iterating the set.
Example Snippet:
var lists = document.querySelectorAll(".work li"),
doSomething = function() {
[].map.call(lists, function(elem) { elem.classList.remove("active") });
this.classList.add("active");
};
[].map.call(lists, function(elem) {
elem.addEventListener("click", doSomething, false);
});
li { cursor: pointer; }
.active { background-color: yellow; }
<ul class="work"><li>One</li><li>Two</li><li>Three</li></ul>
<ul class="work"><li>Four</li><li>Five</li><li>Six</li></ul>
In fact you could also use event delegation and add event listener only on the ul
and then use the event.target
to handle your routine. This is better than adding an event listener to each and every li
, in case there are many of them. There will be only as many handlers as ul
s.
Is there a nicer solution in pure js rather than making something like this with nested loops
Not really. You have to iterate over the set of elements anyway. Nicer in terms of jazzy, yes. Nicer in terms of avoiding the loops, no. Nicer in terms of efficiency, well yes. Compare the above vanilla javascript code with the jQuery one in your question:
$('.work li').on('click', function () { // internally will iterate and add listener to each li
var that = $(this);
that.parent().find('li.active').removeClass('active'); // go to parent and then find li
$(this).addClass('active');
});
.
querySelectorAll() is quite powerfull tool.
I've made a JSFiddle for you.
Code can still be enough nice and readable:
for (var item of document.querySelectorAll(".work li")) {
item.addEventListener("click", function (evt) {
evt.target.classList.add("active");
}, false);
}
Another way is (if you still want to iterate using forEach):
Array.prototype.forEach.call(document.querySelectorAll(".work li"), function(item) {
item.addEventListener("click", function (evt) {
evt.target.classList.add("active");
}, false);
});
Here (JSFiddle) we are interating through a NodeList returned by querySelectorAll(".work li")
by calling the Array.prototype.forEach method on it.
And here (Jsfiddle) is the full example, that toggles the class:
Array.prototype.forEach.call(document.querySelectorAll(".work li"), function(item) {
item.addEventListener("click", function (evt) {
evt.target.toggleActive = !evt.target.toggleActive;
evt.target.classList[!!evt.target.toggleActive ? 'add' : 'remove']("active");
}, false);
});
And finally if there should be only one active element at a time (Jsfiddle):
var currentActiveElement;
Array.prototype.forEach.call(document.querySelectorAll('.work li'), function(item) {
item.addEventListener('click', function(evt) {
currentActiveElement && currentActiveElement.classList.remove('active');
currentActiveElement = evt.target;
currentActiveElement.classList.add('active');
}, false);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With