I am a novice and trying to design both relative CSS selectors and JSPath to run my automation scripts.
While on the way, I could find the return statements are different between these two. Please check the below example. Could someone tell what changes I need to make in JSPath to keep the results same as relative CSS selectors.
Result of Relative CSS selector.
Result of Relative JSPath.
JS just returns first element while the css selectors returned multiple above. What changes do I need to make in JS to keep the results same.
Document.querySelector()
The Document method querySelector() returns the first WebElement within the document that matches the specified selector, or group of selectors. If no matches are found, null is returned.
Note: The matching is done using depth-first pre-order traversal of
the document's nodes starting with the first element in the document's
markup and iterating through sequential nodes by order of the number
of child nodes.
Syntax:
element = document.querySelector(selectors);
Document.querySelectorAll()
The Document method querySelectorAll() returns a static NodeList representing a list of the document's elements that match the specified group of selectors.
Note: Although NodeList is not an Array, it is possible to
iterate over it with forEach(). It can also be converted to a real
Array using Array.from(). However, some older browsers have not implemented NodeList.forEach() nor Array.from(). This can
be circumvented by using Array.prototype.forEach().
Syntax:
elementList = parentNode.querySelectorAll(selectors);
Using CssSelector
While using a xpath or a css-selectors would return a list of the document's elements that match the specified group of selectors.
Conclusion
As discussed above, when you use querySelector() only the first matching element is returned. If your usecase is to return all the matching element, you need to use querySelectorAll() as:
document.querySelector("div[slot^='Learner']>div>div>span")
You have to use
document.querySelectorAll();
Related
In my code I am trying to get all classes which begin with priceList_ into an array for later use. the code I used to do that is: var prefix = document.querySelector('[class^="priceList_"]').className;
There is one problem I am having with this code, which is that it only gives the first class with that prefix instead of giving an array of classes. Does anyone know the solution to this problem?
You need to be using document.querySelectorAll, document.querySelector purpose is to find the first element on the page matching the condition.
Just to add
document.querySelectorAll('[class^="priceList_"]').className
will error as the returned object is an NodeList of Nodes, not a singular Node (from which you'd be able to get the classe's).
If you wanted to obtain a structured array of each elements classe's then do the below
const classes = [...document.querySelectorAll('[class^="priceList_"]')].map(elem => elem.className);
This will assign an array of classe's (in the order of the DOM elements on the page). You could also do
const classes = [];
for (const elem of document.querySelectorAll('[class^="priceList_"]')) {
classes.push(elem.className);
}
console.log('CLASSES', classes);
querySelector() returns the first result, if you want multiple results use querySelectorAll().
Use querySelectorAll(), it gives NodeList. It is iterable.
Consider using Element.classList instead of className. You can also call classList.entries() to get an iterator.
You can also get an array from className with Element.className.split(" ")
classList on MDN Web Docs
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.
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.
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.
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');