I know getElementsByName('something') that returns the elements with name="something", but I want to return a list of elements where custom="something", how would I do that?
There are no standard API in the DOM to do this.
If you do not mind adding jQuery to your project, you could query your elements using the jQuery attribute selector:
$("[custom='something']")
To answer my own question, it seems it was easier than I thought.
elements = document.getElementsByTagName('pre');
for (elem = 0;elem < elements.length;elem++)
{
element = elements[elem];
if (element.lang != 'php')
break;
...
}
The above happened to work in my situation. :)
This page lists all the functions of the Document object in the JavaScript available in browsers. Thus it provides getElementById(), getElementByName() and getElementByTagName().
I guess need to use something like JQuery to gain more freedom as it allows you to express more complex "queries". I'm not sure, but that might also be slower, depending on how often you have to look up things.
Related
I was quite sure that I already did this in some earlier version of jQuery, but http://api.jquery.com/category/traversing/ seems to suggest otherwise.
What I'm looking for is similar kind of the opposite of .addBack() - a traversing function that uses "all other" elements (not .not()!)
Preusdo Example:
$('.some-class li').slice(33,55).hide().allOthers().show()
Edit: This is not actually a hide() / show() based problem, this is just a simple example to clarify what I meant.
First, I'ld like to manipulate a set of elements selected with .slice(), and then manipulate all elements that were not selected by .slice().
Is there a handy traversing function I've missed that does just that? I know how to solve it in general, but a ".allOthers()" method that I might have missed would certainly be more handy and clearer.
In your case you can just call show before calling slice
$('.some-class li').show().slice(33,55).hide();
It's true that there is no method to get all others, the closest is to get back they previous collection as you mentioned, http://api.jquery.com/addback/
You could implement a plugin, since I'm on my mobile, I'll just write some straight code
// o(n*m), could be improved
function allOthers(jqObj) {
var current = [].concat(jqObj);
var prev = jqObj.addBack();
return prev.filter(function(obj){
return !current.includes(obj);
});
}
First show all of them and then hide from 33 to 55, here is the demo
$('.some-class li').show().slice(33,55).hide();
After testing #JuanMendes suggestion, I played around with it a bit and found quite a compact way to implement this kind of functionality, due to jQuery's prevObject:
$.fn.others = function() {
return this.prevObject.not( this );
}
I didn't test it too much with other methods, so it might needs some further changes - but it seems to work fine with .slice() at least.
https://jsfiddle.net/1L3db7k4/
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Performance of jQuery selector with context
In the jQuery DOCS it says
By default, selectors perform their searches within the DOM starting
at the document root. However, an alternate context can be given for
the search by using the optional second parameter to the $() function.
Based on that my understanding is that a selection using a context passed in as the second parameter should be faster then the same selection without the context passed in. However I ran some tests and it seems as if this isn't the case, or at least isn't always the case.
To elaborate, I originally wanted to see if searching for multiple elements at once ($("div1, #div2")) was faster then searching for the two separately ($("#div1") $("div2")). I then decided to test it with the context and without to see how much faster it was with the context, but was surprised when it turned out that the context seemed to be slowing it down.
For example given the following basic HTML markup
<div id="testCnt">
<div id="Div0"></div>
<div id="Div1"></div>
<div id="Div2"></div>
<div id="Div3"></div>
<div id="Div4"></div>
<div id="Div5"></div>
<div id="Div6"></div>
<div id="Div7"></div>
<div id="Div8"></div>
<div id="Div9"></div>
</div>
And the following JavaScript (jQuery 1.8.2, and tested using FireBug)
$(function () {
var $dvCnt = $('#testCnt');
var dvCnt = $dvCnt[0];
console.time('Individual without cache');
for (var i = 0; i < 10000; i++) {
$('#Div0').text('Test');
$('#Div1').text('Test');
$('#Div2').text('Test');
$('#Div3').text('Test');
$('#Div4').text('Test');
$('#Div5').text('Test');
$('#Div6').text('Test');
$('#Div7').text('Test');
$('#Div8').text('Test');
$('#Div9').text('Test');
}
console.timeEnd('Individual without cache');
console.time('Individual with $cache');
for (var i = 0; i < 10000; i++) {
$('#Div0', $dvCnt).text('Test');
$('#Div1', $dvCnt).text('Test');
$('#Div2', $dvCnt).text('Test');
$('#Div3', $dvCnt).text('Test');
$('#Div4', $dvCnt).text('Test');
$('#Div5', $dvCnt).text('Test');
$('#Div6', $dvCnt).text('Test');
$('#Div7', $dvCnt).text('Test');
$('#Div8', $dvCnt).text('Test');
$('#Div9', $dvCnt).text('Test');
}
console.timeEnd('Individual with $cache');
console.time('Individual with DOM cache');
for (var i = 0; i < 10000; i++) {
$('#Div0', dvCnt).text('Test');
$('#Div1', dvCnt).text('Test');
$('#Div2', dvCnt).text('Test');
$('#Div3', dvCnt).text('Test');
$('#Div4', dvCnt).text('Test');
$('#Div5', dvCnt).text('Test');
$('#Div6', dvCnt).text('Test');
$('#Div7', dvCnt).text('Test');
$('#Div8', dvCnt).text('Test');
$('#Div9', dvCnt).text('Test');
}
console.timeEnd('Individual with DOM cache');
console.time('Multiple without cache');
for (var i = 0; i < 10000; i++) {
$('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9').text('Test');
}
console.timeEnd('Multiple without cache');
console.time('Multiple with $cache');
for (var i = 0; i < 10000; i++) {
$('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9', $dvCnt).text('Test');
}
console.timeEnd('Multiple with $cache');
console.time('Multiple with DOM cache');
for (var i = 0; i < 10000; i++) {
$('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9', dvCnt).text('Test');
}
console.timeEnd('Multiple with DOM cache');
});
Here's a jsbin
I'm getting something like the following results
Individual without cache: 11490ms
Individual with $cache: 13315ms
Individual with DOM cache: 14487ms
Multiple without cache: 7557ms
Multiple with $cache: 7824ms
Multiple with DOM cache: 8589ms
Can someone shed some insight on whats going on? Specifically why the search is slowing down when the jQuery context is passed in?
EDIT:
Most of the anwsers here (as well as Performance of jQuery selector with context) basically say that that either the DOM in this example is too small to really gain much or that selecting by ID is going to be fast regardless. I understand both points, the main point of my question is why would the context slow down the search, the size of the DOM shouldn't make a difference for that, and neither should the fact that searching by ID is already very fast.
#pebble suggested that the reason that its slower is because jQuery can't use the native browser methods (getElementByID), this seems to make sense to me, but then why is it faster to search for multiple elements in one selection?
Anyway I dumped the tests into a jsPerf adding cases to search by class and was again surprised to see that the search for multiple classes with a cache this time was the fastest.
I would imagine there are lots of situations where using context will slow things down, mainly because jQuery will try and use browser native methods where it can - rather than traverse the entire dom. One example of this would be using document.getElementById as in your example.
why the slow down?
getElementById only exists on the document object - you have no way of using this on a contextual element - i.e. element.getElementById. So my theory would be that jQuery first does the id request using document.getElementById, and then, if there is a context set - scans through the parents of each element to tell if any of them exist as children of the context - thereby slowing the process down.
Other examples of selectors that may be slow
You will also find other places where depending on the selector you are using you will get performance increases - all down to what methods jQuery can use to speed up it's work. For example:
$('.className');
Would most likely translate to using getElementsByClassName or any other native method offered to select by className, However:
$('.className .anotherClassName');
Wouldn't be able to use this (as it has to take the relationship into account) and would have to use a mixture of querySelector (if it exists) and or pure javascript logic to work things out.
Having a good knowledge of what native methods are available will help you optimise your jQuery queries.
Ways to optimise
If you wish to optimise using a context, I would imagine this would prove a faster query than without:
$('div', context);
This will be because getElementsByTagName has existed since the dawn of time for a while, and can be used in pure JavaScript directly on a DOM element. However if you are to do this, it may be quicker to do the following:
$().pushStack( context[0].getElementsByTagName('div') );
or
$( context[0].getElementsByTagName('div') );
Mainly because you cut down on the jQuery function calls, although this is much less succinct. Another thing to be aware of with regards to many of the popular JavaScript environments - calling a function without arguments is a lot faster than calling with.
A relatively unused method for optimising certain jQuery selectors is to use the jQuery eq pseudo selector - this can speed things up in a similar way to using LIMIT 0,1 in SQL queries - for example:
$('h2 > a');
Would scan inside all H2s looking for A elements, however if you know from the start that there is only ever going to be one A tag within your H2s you can do this:
$('h2 > a:eq(0)');
Plus if you know there is only ever going to be one H2 - the logic is the same:
$('h2:eq(0) > a:eq(0)');
The difference between $().pushStack and $().add
In response to Jasper's comment here is the difference between the two functions:
.add:
function (a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?
[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?
d:p.unique(d))}
.pushStack:
function (a,b,c){var d=p.merge(this.constructor(),a);return
d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector
+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d}
The major difference is that .add() uses .pushStack() to acheive it's goals - add allows support for a lot more data types - even jQuery objects. Whereas .pushStack is only designed for DOM Elements, which makes it more optimal if that is what you are using :)
A quicker way to select by ID?
This is obvious, but I thought I'd put this here as sometimes things are missed - a quicker way to select an element by id would be to do the following:
$(document.getElementById('id'));
All because there is no way jQuery/Sizzle can out-do a native method, and it also means you avoid any string parsing on jQuery/Sizzle's part. It's no where near as neat as it's jQuery counterpart though, and probably wont gain that much speed increase, but it is worth mentioning as an optimisation. You could do the following if you were to use ids often.
jQuery.byid = function(id){
return jQuery(document.getElementById(id))
};
$.byid('elementid');
The above would be slightly slower that my previous example, but should still out-do jQuery.
Since you are selecting by ID, jQuery (or sizzle, i forget) is skipping ahead to the faster document.getElementById() in this case. You may get different results when using classes, however even then it may vary by browser.
You could make your testing easier using something like http://jsperf.com/
You are not going to benefit with context when you use an id since that is highly optimized in the browser.
With a id you can call out and say hey. A non programming example, you are in a room of people, you yell out a name, the person answers.
Now lets look at context. Lets say you know the name is a mans name so you separate the room into men and women. You than ask the group of men for their name. One extra step for something that is rather easy.
You will benefit when you are looking up specific things like attributes. Something that is harder for the browser to look up and is not highly optimized. Say you are looking for an input that has a specific attribute. It would be better to reference an element you know that contains it so it does not have to search every input on the page.
Now the fun part is the context selector is slower. It is better to use find. Why? It has to deal with the creation of multiple jQuery objects. :)
So instead of
$('.myClass', dvCnt).text('Test');
do
$(dvCnt).find('.myClass').text('Test');
if you are doing multiple look ups, it is better to store the first one into a variable
var myDiv = $(dvCnt)
myDiv.find('.myClass1').text('Test');
myDiv.find('.myClass2').text('Test');
But now with jQuery doing to querySelector, these optimizations are a smaller deal unless you are using the made up jQuery selectors that querySelector does not support. For browsers that do not support querySelector, the context is important.
You seem to be using #elementid attribute to perform the tests.
Remember that an ID in a HTML page is supposed to be Unique. So this will not make a difference if you give it a context or not when searching for ID..
This test might make more sense if you are trying to target elements with classes or the element tag themselves.
$('.mydiv' , $('#innerDiv')) might be faster than $('.mydiv')
I m a JavaScript pure developer i design my own framework when i tested my selectors module i found a very big issue which is performance
in the selectors module i don't do a very complex selector like jquery i do a simple one
my big cause here when i run my selectors in some cases i have to get all elements on the body of the page and have to loop over them to get a specific kind of elements like TD elements for instance , note >>>> dont tell me use getElementsByTagName('TD') cause in my selectors i can make the developer select more than 1 tagName like
getElementsByTagNames('td,tr')
so in that case i have to get all and then loop over and pic only the needed items
i found that way is very performance eater in the other hand jquery have a hilarious speed to select items doesn't jquery do loops also or what so my main question here
how to do a high performed selectors using JavaScript
:)
thanks
doesn't jquery do loops also or what
jQuery is smart enough to use an existing selector library (sizzle.js).
Sizzle is smart enough to let the browser do the work. document.querySelectorAll does the trick.
edit: actually, sizzle.js used to be inherent part of jquery, but is a separate project now
You can still use getElementsByTagName if you do something like this:
function getElementsByTagNames(elements) {
elements = elements.split(",");
var foundElements = [];
for(var i = 0, len = elements.length; i<len; i++) {
foundElements.push(document.getElementsByTagName(elements[i]));
}
return foundElements;
}
Now if you call getElementsByTagNames("tr,div"), an array containing all tr and div elements will be returned.
Hi I would like to do dom selection and manipulation out of the dom.
The goal is to build my widget out of the dom and to insert it in the dom only once it is ready.
My issue is that getElementById is not supported on a document fragment. I also tried createElement and cloneNode, but it does not work either.
I am trying to do that in plain js. I am used to do this with jQuery which handles it nicely. I tried to find the trick in jQuery source, but no success so far...
Olivier
I have done something similar, but not sure if it will meet your needs.
Create a "holding area" such as a plain <span id="spanReserve"></span> or <td id="cellReserve"></td>. Then you can do something like this in JS function:
var holdingArea = document.getElementById('spanReserve');
holdingArea.innerHTML = widgetHTMLValue;
jQuery will try to use getElementById first, and if that doesn't work, it'll then search all the DOM elements using getAttribute("id") until it finds the one you need.
For instance, if you built the following DOM structure that isn't attached to the document and it was assigned to the javascript var widget:
<div id="widget">
<p><strong id="target">Hello</strong>, world!</p>
</div>
You could then do the following:
var target;
// Flatten all child elements in the div
all_elements = widget.getElementsByTagName("*");
for(i=0; i < all_elements.length; i++){
if(all_widget_elements[i].getAttribute("id") === "target"){
target = all_widget_elements[i];
break;
}
}
target.innerHTML = "Goodbye";
If you need more than just searching by ID, I'd suggest installing Sizzle rather than duplicating the Sizzle functionality. Assuming you have the ability to install another library.
Hope this helps!
EDIT:
what about something simple along these lines:
DocumentFragment.prototype.getElementById = function(id) {
for(n in this.childNodes){
if(id == n.id){
return n;
}
}
return null;
}
Why not just use jQuery or the selection API in whatever other lib youre using? AFAIK all the major libs support selection on fragments.
If you wan tto skip a larger lib like jQ/Prototype/Dojo/etc.. then you could jsut use Sizzle - its the selector engine that powers jQ and Dojo and its offered as a standalone. If thats out of the question as well then i suppose you could dive in to the Sizzle source and see whats going on. All in all though it seems like alot of effort to avoid a few 100k with the added probaility that the code you come up with is going to be slower runtime wise than all the work pulled into Sizzle or another open source library.
http://sizzlejs.com/
Oh also... i think (guessing) jQ's trick is that elements are not out of the DOM. I could be wrong but i think when you do something like:
$('<div></div>');
Its actually in the DOM document its just not part of the body/head nodes. Could be totally wrong about that though, its just a guess.
So you got me curious haha. I took a look at sizzle.. than answer is - its not using DOM methods. It seems using an algorithm that compares the various DOMNode properties mapped to types of selectors - unless im missing something... which is entirely possible :-)
However as noted below in comments it seems Sizzle DOES NOT work on DocumentFragments... So back to square one :-)
Modern browsers ( read: not IE ) have the querySelector method in Element API. You can use that to get and element by id within a DocumentFragment.
jQuery uses sizzle.js
What it does on DocumentFragments is: deeply loop through all the elements in the fragment checking if an element's attribute( in your case 'id' ) is the one you're looking for. To my knowledge, sizzle.js uses querySelector too, if available, to speed things up.
If you're looking for cross browser compatibility, which you probably are, you will need to write your own method, or check for the querySelector method.
It sounds like you are doing to right things. Not sure why it is not working out.
// if it is an existing element
var node = document.getElementById("footer").cloneNode(true);
// or if it is a new element use
// document.createElement("div");
// Here you would do manipulation of the element, setAttribute, add children, etc.
node.childNodes[1].childNodes[1].setAttribute("style", "color:#F00; font-size:128px");
document.documentElement.appendChild(node)
You really have two tools to work with, html() and using the normal jQuery manipulation operators on an XML document and then insert it in the DOM.
To create a widget, you can use html():
$('#target').html('<div><span>arbitrarily complex JS</span><input type="text" /></div>');
I assume that's not what you want. Therefore, look at the additional behaviors of the jQuery selector: when passed a second parameter, it can be its own XML fragment, and manipulation can happen on those documents. eg.
$('<div />').append('<span>').find('span').text('arbitrarily complex JS'). etc.
All the operators like append, appendTo, wrap, etc. can work on fragments like this, and then they can be inserted into the DOM.
A word of caution, though: jQuery uses the browser's native functions to manipulate this (as far as I can tell), so you do get different behaviors on different browsers. Make sure to well formed XML. I've even had it reject improperly formed HTML fragments. Worst case, though, go back and use string concatenation and the html() method.
What's the easiest way to find Dom elements with a css selector, without using a library?
function select( selector ) {
return [ /* some magic here please :) */ ]
};
select('body')[0] // body;
select('.foo' ) // [div,td,div,a]
select('a[rel=ajax]') // [a,a,a,a]
This question is purely academical. I'm interested in learning how this is implemented and what the 'snags' are. What would the expected behavior of this function be? ( return array, or return first Dom element, etc ).
In addition to the custom hacks, in recent browsers you can use the native methods defined in the W3C Selectors API Level 1, namely document.querySelector() and document.querySelectorAll():
var cells = document.querySelectorAll("#score > tbody > tr > td:nth-of-type(2)");
These days, doing this kind of stuff without a library is madness. However, I assume you want to learn how this stuff works. I would suggest you look into the source of jQuery or one of the other javascript libraries.
With that in mind, the selector function has to include a lot of if/else/else if or switch case statements in order to handle all the different selectors. Example:
function select( selector ) {
if(selector.indexOf('.') > 0) //this might be a css class
return document.getElementsByClassName(selector);
else if(selector.indexOf('#') > 0) // this might be an id
return document.getElementById(selector);
else //this might be a tag name
return document.getElementsByTagName(selector);
//this is not taking all the different cases into account, but you get the idea.
};
Creating a selector engine is no easy task. I would suggest learning from what already exists:
Sizzle (Created by Resig, used in jQuery)
Peppy (Created by James Donaghue)
Sly (Created by Harald Kirschner)
Here is a nice snippet i've used some times. Its really small and neat. It has support for the all common css selectors.
http://www.openjs.com/scripts/dom/css_selector/
No there's no built in way. Essentially, if you decide to go without jQuery, you'll be replicating a buggy version of it in your code.