Javascript selector engines and built in functions - javascript

I have found the following tutorial on creating a selector engine..
http://blog.insicdesigns.com/2010/04/creating-your-own-selector-engine/
In javascript we have functions like
getElementById()
getElementsByTageName()
getElementsByName()
etc,.....But for the same functionality,in their selector engine,they are doing checks like
this.nodes[i].tagName == nm.toUpperCase()
instead of getElementsByTagName.What is the advantage of this approach?...
Also what is the usage of assigning all nodes to a vairiable using
e.getElementsByTagName('*');

There is an inconsistency when you get the tagName property of elements. Some browsers return uppercase and others lowercase. To normalize the output of the value, you have to do one or the other before continuing further operation.
As for e.getElementsByTagName('*');, i recently answered a question where the OP wants to find ALL elements containing an attribute name which has a prefix mce_. The only way to get such elements is to get all elements in the DOM, and inspect their attribute names.
There is also a good application of this getElementsByTagName('*') and that is determining the direct child of an element. For instance, in a very deep DOM. If I were to find certain parent elements based on an attribute and get it's children, normally you would do a recursive search from body downwards to find the parent. This would take a lot of recursive operations. Afterwards, you determine their children.
Another way to do it is to get all tags, determine their parent node, and if they have the parent with the attribute, they are the direct child. This method requires no recursion, only getElementsByTagName('*') and a loop through the nodeList that was returned.

this.nodes[i].tagName == nm.toUpperCase() is part of a method to filter the list of nodes by tag name.... so nothing to do with 'getting elements by their tag name'
the last point is not a real question.. you want to know reasons for "why would you select all nodes"? well you are writing a sector engine....

The following line
this.nodes[i].tagName == nm.toUpperCase()
Is within the function ofTag. It's filtering a set of nodes looking for nodes with the given name.
The next line
e.getElementsByTagName('*');
Retrieves all the children/descendants under a node so you can later filter it like the following
new DOMNodes(Selector.getAll(document)).ofTag('p').hasClass('note');

Related

Is it possible to remove an entry from a NodeList without converting it to an array or destroying the DOM node?

Okay, so I'm working on a method that accepts any form of iterable as its argument. I'd like to perform what amounts to a splice on the parameter, but - and this is the crucial bit - without changing its Symbolic Type. That is to say, I'd like the return value's Symbol to match that of the input (NodeList, DOMTokenList, HTMLCollection, etc.)
Contrived Example:
let iterableList = document.querySelectorAll('p');
// Result: Nodelist(5): p, p.body, p.body, p, p
Assumptions
Assume I wish to remove those two <p class="body"> tags (from the COLLECTION, NOT the DOM).
Assume iterableList is user-provided (e.g. I cannot simply alter the query to read p:not(.body))
Assume we do not know what kind of iterator is the input (i.e. in this example, by virtue of the fact it was a querySelectorAll that produced our results, it is a NodeList. Conversely, if it had been a getElementsByTagName it would be a HTMLCollection. Or any of a half dozen others)
Assume the return value should have the same Symbol as the input.
Assume I chose this example because the results are live nodes (and we know the subset could have come from a querySelectorAll('p:not(.body)'), so we know the subset is possible)
1. Now, obviously I can convert the symbol to an Array...
...through any number of means. I could then remove the two offending nodes, again by a variety of tactics. The problem is then my return value is an Array, and I've killed off the live node correspondence within the browser.
2. I could clone just the desired nodes...
...into a document fragment or similar, then re-query the fragment to return just the subset, but then I'd be targeting clones of the DOM nodes in question, not really those themselves, plus it would shake free any event bindings they had once had.
3a. I could insert a DOM node, append the array nodes I wish to keep, then query THAT, then put them back again, and destroy the inserted node...
... which keeps the nodes in tact (replete with any events bound to them) and even auto-remaps their xPaths to their original DOM locations...
3b. ...or even use the unique attributes of each node to attempt to dynamically construct a selector that matched only those I wished to keep...
...but because I don't know the Symbolic Type of iterator the collection is prior to receiving it, I would need a massive collection of such workarounds to conditionally handle each of the possibilities (and that's entirely notwithstanding the fact it'd be a bicycle-tire-with-a-banana solution that I'D never let through code review, either).
So I guess my question is:
Is there a way to modify, selectively clone, or even iteratively construct such an output? I don't need to perform any operation other than REMOVING one or more of the offending nodes; no edits or inserts.
A for...of statement will let one iterate them without conversion to the Array prototype, although I'm not aware of a way to call the Iterator constructors on the fly, even if I could figure out a way to move one of the live nodes from one collection to the other (the spread operator coerces the collection into an array).
Anyone know any good voodoo for this one? Or am I just SOL here?

JavaScript querySelectorAll elements order

I need help with getting elements by querySelectorAll function.
When I call it and I pass a string as parameter of this function with multiple elements separated with commas, it will return me a NodeList with nodes but, they are sort by first-depth traversal pre-order (which should be as they are positioned in document body). And I have a problem when I want to call a method to return values of more elements in document saved in Array. So I call this
$('#textinput, #someotherinput').value();
where element with id #someotherinput is positioned above the element #textinput, but the result after querySelectorAll is this:
[input#someotherinput, input#textinput]
0: input#someotherinput
1: input#textinput
length: 2
__proto__: NodeList
and I need to sort them way I did when I called $(), because when I need to use values, I look just at $('#textinput, #someotherinput') and it's annoying to find out an order of the elements in document body and use returned array with values of those elements when I don't know what is saved in first (0th) position in array. And yes, I know that querySelectorAll is traversal pre-order function. So, how to sort (easy as possible) elements to MY order?
There is no simple way to determine if an element would be matched by a selector. You would have to reimplement the entire rule matching system, so the easiest way is to simply use the selector to query for the elements.
The simplest way to get the elements in the order that you like, would be to split the selector into its subselectors, do a query for each, then put the results together by eliminating duplicate element references.

Equivalent queries => identical JQuery object?

If I have <element id="blah" class="blah" attr="blah"> (edit: and I have NO other elements in the DOM, just this one)
Then are these jQuery objects absolutely equivalent:
$("#blah")
$(".blah")
$("[attr=blah]")
Is it the case that the query is executed when the object is created and after that it is irrelevant what query was used?
Edit: Does the original query used have any impact later on anything I might do with the resulting jQuery object? Are the 3 resulting objects above identical? Are they any lasting effects from the actual query I had used? (e.g., when I do something with that object later)
Each may get the element, however they do it in different ways because they are different selectors.
The id selector #
$("#blah")
This will return a jQuery object with 1 element in it (the element you list). The benefit of using an id is that it will only return your one element if it exists, and is fastest as a result of ids being expected to be unique.
The class selector .
$('.blah')
This will return a jQuery object with an array of elements in it (including the element you lsit), but also with any other element that has this class. Since there is no combination with this selector, it will be slower than a straight id lookup because it must inspect every element on the page for this class.
The attribute selector []
$("[attr=blah]")
Much like the class selector, this will return an array of elements. It also must inspect every element.
These may look the same if there is only one match when a jQuery function call is used. The reason for that happening is jQuery will look to see if there is an array of elements matched, and then internally use $.each on the set to apply the jQuery function call to them. The benefit is that this makes sets of elements responds very similarly to single elements which are wrapped by the jQuery object.
Here is a whole list of selectors jQuery supports:
http://api.jquery.com/category/selectors/
The element will match all three selectors, but it does not make the three selectors absolutely equivalent because they all have different matching criteria (one looks for an ID, one looks for a class name, and one looks for an arbitrary attribute). In particular, the class and attribute selectors can return more than one element, since unlike an ID selector they do not imply uniqueness of an element.
Even if you can guarantee that this element will be the only one matched by all three selectors, every call to $ always yields a unique jQuery object, even if the resulting jQuery objects encapsulate the same DOM element.
The query returns an array of html elements. In this specific case all queries return the same array. There is no way the original query will have an effect later.

Remove all the DOM elements with a specific tag name in Javascript

How can I remove all the elements with a specific tag name using Javascript. For example, I did the following:
var els = document.getElementsByTagName("center");
and it returned an array of all the center elements. How may I remove all these elements?
Coming from Remove all child elements of a DOM node in JavaScript and JavaScript DOM remove element I know that I can loop through els, find the parent of each element and then remove that specific node. But is there anyother way provided by javascript. Like we can do $('center').remove() in jquery and it removes all the elements with center tag. Anything similar to that in Javascript?
With the mention that you still loop over the elements (there is no way to avoid that), you can do this:
Array.prototype.slice.call(document.getElementsByTagName('center')).forEach(
function(item) {
item.remove();
// or item.parentNode.removeChild(item); for older browsers (Edge-)
});
DEMO: http://jsbin.com/OtOyUVE/1/edit
Some notes on slice:
document.getElementsByTagName doesn't return an array, it returns a live list with a length
property. That is why it is needed to first convert it into an array (Array.prototype.slice does that for any object with the length property). By doing that you eliminate the problem of the list being live (gets updated when you change the DOM) and you also get the other array functions to work (like forEach) which have a more functional syntax for looping.
"it returned an array of all the center elements."
Well, it returned an array-like object (a live NodeList or an HTMLCollection depending on the browser).
"How may I remove all these elements?"
You have to loop through the list removing them one at a time as you mentioned later in your question. If you find yourself doing that a lot, encapsulate the looping inside a function so that you don't have to repeat it.
"we can do $('center').remove() in jquery and it removes all the elements with center tag. Anything similar to that in Javascript?"
jQuery is a collection of JavaScript functions, so it can't do anything JavaScript can't do. jQuery's .remove() method (like most other jQuery methods) will loop internally, it just saves you having to think about it. So that comes back to what I already mentioned above, encapsulate the loop/remove code in a function so that you can use it from any part of your code.

Dojo restricting queries?

I was reading the dojo query tutorial and saw
// retrieve an array of nodes with the class name "odd"
// from the first list using a selector
var odds1 = query("#list .odd");
// retrieve an array of nodes with the class name "odd"
// from the first list using a DOM node
var odds2 = query(".odd", document.getElementById("list"));
and they explain that odds2 is faster than odds1 because odds2 searches for .odd in the #list dom rather than the whole html dom. What I'm wondering is what are the advantages of doing odds1 (other than cleaner code, i guess)? Because it seems to me for any case where query is searching for objects within an id element the odds2 style should always be used (assuming proper id, class html use), so why doesn't dojo automatically parse the query string in odds1 to call odds2?
Well looking at the code (http://svn.dojotoolkit.org/src/dojo/trunk/query.js for query and http://svn.dojotoolkit.org/src/dojo/trunk/selector/acme.js the default selector engine) it appears that the "big" performance improvements comes from the fact that the initial DOMNode List is reduced when you give the query method some help with the document.getElementById("list"), however it appears you can just pass the query method a string of the parent node's id and achieve the same performance.
query(".odd", "list");
Then there is the fact that you can cache the DOMNode list by storing the result of document.getElementById("list") in a variable and reuse it. However in general readability (in matters that are this trivial) tends to trump performance. Considering the number of problems that a bad JavaScript interpreter can hide, having readable code can end up saving you a lot of trouble.

Categories

Resources