Selecting the proper HTML Element - javascript

I use Nightwatch.js to test foreign website code. I used this command:
.waitForElementVisible('input[id="inputField"]', TIMEOUT)
This should wait until the specified element is visible. But I get this warning:
Warn: WaitForElement found 24 elements for selector "input[id="inputField"]". Only the first one will be checked.
I thought the id of a tag is unique. How is it possible to get a list of 24 elements when looking for this id?
What can I do now to select exactly the element I need?

How is it possible to get a list of 24 elements when looking for this id?
Because people constantly fail to understand the basic concept that ids must be unique. Apparently, the site you're testing against was written by one or more of those people.
What can I do now to select exactly the element I need?
According to the documentation, Nightwatch.js lets you use XPath as an alternative to CSS. With XPath, you can specify which of a set of elements to target, e.g.:
.useXpath()
.waitForElementVisible('//input[#id="inputField"][1]', TIMEOUT)
...would use the first, [2] would use the second, etc.
If you can't do it by the index of the element in the document, you'll need to find other things about it you can use to select it (with CSS or XPath).

The id of an element should be unique. However that doesn't take out the ability for a developer to create as many elements with the same id on the page as he wants.
Also, id selectors are usually specified like this input#inputField.

I could now solve the problem by selecting an unambiguous parent element and then selected the child of the child of it; something like this:
div[id="listItem_0"] > span > input[id="inputField"]

Related

Select second element by custom data/attirbute

OK. I'm trying to get elements in javascript by CSS selector. To get one by custom element I know is like 'element[custom-name="Literal name"]'.
OK. This works. But now I need to get the second one. I mean I have some elements with the exact custom-name, and for everyone I need to apply a diferent rule (tehere are only 5).
How can I select the other ones? Is posibble select them by CSS?
PS: They are located in random positions, so maybe the first one is the 5 element one time and if I refresh the page it can be the 10 element inside the container.
PS2: No, It's not possible to change the HTML in my case :( . The only code I'm alowed to change is CSS and javascript.
Thanks for reading me! :D
Assuming you can't select specific ones by another, non-order-dependent factor, you can use the pseudo-selector :nth-child. In your case, the complete CSS selector would be element[custom-name="Literal name"]:nth-child(2) - substitute the 2 for any other number as you see fit. Generally it's not the best idea to select only by position in the document, as position may change more often than attributes - but in any case, there's a pure CSS solution!
Note that this only works if the elements you're working with are the only children of a common parent element - if you're looking for the second element that matches that query in general across the entire document, there is no way to do that with a CSS selector. Instead, you can make sure to add a unique class or other differentiating attribute to each element, or simply use querySelectorAll - in that case, you could get the second element using this little snippet: document.querySelectorAll('element[custom-name="Literal name"]')[1].

How can I direct Javascript to access the second element with a non-unique ID

I am trying to declare a paired list in my automation framework, and to do so I pass in two parameters of that list. The first parameter is the DOM id of the "Available" items list, while the second is the DOM id of the "Selected" items list.
var pairedList: newPairedList( "availableItemsListID" , "selectedItemsListID");
In the specific case I'm working on, both the availableItemsListID and the selectedItemsListID happen to have the same ID in the DOM.
The both ids are 'x-fieldset-bwrap', and I have tried the following to indicate the availableItemsListID is the first instance of the id, and the selectedItemsListID is the second instance of the id:
var pairedList: newPairedList( "/x-fieldset-bwrap/[0]" , "/x-fieldset-bwrap/[1]");
It seems to find the availableItemsList however when it attempts to get the selectedItemsList it fails. Does anyone have any suggestions on how to best handle the problem?
Thanks!
You can use document.querySelectorAll to select all elements that match a CSS selector.
document.querySelectorAll("#x-fieldset-bwrap") will match all elements with a id of x-fieldset-bwrap.
If you have the ability to change your system so that it does not generate elements with repeated IDs concurrently within the DOM, I would highly encourage you to do so.
If you can distinguish the first element from the second one somehow, then you can use that difference in your selection. See below:
function display($div) {
console.log($div.html());
}
display($('#unique'));
$('#unique').addClass('firstUnique');
display($('#unique:not(.firstUnique)'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="unique">One</div>
<div id="unique">Two</div>

Why does the jquery selector ("id,id") selects all elements with duplicate IDs

I came across some year old code written by a good developer (yes, I knew him personally) to access all elements having the same id.
$("#choice,#choice")
It returns all elements having the id. But if we use the below
$("#choice")
It returns only the first match, as expected.
After searching for some time, I'm unable to figure out any official links pointing to his technique, as to how it selected all elements with duplicate id.
Can anyone please explain how is this working ?
UPDATE
Please see the question is not about what alternative to use. I'm aware of classSelectors and attributeSelectors and know having duplicate IDs is not recommended, but sometimes you just have to live with years old code the way it is (if you know what I mean).
http://jsbin.com/zodeyexigo/1/edit?html,js,output
If you look at the code of sizzle.js that jQuery uses for selecting elements based on selector you will understand why this happens. It uses following regex to match simple ID, TAG or class selector:
// Easily-parseable/retrievable ID or TAG or CLASS selectors
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
but as the selector in question is $("#ID,#ID") it does not match with the selector and uses querySelectorAll (line no 270 in ref link), which replaces selector to "[id='" + nid + "'] " (line no 297 in ref link) which selects all the elements with matching ID.
However, I agree with the other people in this thread, that it is not good idea to have same ID for multiple elements.
Having 2 elements with the same ID is not valid html according to the W3C specification.
When your CSS selector only has an ID selector (and is not used on a specific context), jQuery uses the native document.getElementById method, which returns only the first element with that ID.
However, in the other two instances, jQuery relies on the Sizzle selector engine (or querySelectorAll, if available), which apparently selects both elements. Results may vary on a per browser basis.
However, you should never have two elements on the same page with the same ID. If you need it for your CSS, use a class instead.
If you absolutely must select by duplicate ID, use an attribute selector:
$('[id="a"]');
Take a look at the fiddle: http://jsfiddle.net/P2j3f/2/
Note: if possible, you should qualify that selector with a tag selector, like this:
$('span[id="a"]');
Having duplicated id on the page making your html not valid . ID is unique identifier for one element on the page (spec). Using classes, that are classify similar elements that's your case and $('.choice') will return set of elements
So in JS Fiddle i have shown an example of what jQuery is doing.
https://jsfiddle.net/akp3a7La/
When you have a
$('#choice,#choice');
It is actually getting all the instances of the objects #choice twice, and then filtering out any duplicates.
in my example i show you how it does that also when you have something like this
$("#choice,li");
Where items are actually your #choice items.
In the Jquery Documentation
https://api.jquery.com/multiple-selector/
it talks about multiple Selectors, which is what i think is happening here, your developer friend is selecting the same ID twice, and it would be returning it twice. as you can only have one input with the same ID once on a page (good html syntax)

How to use common js function for two divs containig elements of identical ids?

I have common jQuery function and two div tags. Both div tags have different names but both containing elements of identical ids now i want to use this common Jquery function for them both?
I have implemented common function but it's not working for both.
Here's link to my jsfiddle -jsfiddle.net/xS7zF/1/
In my jsfiddle there are two div tags namely example1 and example2 and both tags have elements of identical ids. Function is working fine for first div but not for second.
please help me to sort out this.
Yeah, under the hood, jQuery selection on an ID will use the Document.GetElementById() function implemented by the browser, which is really fast, but (i guess depending on the browser) will stop after it finds the first element, since ID's should be unique and no further searching is needed after the first one is found.
For instance, rename the divs with id="eb" to class="eb" and you can still target specific elements using $("#example1 .eb") and $("#example2 .eb")
UPDATE:
Using your new Fiddle I created this: http://jsfiddle.net/xS7zF/5/
I cleaned up a lot of code and hopefully you can see what I have done. I changed all elements that appear twice from id to class. Now, when you attach an event to an element using $(".classname").click(), it attaches to all the elements. In the handler function where you set HTML and do your show()/hide(), you don't target a specific element using it's ID, but you find it relative to the element that does the event. You can do this using parent(), parentsUntil(), next(), find(), etc. Check jQuery docs for all possibilities. So for instance, the change-handler attaches to all inputs with name=Assets. But instead of doing $("#b1").show(), I go to the parent of the specific input that fires using $(this).parent(). Then I find the element with a class=".b1", which it will only find the one that is next to this specific input and I set the HTML to just that element.
Since there is another input, the same actions happen when THAT input changes, but instead it finds IT's parent, and finds the element with class=".b1" that is next to IT. So both divs with input are contained since they act on elements relative to itself and not across the document.
For extra fun and to show you how flexible this way of programming is, here is a fiddle with the Javascript-code unchanged, but with the exact same question-div copied 8 times. No matter how many times you repeat this, the same code will act on as many divs as you create since everything works relative. http://jsfiddle.net/xS7zF/7/
Hopefully this helps, the rest is up to you!
ID's must be unique, you should not repeat them. You could replace id with class and in the jQuery function do (".ub").each() or manually referencing the object using eq(x). e.g. (".ub").eq(1).
You shouldn't assign same id's to different elements.
You CAN but you SHOULDN'T. Instead of giving the same id, use class
IDs must be unique, try fix this, change to classes.
You can try something like this:
$("div div:first-child")
instead of
$("#eb")
But depends of the rest of your page code. So, change to classes first and use
$(".eb")
when jQuery / javascript find the first ID it would ignore the rest, please read more about it
http://www.w3schools.com/tags/att_global_id.asp

Help me with my selector, the ID is dynamically changing every page load

I want to scan a website using jQuery, but the ID is constantly changing, but there's a permanent pattern for the ID that I'm searching for:
app7019261521_the_coinb4678bc2
app7019261521_the_coind42fgr23
app7019261521_the_coing0992gvb
app7019261521_the_coin12e5d0aa
The IDs always starts with app7019261521_the_coin
But my problem is I don't know how to put that in jQuery selector.
$("#app7019261521_the_coin")
Doesn't seem to work
So how can I make this work?
$("[id^=app7019261521_the_coin]")
Should work - but its MUCH slower selector than knowing the real ID - or assigning a class. This selector will scan every element on the page one at a time, there is no good way for this selector to be optimizied. 9 times out of 10 though you could build a better selector: Is this #app7019... element the direct child of another element that is easier to determine? like a id='container'?
$("#conainter > [id^=app7019261521_the_coin]"); for instance
From the jQuery Selector Documentation
[attribute^=value] Returns: Array<Element(s)>
Matches elements that have the specified attribute and it starts
with a certain value.
can you set a class and just call it by a class name?
you may also be able to try
$("div[id^=app7019261521_the_coin]")
This will find all div's that start with app7019261521_the_coin
Replace div with whatever element type you are searching for.
$j('div[id^=app7019261521_the_coin]')
Remember this is not very optimal, as it causes the script to check the id attribute of every matched element.
You might want to see how you can add a class to the element or at least find the parent element first and traverse from there.

Categories