Handling Dynamic Elements in Selenium WebDriver

H

While automating a web application using any automation tool, be it open-source (selenium webdriver / cypress) or commercial (TestComplete / UFT), we always have to identify locators for elements that we want to interact in our tests. This is an ongoing activity, that we mostly have to do whenever there are UI changes in application. So, it is important to create reliable locators that do not break easily – at least not with minor UI changes. Doing so is much easier, when dealing with applications where elements have static ids, css-attributes.

However, it is not the case with modern-day web applications (built with Javascript, jQuery, AJAX, angularJS) – in which we mostly deal with dynamic web elements . Dynamic  elements are basically those elements,. whose ID, name, class or css-attributes are not fixed. They change, whenever you reload the page. This change is mostly session or database driven. So, whenever we want to create selectors for such elements, it is very important to analyze that what remains constant and what changes with every page reload. Once we have that understanding, we will be able to create much reliable locators.

Below are some examples of dynamic elements in real time applications, that change on page reload or new user session.

- className of CTA buttons on hotel listing in booking.com
- className and ID values on flight search page in tajawal.com
- className or CSS-attribute value of inbox elements in gmail

Let’s discuss some of the approaches that can be used to create xpath or css locators for these cases. They might not work in every scenario, but will give you some idea on possible ways to deal with dynamic elements.

1. Locate Element by Absolute XPath
2. Locate Element by Starting, Ending or containing Text
3. Locate Element by Two or more css attributes
4. Locate Element when there are multiple matches
5. Locate Element by reference of a closest stable Element
6. Interact Element By Using sendKeys function

1. Locate Elements By Absolute XPath

Absolute Xpath is one of the easiest approach to resolve dynamic element issues. It does not change with page reloads. However, problem with Absolute XPath locators is that they are very fragile. They are most prone to breakage in case of any UI changes. Therefore, they should only be used as a last option. Below is an example of Absolute XPath.

/html[1]/body[1]/div[11]/div[2]/header[1]/div[1]/div[1]/div[1]/div[1]/div[2]/li[1]/div[1]/span[1]

2. Locate Elements by starting, Ending or containing Text

If the elements have ID or css-attribute values with static text in start, end or in between, then we can write XPath or CSS selectors based on this . Let’s take a look into it one by one.

If element has attribute value where ‘starting-text’ remains constant, then we can utilize CSS ( [attribute^=value] ) and XPath ( starts-with ) functions to create XPath or CSS selectors like below..

// Example HTML //
<button id="downshift-main-jss373"></button> // on 1st page load
<button id="downshift-main-jss981"></button> //  on page reload


// Example CSS and XPath Locators - with static start-text //
[id^="downshift-main-"]  
//button[starts-with(@id, 'downshift-main-')]

Similarly, if element attribute value has static ‘ending-text’ , then we can create selectors by using CSS ( [attribute$=value] ) as shown below.

// Example HTML //
<button id="jss903-downshift-main"></button> // on 1st page load
<button id="jss981-downshift-main"></button> //  on page reload

// Example CSS Locator - with static ending-text //
[id$="downshift-main-"]  

// NOTE: as of now, we do not have XPath functions to match by ending-text.

Finally, if the ending or starting text are not static, but there is some ‘text-in-between’ that is static, then we can use CSS ( [attribute*=value] ) and XPath ( contains ) functions to create XPath or CSS selectors like below..

// Example HTML //
<li role="jss21-user-option-901"></li>  // on 1st page load
<li role="jss53-user-option-873"></li>  // on page reload

// Example CSS & XPath selectors - use containing-text //
[role*="-user-option-"]  // CSS selector using [attribute*=value]
//li[contains(@role, '-user-option-')]  // relative XPath selector using contains function

In addition to above, following CSS selector tricks can also be used in specific scenarios!

  • When we have an element “elm”, with an attribute “attr”, having attribute-value that is “a list of whitespace-separated values”, one of which is exactly equal to “mainXYZ”. CSS-selector for this case will be… elm[attr~="mainXYZ"]
  • When we have an element “elm”, with an attribute “attr”, having attribute-value that is “a hyphen-separated list of values beginning (from the left) with “mainABC”. CSS-selector for this case will be… elm[attr|="mainABC"]

Please note that, all above approaches are for attribute-value and will not work with attribute-name itself. To target attribute by its name, it can be done by like this .. elm[attr] .

3. Locate Elements by two or more css attributes

We can also use two or more attributes-values together to make a unique css-selector . This approach can become really helpful when used in combination with css wildcard approach. Below is an example of it.

//Example HTML //
<input type="radio-22-jss-97" role="point-22" value="1">
<input type="radio-91-jss44" role="point-21" value="2">
<input type="radio-91-jss22" role="point-24" value="3">

will create css selector for 3rd element has type attribute value ending-with “radio-91” and role attribute-value exactly equal to “point-24”.

// Example CSS & XPath locators - using two attributes combined //
input[type^="radio-91"][name="point-24"]
//input[starts-with(@type,'radio-91') and (@role='point-24')]

4. Locate Element when there are multiple matches

If there are multiple elements present on page matching with the same locator, then we can use CSS ( nth-of-type , nth-child ) and XPath index functions to interact with desired element found at a particular index. Below are the CSS and XPath examples.

[id="downshift-main"]:first-child // when multiple matching childs & target first child
[id="downshift-main"]:last-child // when multiple matching childs & target last child
[id="downshift-main"]:nth-child(2) // when multiple matching childs & target 2nd child

[id="downshift-main"]:first-of-type // when multiple matching type & target first match
[id="downshift-main"]:last-of-type // when multiple matching type & target first match
[id="downshift-main"]:nth-of-type(2) // when multiple matching type &  target 2nd match

//button[@id='downshift-main'][2]  // when multiple matches & target 2nd match

You may also interact with element on particular index from multiple matches,using selenium webdriver code as  shown below..

driver.findElements(By.xpath(“//*submit”)).get(2).click();

5. Locate Element By reference of a closest stable Element

If a dynamic element has nothing static and it cannot be located by any of strategies discussed above, then an alternative approach is to locate a nearby element and use it as a reference to interact with dynamic Element. XPath has really powerful functions that can help in doing it. Check below example to understand.

Let’s say, we have a web page that has 5 buttons. Stable locator can be created for only second button. Let’s say it is //button[@id='menu'] . Now, if we want to interact the button coming before or after it, then we can do so by using XPath axes functions . An axis, basically represents a relationship to the context (current) node, and is used to locate nodes relative to that node on the tree. Examples are following, following-sibling, preceding, preceding-sibling, ancestor, descendant and more. Check below XPath examples with description to understand.

this will return all the elements, that come after the button with “id=menu”
//button[@id='menu']/following::*

this will return all the button elements, that come after the button with “id=menu”
//button[@id='menu']/following::button

this will return 3rd button, that comes after button with “id=menu” [5th button on page]
//button[@id='menu']/following::button[3]

this will select 1st button that comes before button with “id=menu” [1st button on page]
//button[@id='menu']/preceding::button[1]

this will select all siblings of element with id=menu
//button[@id='menu']/following-sibling::*

this will select under header, any element with css-attribute “focusable=false”
//header/descendant::*[@focusable='false']

6. Interact Element By Using SENDKEYS function

If you are using selenium webdriver and none of the above approaches work, then in specific scenarios there is workaround to interact with element using sendKeys function .

Lets say, you have have two input fields on a page. You can find locator for one input field, but other input field is completely dynamic then you can first move to first inputField and then use sendKeys(TAB) function to navigate to 2nd inputField.

// to move focus to 2nd field, do sendKeys(TAB), while in field1
driver.findElement(By.id(“field1”)).sendKeys(Keys.TAB));

END NOTE

These are some of the approaches that I have come across and used in test automation projects to handle dynamic and complex element locator scenarios. Have you ever faced similar scenarios while automating? How did you handle it? Have you any suggestions for or for our readers! Please share your thoughts and experiences in comments section.

Arif Masood

Software Quality Professional. Perfectionist. Love to explore new tools, technologlies & processes. Worked on several web & mobile projects in last 7+ years. Mostly in areas of Functional, API, Automation & Performance testing. Ocassional blogger - trying to be more regular one :)

Posts

Tags