A couple questions:
Is a regular javascript loop (to loop through a series of elements) faster/more efficient than using jQuery each() ??
If so, what is the best way to write the following code as a regular javascript loop?
$('div').each(function(){ //... })
Yes, removing the each() will give you slightly better performance. This is how you could write a for loop for a list of elements.
var divs = $('div');
for(var i = 0; i < divs.length; i++){
var thisDiv = divs[i]; // element
var $thisDiv = $(divs[i]); // jquery object
// do stuff
}
var divs = document.getElementsByTagName('div'),
l = divs.length, i, cur;
for(i=0; i<l; i++) {
cur = divs[i];
// do stuff with cur here
}
Please continue on your path to removing jQuery in the name of efficiency. This code is approximately fifty times faster than the jQuery equivalent.
To answer your second question, due to the first already being answered;
I was also interested in this, and I decided to benchmark the two to find the difference using Gabe's example. The answer is, in circumstances of wanting a jQuery object as the final result:
They perform exactly the same.
http://jsperf.com/jquery-each-vs-native-selectors
Firefox actually finds the jQuery version faster, apparently.
Instead of using jquery .each()
$('div').each(function(){ //... })
You can use document.querySelectorAll(), then convert the HTMLCollection into a JavaScript array. Then you can map over the array of elements.
const elems = document.querySelectorAll('.my-elements')
const $elems = [].slice.call(elems)
$elems.map( (elem) => { console.log(elem) })
Related
I have my own implementation of dropdown list. I am storing all list identifiers in the global array and on window click event I am iterating through this array and deciding which list I have to hide.
I need to check if element has ancestor with .active class.
jQuery version:
for (var i = 0; i < window.dropdowns.length; i++) {
var e = window.dropdowns[i];
if ($(event.target).closest('.active').length == 0) {
e.hideList();
}
}
Pure javascript version:
for (var i = 0; i < window.dropdowns.length; i++) {
var e = window.dropdowns[i];
var parent = event.target.parentElement;
while (parent.tagName != 'BODY') {
if (parent.className.indexOf('active') > 0) {
e.hideList();
break;
}
parent = parent.parentElement;
}
}
So, what version of this will be faster? And how performance depends on number of elements on the page?
If you just want to know what's fastest, write a simple test to find out. If you want to know why...
jQuery is going to do more work than you need, for example, you are only testing tagName and className.
Having said that, your code could give you false positives because className.indexOf('active') will return > -1 if the element has a class of notactive. Use classList instead.
Finally, if you are already using jQuery, you should use it since we just showed you that code we write ourselves can be buggy, and performance is not likely to matter in this case.
Remember, premature optimization is the root of a lot of spaghetti code.
If you were interested in knowing hard numbers, you could turn your loops into functions and use console.time and console.timeEnd on them. Something like this:
//First while loop
console.time(firstWhileLoopInFunction)
console.timeEnd(firstWhileLoopInFunction)
//Second while loop
console.time(secondWhileLoopInFunction)
console.timeEnd(secondWhileLoopInFunction)
I have a website I am working on that takes input from the user and calculates a results. All of the forms are html text input forms where they enter in certain number.
I want to use the input event to check for when the user enters a new value in one of these 6 text forms. The example in the book I am using, JavaScript and JQuery: Interactive Front-End Web Development, suggest to use the getElementById method with the dom event handler to do this:
For example:
function doWhatIwantToDo()
{
//Do something
}
var el = document.getElementById('username');
el.oninput = doWhatIwantToDo;
This is great and I could set up 6 unique ids for each text form, which I will need to do anyway in order to change their inner html in my javascript code, but is there someway I can check for input by using a class name?
I tried using getElementsByClassName but it is tripping me up because it returns an array of objects.
I want to avoid any jquery solutions right now because I am trying to learn vanilla javascript only right now.
Edit/Results:
I like "acbabis" and "mohamedrias" answers but the book implies that using Event Listeners is a newer method and not supported by all browsers. So for now, I would like to stick with the Traditional Dom Event Handlers that it talks about.
"dandavis" answer to just do it in a loop made me realize that perhaps binding an element to an event handler, in a loop, SHOULD actually work and that perhaps I was making a mistake in my loop.
I checked and stupidly I wasn't using array notation to loop through each object in the returned array which is why nothing was happening. Here is my final code that works:
var test = document.getElementsByClassName("test");
for (var i=0; i < test.length; i++)
{
console.log( test[i] );
test[i].onclick = testiness;
}
function testiness ()
{
alert("Success!");
}
var inputElements = document.querySelectorAll('.className');
for(var i = 0, len = inputElements.length ; i < len ; i++) {
inputElements[i].addEventListener('input', doSomethingFunction);
}
querySelectorAll returns you a static NodeList. You need to iterate and attach the event.
Instead of using getElementsByClassName which returns a live HTMLCollection, working with querySelectorAll would be preferrable.
I would document.querySelectorAll for this.
var inputs = document.querySelectorAll('input');
for(var i = 0; i < inputs.length; i++) {
inputs[i].addEventListener('input', doWhatIWantToDo);
}
This will find all input elements on the page and add the handler to each one. If you want to use a class, just add it to the desired input tags, and replace document.querySelectorAll('input') with document.querySelectorAll('input.classname').
I'm trying to highlight rows in a table using jQuery, but I'm wondering if it's possible to use a variable for the row I want highlighted. This is the code I have now, which is not working.
var rowNumber = 3 //I want to use a loop, but for testing purposes I have it set to 3
$('tr:eq(rowNumber)').addClass('highlight');
Sure, why not. You may pass a variable in :eq() selector:
$("tr:eq(" + rowNumber + ")").addClass("highlight");
or use eq() method instead:
$("tr").eq(rowNumber).addClass("highlight");
$('tr').eq(rowNumber).addClass('highlight');
Should work for you.
Let me first address isolated access (i.e. not taking into consideration optimisation for loops)
Best solution: Use .eq() (fast, nice and short)
You could try something like
$('tr').eq(rowNumber).addClass('highlight');
Explanation: .eq(index) Reduces the set of matched elements to the one at the specified index.
Source: http://api.jquery.com/eq/
Alternative solution: Use the ":eq(index)" selector (unnecessarily slower, more verbose and convoluted)
$("tr:eq("+rowNumber+")").addClass('highlight');
A third solution: (fast, but more verbose than the proposed solution)
$($('tr').get(rowNumber)).addClass('highlight');
How this one works: $('tr').get(rowNumber) gets the (rowNumber+1)th DOM element matching the query selector and then this is wrapped in jQuery goodness using the surrounding $( ).
More info at: http://api.jquery.com/get/
Feel free to experiment with the accompanying jsFiddle:
http://jsfiddle.net/FuLJE/
If you are particularly performance conscious and are indeed are going to iterate through an array you can do this instead:
var trs = $('tr').get(); //get without arguments return the entire array of matched DOM elements
var rowNumber, len = trs.length;
for(rowNumber = 0; rowNumber < len; rowNumber++) {
var $tr = $(trs[rowNumber]);
//various stuff here
$tr.addClass('highlight');
//more stuff here
}
Of course you could also use .each()
$("tr").each(function (rowNumber, tr) {
var $tr = $(tr);
//various stuff here
$tr.addClass('highlight');
//more stuff here
})
Documentation can be found here: http://api.jquery.com/each/
Just to point out the obvious: $("tr").addClass('highlight') would work if adding the highlight class to all tr was all that the OP wanted to do :-)
Using jQuery I can easily get the number of DOM elements used by a web page:
$('*').length;
But not all web sites are using jQuery.
So my question is: How do I get the number of DOM elements used in a web page using pure JavaScript and js console.
Assuming you mean "HTMLElementNodes" as opposed to "All nodes" (which would include such things as text nodes and also be skipped by your jQuery example) then:
document.getElementsByTagName('*').length
This still requires the use of DOM though. Pure JavaScript can't interact with an HTML document other than as a string of text.
It's quite simple really:
document.getElementsByTagName('*').length
If you can ignore older browsers, you could also preserve some of the jQuery-like syntax with:
var $;
$ = document.querySelectorAll.bind(document);
$('*').length;
This question is the top result on google for "js count all dom nodes" but it's 2021 now and we have ShadowDOM so none of these previous answers will actually give an accurate result.
Better to use this function which is based on the code used by Lighthouse to calculate the true DOM size.
function countNodes(element = document.body) {
let count = 0; let child = element.firstElementChild;
while (child) { count += countNodes(child);
if (child.shadowRoot) { count += countNodes(child.shadowRoot); }
child = child.nextElementSibling; count++;
} return count;
}
Using a recursive function countChildrenNumber:
function countChildrenNumber(el) {
let result = 0
if (el.children && el.children.length > 0) {
result = result + el.children.length
for (let i = 0; i < el.children.length; i++) {
result = result + countChildrenNumber(el.children[i])
}
}
return result
}
then call it by passing document as the parameter
countChildrenNumber(document)
The main answer doesn't really count everything (I think shadow DOM would be excluded)
Using snippet below works better for me:
$$('*').length
Today i'm very stack with a Work and jQ. I was get a morning for it but i can't resolve it :(.
My Work here:
<div class="container">
<p class="test">a</p>
<div>
<p class="test">a</p>
</div>
</div>
In normal, i can using jQ with each function for select all <p class="test">a</p> EX:
$(".test").each(function() {
$(this).text('a');
});
But i hear everyone talk that, for function get a less timeload than each function. Now i want using for instead of each.. but i don't know how to write code jQ in this case.
Somebody can help me!. thankyou!
I wouldn't worry about it unless you were iterating through hundreds of them.
for loop is usually used with normal DOM (aka without jQuery) traversing, like...
var elements = document.getElementById('something').getElementsByTagName('a');
var elementsLength = elements.length;
for (var i = 0; i < elementsLength; i++) {
elements[i].style.color = 'red';
}
Caching of elementsLength is a good idea so it is not calculated every iteration. Thanks to CMS for this suggestion in the comments.
Just adapt that for your jQuery object if you wanted to do it with jQuery.
Replace elements variable with your jQuery collection, like $('#something a'). I think you may need to rewrap the object if you need to do any more jQuery stuff with it.
One thing to watch out for is that using an ordinal accessor on the result of a jQuery selection will return a native DomElement. If you want to use jQuery methods on them, you have to re-wrap them:
var testElements = $('.test');
for (var i = 0; i < testElements.length; i++) {
// Using $() to re-wrap the element.
$(testElements[i]).text('a');
}
I'd second what others have said though. Unless you're dealing with many elements, this is premature optimization. Re-wrapping the elements to use the .text() method may even bring it back to no gain at all.
have you tried the obvious solution?
var nodes = $(".test");
for(var i = 0; i < nodes.length; i++)
{
var node = nodes[i];
}
This article shows that each() has no significant performance penalty until you get into the hundreds of thousands of looped-over items.
Another alternative:
for (var i = 0; i < $('.test').length; i++){
var element = $('.test').eq(i);
}