How can I handle multiple <img> elements in Selenium when all of them have the same id attribute?
I need a way to automatically detect and interact with these images, even though they share the same ID and only the image source (src) is different.
3 Answers
Ideally you would use a CSS selector and specify not only the ID but also the full or partial src, e.g.
img#test[src^='https://stackoverflow.com']
^ an IMG tag
^ that has an ID of 'test'
^ that has a 'src' starting with (^=) the string, 'https://stackoverflow.com'
Common string operators include,
^= starts with
$= ends with
*= contains
There are more listed in the spec, https://www.w3.org/TR/selectors/#overview, and a whole lot more info on CSS selectors in general.
4 Comments
#id selector would only return a single result, similar to how document.getElementById() does. An [id='test'] attribute selector may be required instead.findElement() or .findElements() (and the HTML, of course). document.getElementById() is specifically Element (singular) not Elements (plural). Selenium turns By.Id() into a CSS selector in the background anyway.By.id() but it wasn't clear if #id behaves the sameYeap, that kind of thing occurs when a website uses the same id for different elements multiple times (which really shouldn't be the case, but anyway). Selenium IDs to be unique, so it almost can't distinguish those pictures if you try to use find_element(By.ID) there.
The easiest way to resolve this problem is to totally ignore the ID and just fetch the <img> elements via XPath or CSS. For e.g. if all of them have the same ID but their src is different, you can just get them all like this:
images = driver.find_elements(By.XPATH, "//img[@id='Id']")
for img in images:
print(img.get_attribute("src"))
You can also do the same using CSS:
images = driver.find_elements(By.CSS_SELECTOR, "img#Id")
In the end, you can get through all the list elements and perform any actions you want .
It works this way because find_elements returns all the elements that match, even if the HTML is somewhat broken (like duplicated IDs). So rather than depending on the ID, you're basically handling them as a regular list of elements.
Moreover, if you want to locate an image using its src, this would work most of the time:
for img in images:
if "target_image.png" in img.get_attribute("src"):
img.click()
break
So, yeah - it's better to skip find_element(By.ID) here. Selenium assumes that IDs are unique, and if the page is not following the rules, XPath/CSS is just a safer way to work with.
Since ids are meant to be unique in HTML, many searches by id will stop at the first match. In order to cope with this invalid HTML that you have to work with, a way to do so is to use an attribute selector for id, see here:
driver.findElements(By.cssSelector("[id='yourid']"));
Explanation:
driver.findElementssearches for a plurality of elements matching the criteria givenBy.cssSelectoris treating the received parameter as a CSS selector to search by[id='yourid']is a CSS attribute selector, which looks for tags matching yourid in their id value
After getting the results, which are the matches, you can loop them and differentiate them within each iteration of your loop.
4 Comments
By.cssSelector("[id='yourid']") instead of the much simpler, easier to read By.id('yourid')??? This also doesn't solve the problem OP is having... which is how to distinguish between all the IMG tags with the same ID.
driver.findElements(By.cssSelector("[id='foobar']"));By.id('foobar'). You still find ALL the IMG tags with that ID.