This question already has answers here:
Selenium - wait until element is present, visible and interactable
(6 answers)
Closed 1 year ago.
I am using Selenium with Python to automate a process to upload a file. There is an "Upload" button which is disabled by default and only becomes clickable when the file to be uploaded is chosen.
The HTML for Disabled Button is -
<button type="button" id="upload-button" data-bi-id="upload-button" class="ms-Button ms-Button--primary is-disabled root-296" disabled="" aria-label="Upload" aria-disabled="true" data-is-focusable="false">
And the HTML for button after it becomes clickable is -
<button type="button" id="upload-button" data-bi-id="upload-button" class="ms-Button ms-Button--primary root-437" aria-label="Upload" data-is-focusable="true" tabindex="0">
I am using -
WebDriverWait(browser, 15).until(EC.element_to_be_clickable((By.ID,"upload-button"))).click()
But its not working. I believe this is clicking on the disabled button (even though the file is chosen and the button has become clickable). I also tried -
WebDriverWait(browser, 15).until(EC.element_to_be_clickable((By.CLASS_NAME,"ms-Button ms-Button--primary root-437"))).click()
But this gives a TimeOut Exception. So what should I do to click this button after it becomes clickable. I have tried some solutions from the Internet, but none of them seem to be working.
Regarding the current version of your code, I think you may be right that it is clicking the button before it is really enabled. You have
WebDriverWait(browser, 15).until(EC.element_to_be_clickable((By.ID,"upload-button"))).click()
You are waiting for this element to be clickable. I wanted to try and figure out exactly what this meant so I looked at the source code. element_to_be_clickable is satisfied As soon as the element is "visible" and "enabled".
Visibility, I know, is defined as presence on the DOM and height/width both > 0. From your description it sounds like your button is immediately visible. So as soon as it is "enabled", element_to_be_clickable is satisfied and the wait will end.
This begs the question, what exactly determines whether an element is "enabled"? I found that selenium's is_enabled (which is required in the source code for element_to_be_clickable to pass), is essentially a negation of the W3C specification for disabled(). What it boils down to is this single line, which states that an element is "disabled" if The element is a button, input, select, textarea, or form-associated custom element, and the disabled attribute is specified on this element (regardless of its value).
That's it. Your element does have the "disabled" attribute, but it also has some other stuff that might cause it to be disabled -- the class name contains is-disabled, it's got aria-disabled="true" as well as data-is-focusable="false", all of which change by the time the button is fully clickable. I wonder if the disabled attribute goes away before something else that also causes the element to be disabled, so just as you said maybe your click is registering before the button is ready. To debug this I would try temporarily adding a hard wait, a few seconds long, after executing the WebDriverWait and before clicking the button.
For your class name,
WebDriverWait(browser, 15).until(EC.element_to_be_clickable((By.CLASS_NAME,"ms-Button ms-Button--primary root-437"))).click()
I suspect this is a dynamic class name, the root-437 part in particular, so maybe that's why that didn't work.
Finally, are you intending to upload from your filesystem by clicking the button? Because it can only interact with your web browser and can't browse a window on your OS, that doesn't work. There's a special way to upload files--you have to identify the file input element and send the absolute path of the file you want to upload to that element using send_keys().
It seems you are using wrong ID value.
WebDriverWait(browser, 15).until(EC.element_to_be_clickable((By.ID,"upload-button"))).click()
Or use this css selector
WebDriverWait(browser, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR,"#upload-button[data-is-focusable='true']"))).click()
As you can see, the button element while it is still disabled contains class is-disabled and contains attribute disabled and it doesn't contain these attributes when it is enabled.
So the expected condition is to locate the element defined by the following xpath:
//button[#id='upload-button' and(not(contains(#class,'is-disabled'))) and(not(#disabled))]
In other words you should use the following:
WebDriverWait(browser, 15).until(EC.presence_of_element_located((By.XPATH, "//button[#id='upload-button' and(not(contains(#class,'is-disabled'))) and(not(#disabled))]")))
You can locate the enabled button based on absence of one of the two attributes mentioned as well.
Try clicking without explicit wait, using implicit:
driver.implicitly_wait(15)
driver.find_element_by_xpath('//button[#data-is-focusable="true"]').click()
Or
driver.implicitly_wait(15)
driver.find_element_by_xpath('//button[#data-is-focusable="true" and #tabindex="0"]').click()
If this won't help, add more details to your question.
Related
I use selenium with python and want to mark a checkbox by clicking on it.
After having identified the <input> tag of the checkbox with selenium, I attempt to click on it with
checkbox.click()
However, it throws the ElementClickInterceptedException.
I identify the checkbox I want to click on through a loop. When I try to click on the checkbox outside of the loop (by manually running the code after it was identified and saved to a variable), I found two things:
All things equal, I still get the exception
When I click in the browser window once (that was opened with selenium) and then run checkbox.click(), it works as expected
Any ideas why and how I could attempt to slove this issue (i.e. being able to click on the checkbox within the loop)?
Thanks!
You either deal with the overlapping element or use javascript
driver.execute_script("arguments[0].click();", checkbox)
Sometimes, the checkbox is not directly interactable. You may have to click on the label/text next to the checkbox.
or May be you have to wait till the element is clickable since your are running in a loop.
I'm making a simple bot to apply to jobs on LinkedIn easy apply. Usually after clicking apply you fill in the basic info and the next button appears until you have a review button and then a submit application button. I was able to click next using a while loop with this code.
button = driver.find_element_by_css_selector(".artdeco-button--primary") while button: button.click() time.sleep(1.25)
but I get this error when the Review button appears
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document (Session info: chrome=102.0.5005.115)
I've tried a lot of different methods, XPath, WebdriverWait, and others but nothing works to click on the Review button with the following element HTML
<button aria-label="Review your application" id="ember562" class="artdeco-button artdeco-button--2 artdeco-button--primary ember-view" type="button"> <span class="artdeco-button__text"></span> </button>
could I get some help figuring out how to select the Review button?
DOM Issue:
This probably has to do with the element not being attached to the DOM. See Selenium Docs on this error.
A common technique used for simulating a tabbed UI in a web app is to prepare DIVs for each tab, but only attach one at a time, storing the rest in variables. In this case, it's entirely possible that your code might have a reference to an element that is no longer attached to the DOM (that is, that has an ancestor which is document.documentElement).
If WebDriver throws a stale element exception in this case, even though the element still exists, the reference is lost. You should discard the current reference you hold and replace it, possibly by locating the element again once it is attached to the DOM.
For your particular example, add the button variable inside your while loop. This will have the button variable be re-referenced each time you click which will make it a part of the current DOM.
I'm creating an Instagram Unfollow Tool. The code usually works, but occasionally Instagram will show the information of some user (as if I hovered over that user) and cause my code to stop running because it obscures the buttons that I need to click in order to unfollow a user.
It's easier to understand with an example:
How can I edit my code to make the mouseover information go away (is there a way to turn off these mouseover effects)?
My code:
for i in range(num_following):
following = self.browser.find_element_by_xpath("/html/body/div[5]/div/div/div[2]/ul/div/li[{}]/div/div[1]/div[2]/div[1]/span/a".format(i))
following_username = following.get_attribute("title")
if following_username not in followers:
# Unfollow account
following_user_button = self.browser.find_element_by_xpath("/html/body/div[5]/div/div/div[2]/ul/div/li[{}]/div/div[2]/button".format(i))
following_user_button.click()
unfollow_user_button = self.browser.find_element_by_xpath("/html/body/div[6]/div/div/div/div[3]/button[1]")
unfollow_user_button.click()
print("You've unfollowed {}.".format(following_username))
The error I get:
selenium.common.exceptions.ElementClickInterceptedException: Message: Element is not clickable at point (781,461) because another element obscures it
It seems like the element unfollow_user_button = self.browser.find_element_by_xpath("/html/body/div[6]/div/div/div/div[3]/button[1]") you want to execute .click() on is blocked by a temporary or permanent overlay.
In such cases you either can wait with ExplicitWait and ExplicitConditions till said blocking element has vanished - though this shouldn't work in this specific case as to my knowledge the popup remains if nothing is done. Another approach is to send the click directly to the element by using the JavascriptExecutor:
#Find the element - by_xpath or alike
unfollow_user_button = driver.find_element_by_xpath("XPATH")
#Sending the click via JavascriptExecutor
driver.execute_script("arguments[0].click();", unfollow_user_button)
Note two things:
driver must obviously be an instance of WebDriver.
I would suggest not using the absolute XPath in general. Going with the relative XPath is less prone to be broken by small changes in the site structure. Click here for a small guide to read through.
I am trying to understand Python in general as I just switched over from using VBA. I interested in the possible ways you could approach this single issue. I already went around it by just going to the link directly, but I need to understand and apply here.
from selenium import webdriver
chromedriver = r'C:\Users\dd\Desktop\chromedriver.exe'
browser = webdriver.Chrome(chromedriver)
url = 'https://www.fake.com/'
browser.get(url)
browser.find_element_by_id('txtLoginUserName').send_keys("Hello")
browser.find_element_by_id('txtLoginPassword').send_keys("There")
browser.find_element_by_id('btnLogin').click()
At this point, I am trying to navigate to a particular button/link.
Here is the info from the page/element
T-Mobile
Here are some of the things I tried:
for elem in browser.find_elements_by_xpath("//*[contains(text(), 'T-Mobile')]"):
elem.click
browser.execute_script("InitiateCallBack(187, True, T-Mobile, https://www.fake.com/, TMobile)")
I also attempted to look for tags and use css selector all of which I deleted out of frustration!
Specific questions
How do I utilize the innertext,"T-Mobile", to click the button?
How would I execute the onclick event?
I've tried to read the following links, but still have not succeeded incoming up with a different way. Part of it is probably because I don't understand the specific syntax yet. This is just some of the things I looked at. I spent about 3 hours trying various things before I came here!
selenium python onclick() gives StaleElementReferenceException
http://selenium-python.readthedocs.io/locating-elements.html
Python: Selenium to simulate onclick
https://stackoverflow.com/questions/43531654/simulate-a-onclick-with-selenium-https://stackoverflow.com/questions/45360707/python-selenium-using-onclick
Running javascript in Selenium using Python
How do I utilize the innertext,"T-Mobile", to click the button?
find_elements_by_link_text would be appropriate for this case.
elements = driver.find_elements_by_link_text('T-Mobile')
for elem in elements:
elem.click()
There's also a by_partial_link_text locator as well if you don't have the full exact text.
How would I execute the onclick event?
The simplest way would be to simply call .click() on the element as shown above and the event should, naturally, execute at that time.
Alternatively, you can retrieve the onclick attribute and use driver.execute_script to run the js.
for elem in elements:
script = elem.get_attribute('onlcick')
driver.execute_script(script)
Edit:
note that in your code you did element.click -- this does nothing. element.click() (note the parens) calls the click method.
is there a way to utilize browser.execute_script() for the onclick event
execute_script can fire the equivalent event, but there may be more listeners that you miss by doing this. Using the element click method is the most sound. There may very well be many implementation details of the site that may hinder your automation efforts, but those possibilities are endless. Without seeing the actual context, it's hard to say.
You can use JS methods to click an element or otherwise interact with the page, but you may miss certain event listeners that occur when using the site 'normally'; you want to emulate, more or less, the normal use as closely as possible.
As per the HTML you have shared it's pretty clear the website uses JavaScript. So to click() on the link with text as T-Mobile you have to induce WebDriverWait with expected_conditions clause as element_to_be_clickable and your can use the following code block :
WebDriverWait(driver, 20).until(expected_conditions.element_to_be_clickable((By.XPATH, "//a[contains(.,'T-Mobile')]"))).click()
you can use it
<div class="button c_button s_button" onclick="submitForm('rMTF')" style="margin-bottom: 30px;">
<input class="v_small" type="button"></input>
<span>
Reset
</span>
I am trying to click a button to show more comments on a discusson thread on http://disqus.com/
The HTML looks like following:
<div class="load-more" data-role="more" style="">
Show more
</div>
I have tried using the xPath from the button like following:
driver = webdriver.Chrome("/usr/lib/chromium-browser/chromedriver")
driver.get(url)
driver.find_element_by_xpath('''//*[#id="posts"]/div[3]/a''').click()
But I get the NoSuchElement exception.
What is the proper way of clicking a button with that type of HTML?
UPDATE: Worked when I switched to a specific iFrame:
driver.switch_to.frame('myID')
Then loaded by class name:
element = driver.find_element_by_class_name('load-more')
element.click()
NOTE: That the click() did not work when it was performed on the same line like driver.find_element_by_class_name('load-more').click()
Xpath should be a last resort for finding elements. For your case, I would try
driver.find_element_by_class_name("load-more__button"));
Using btn load-more__button won't work as compound classNames aren't valid.
If that still doesn't work, Id recommend using css selectors before Xpath.
However, if none of these options work, and your Xpath is still not working, you should use the firebug add on for Firefox to ensure the Xpath you are using is correct.
Here's some of my other answers that may help you get started with Firebug:
https://stackoverflow.com/a/38980488/3537915
https://stackoverflow.com/a/38723782/3537915
https://stackoverflow.com/a/38744577/3537915