Finding an element by its class name is an efficient yet convenient way to find many elements on a page. Despite its simplicity, many beginners make mistakes when using it. It may look like you’re doing everything right, but the NoSuchElementException error tells you that you’re doing something wrong.
By.CLASS_NAME locator strategy
To begin with, let me remind you that in order to use any strategy for finding elements, you need to import the class By
from selenium.webdriver.common.by import By
The strategy By.CLASS_NAME is designed to locate an element by the value of its class attribute. For example, let’s find a button that has this html code:
<input type="submit" value="Click" class="btn-primary">
To understand whether you can find this button by its class name, you need to make sure that such a selector returns a unique result. To do this, use the search inside the elements tab in the developer tools. Search is enabled with CTRL +f.
When you are sure that you want to find this element by its class name, write the following code
button = driver.find_element(By.CLASS_NAME, 'btn-primary')
After this code is executed, the found element will be stored in the button variable. If your task was to click on this button, then just do
button.click()
If you do not know what driver means in this code, you can read in this post how to create and configure it.
HTML class attribute
The above seems to be quite simple. But there is one feature of the class attribute that can cause unexpected problems. In order not to fall into this trap, we need to know what is stored in this attribute. So, the class attribute does not store the name of the class to which this element belongs, but a list of class names separated by a space.
In the example that we looked at, only one name was specified in the class attribute, so we had no problems with it. Quite often there are situations when more than one name is specified in the class attribute. In such cases, you need to choose which of the class names you are going to search for.
How to avoid the mistake
What is the most common mistake? Attempt to find an element using the entire contents of the class attribute, including spaces. If you see spaces in the class attribute, it means that it contains not one class name, but several. For example, let’s use this automation practice website and look at this input field.
<input type="text" name="text_string" placeholder="Submit me" class="textinput textInput form-control" required="" id="id_text_string">
Yes, we could find this element by id or by name, but imagine that these attributes do not exist and we need to find this field by the name of its class.
Now we know that we need to check if there are spaces in the class names. There are two spaces in this class attribute. This means that it lists 3 class names.
The main feature of the By.CLASS_NAME strategy is that it can be used to find elements using only one of the class names when searching. So this code:
driver.find_element(By.CLASS_NAME, ‘textinput textInput form-control’)
will return
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"._acan _acap _acas"}
You can locate this element using one of these options:
driver.find_element(By.CLASS_NAME, 'textinput')
driver.find_element(By.CLASS_NAME, 'textInput')
driver.find_element(By.CLASS_NAME, 'form-control')
And there is no way to find this element using all three class names when using the By.CLASS_NAME strategy. But what if none of these class names are unique to the page, but it is the combination of these classes that is unique? In this case, you will have to choose a different strategy.
Strategies for locating an element by a combination of class names
There are two strategies by which we can solve this problem. The most relevant one is By.CSS_SELECTOR, which we’ll look at first. Also, the By.XPATH strategy can help us.
By.CSS_SELECTOR
Class names are created specifically to indicate which css rules should be used for an element. Therefore, the By.CSS_SELECTOR strategy will be the most appropriate for this task.
With this strategy, finding an element by all the class names listed in the class attribute would look like this:
driver.find_element(By.CSS_SELECTOR, '.textinput, .textInput, .form-control')
The dot before each class name indicates that we want to search by class name. And, for example, the # symbol would indicate that the search is performed using the element id.
By.XPATH
This strategy is universal for any element. If you were unable to find the element using any other strategy, then the By.XPATH strategy can definitely help you. In order to solve our problem using this strategy, we will create the following code
driver.find_element(By.XPATH, '//a[@class="textinput textInput form-control"]')