Nro
Nro

Reputation: 349

How to locate an element by class name and its text in python selenium

Hi I am trying to locate an element by its class name and the text that it contains

<div class="fc-day-number">15</div>

there are a bunch of fc-day-number on the page with different values, I need the one with for example 15.

I do

driver.find_element_by_class_name("fc-day-content") 

but I also need it to be equal to 15 and I am stuck here, please help.

Upvotes: 12

Views: 45419

Answers (3)

Louis
Louis

Reputation: 151491

For things like this, prefer to use JavaScript:

els = driver.execute_script("""
return Array.prototype.slice.call(document.getElementsByClassName("fc-day-content"))
    .filter(function (x) { return x.textContent === "15"; });
""")
assert len(els) == 1
el = els[0]

What this does is get all elements that have the class fc-day-content as the class. (By the way, your question uses fc-day-content and fc-day-number. Unclear which one you're really looking for but it does not matter in the grand scheme of things.) The call to Array.prototype.slice creates an array from the return value of getElementsByClassName because this method returns an HTMLCollection and thus filter is not available on it. Once we have the array, run a filter to narrow it to the elements that have 15 for text. This array of elements is returned by the JavaScript code. The assert is to make sure we don't unwittingly get more than one element, which would be a surprise. And then the element is extracted from the list.

(If you care about IE compatibility, textContent is not available before IE 9. If you need support for IE 8 or earlier, you might be able to get by with innerText or innerHTML or you could check that the element holds a single text node with value 15.)

I prefer not to do it like TehTris does (find_elements_by_class_name plus a Python loop to find the one with the text) because that method takes 1 round-trip between Selenium client and Selenium server to get all the elements of class fc-day-content plus 1 round-trip per element that was found. So if you have 15 elements on your page with the class fc-day-content, that's 16 round-trips. If you run a test through Browser Stack or Sauce Labs, that's going to slow things down considerably.

And I prefer to avoid an XPath expression with @class='fc-day-content' because as soon as you add a new class to your element, this expression breaks. Maybe the element you care about has just one CSS class now but applications change. You could use XPath's contains() function but then you run into other complications, and once you take care of everything, it becomes a bit unwieldy. See this answer for how to use it robustly.

Upvotes: 4

TehTris
TehTris

Reputation: 3217

 fc_day_contents = driver.find_elements_by_class_name("fc-day-content")
 the_one_you_want = [x for x in fc_day_contents if "15" == x.text][0]

first line puts all elements with class name "fc-day-content" in a list ( also notice how its elementSSSSSSSSS with an S, this returns a list of all elements by_class_name, by_name, by_id or wahtever)

second line, goes through each element and looks to see if it has the text "15" as its text, and returns it as a (probably smaller) list

the [0] at the end of it, returns the first item in the list (you can remove it, if you want a list of all the ones that are "15" )

Upvotes: 4

Richard
Richard

Reputation: 9029

You can use xpath:

driver.find_element_by_xpath("//div[@class='fc-day-content' and text()='15']")

Upvotes: 30

Related Questions