Selenium Compound class names not permitted
Asked Answered
B

5

35

I have the below code that clicks on an element to pop up a screen and copy the text in it

el1 = driver.find_element_by_id("keyDev-A")
el1.click()
el2 = driver.find_element_by_class_name("content")
print(el2.text)

However, when I tried to get selenium to click on the button within that popup with

el3 = driver.find_element(By.CLASS_NAME, "action-btn cancel alert-display")
el3.click()

It produces an error message:

invalid selector: Compound class names not permitted

This is the HTML that I am trying to get selenium to click on. The Close button.

<div class="nav">
    <span class="action-btn confirm prompt-display">Confirm</span>
    <span class="action-btn cancel prompt-display">Cancel</span>
    <span class="action-btn cancel alert-display">Close</span>
</div>

How should I be writing el3 in order to click on the Close button?

Brasca answered 12/6, 2016 at 7:1 Comment(3)
Have you seen #10659407 ? It might help.Pivot
Selenium does not support BY.CLASS_NAME with compound class..you need to use cssSelector or XPath to find el3 here...Spinule
Possible duplicate of How to avoid Compound Class name error in Page Object?Gaffe
O
67

Leon's comment leads to the correct information that compound class names are no longer supported. What you could do instead is try using css selectors. In your case, the following line of code should help you get the element you want :

el3 = driver.find_element_by_css_selector(".action-btn.cancel.alert-display")

For newer Selenium Versions

el3 = driver.find_element(By.CSS_SELECTOR, ".action-btn.cancel.alert-display")

It finds the element with all three classes (action-btn, cancel and alert-display) in the class attribute. Do note that the order of the classes does not matter here and any of the classes may appear anywhere in the class attribute. As long as the element has all three classes, it will be selected. If you want the order of the classes to be fixed, you can use the following xpath :

el3 = driver.find_element_by_xpath("//*[@class='action-btn cancel alert-display']") 
Okoka answered 12/6, 2016 at 9:47 Comment(1)
In 2023, this is the best solution, but the API has changed: driver.find_element(By.CSS_SELECTOR, ".action-btn.cancel.alert-display")Trott
F
3

I am late to this question. But I also found a work around by treating the compound classes as a String, using tag_name, and get_attribute('class'), when you are not familiar with Xpath. It needs some more lines of code but it's straight forward and fit for beginners like me.

   elements = driver.find_elements_by_tag_name('Tag Name Here')
        for element in elments:
            className = watchingTable.get_attribute('class')
            print(className)
                if className == 'Your Needed Classname':
                    #Do your things
Fugal answered 27/5, 2018 at 4:27 Comment(1)
After using this way for a short while. I found that it's may not be a good solution. Because when the TagName is 'DIV' or 'SPAN', you will have to go through the whole document each time you try to locate the element which is too heavy. Besides, when the page is loading, you may have to add some Try Except error handles to get rid of the annoying exceptions. Learn and get acquaintance with sagarwadhwa1's answer suggests will be the good practise.Fugal
C
1

The answer here are incorrect :

If you check the exception from the by_class_name:

enter image description here

you can see it is using css under the hood

by_class_name just adds '.' infront of the locator provided so 'a' will be passed as '.a' and a.b will be passed as '.a.b'

So You can use class_name for multiple classes , just need to replace space with '.'

so "a b c" should be passed as "a.b.c"

Eg:

Working example:

from selenium import webdriver

import time

from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://mcmap.net/q/429028/-find_element_by_class_name-in-selenium-giving-error/65579606?noredirect=1#comment115946541_65579606")
time.sleep(5)
elem = driver.find_element_by_class_name('overflow-x-auto.ml-auto.-secondary.grid.ai-center.list-reset.h100')

print(elem.get_attribute("outerHTML"))
Cinemascope answered 5/1, 2021 at 13:51 Comment(0)
P
0

This error message...

invalid selector: Compound class names not permitted

...implies that locator strategies using Compound class names are not valid while using Selenium.

Traces of this change can be confirmed from the Selenium v2.40.0 changelist where the change log mentions about adding proper error code for compound class name usage:

  • Implemented proper error code for the case of invalid css selector empty class name, and compound class name in atoms.

Solution

As an alternative you can use either of the following Locator Strategies:

  • Using CSS_SELECTOR:

    driver.find_element(By.CSS_SELECTOR, "span.action-btn.cancel.alert-display").click()
    
  • Using XPATH:

    driver.find_element(By.XPATH, "//span[@class='action-btn cancel alert-display']").click()
    

References

You can find a couple of relevant detailed discussion in:

Polyhistor answered 30/1, 2021 at 10:33 Comment(0)
E
0

Manually intersect the results for multiple classes.

def find_element_multi_class(driver, classes):
    elements = []
    for i,c in enumerate(classes):
        if i == 0:
            elements = driver.find_elements(By.CLASS_NAME, c)
        else:
            new_elems = driver.find_elements(By.CLASS_NAME, c)
            elements = list(set(elements) & set(new_elems))
    return elements
Eurypterid answered 8/9, 2022 at 13:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.