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.
Related
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.
yeah, there are similar question, but upon reading through them I weren't able to find a solution for my problem.
Following situation: I'm tryin to click the "reply" button on "https://charleston.craigslist.org/ctd/d/charleston-2018-nissan-sentra-sedan-4d/7108660907.html" and after executing this click a popups shows up where I shall click another button, but let's start with the first button as the "reply" click itself is very trouble-making.
The reply button has the following X-Path:
'/html/body/section/section/header/div[2]/div/button'
speaking of which the source code is:
<button role="button" class="reply-button js-only" data-href="/__SERVICE_ID__/chs/ctd/7108660907">
reply
</button>
(see code on mentioned website).
However, my approach with Selenium (Python) doesn't work:
reply_button = '/html/body/section/section/header/div[2]/div/button'
driver.get('https://charleston.craigslist.org/ctd/d/charleston-2018-nissan-sentra-sedan-4d/7108660907.html')
driver.find_element_by_xpath(reply_button).click()
Everytime I tried, the website just loads up properly (even with implementing time.sleep(x)) and tries to click the button, but this fails and the website just refreshes - my guess is that they either regocnize the browser being Selenium-controlled, that the click isn't legitimate or that I didn't catch anything right in my code.. Anyone able to help out?
Btw I already tried searching "by_class_name", that didn't work either.
This Xpath: '/html/body/section/section/header/div[2]/div/button' is like when you get a map with instructions like step forward until you see a car then turn left 30° then step forward until you see a tree then hop twice then go to the second house to your right. Not safe to use, avoid such paths. If the page layout changes, your path may become invalid.
Try this:
button = driver.find_element_by_xpath('//*[#class="reply-button js-only"]')
button.click()
Clicking the button opens a "show phone number" popup (which may be located by driver.find_element_by_xpath('//*[#class="show-phone"]')).
Explanation:
If you want proper Xpath, inspect what you want to interact with. The button you want to click is this:
<button role="button" class="reply-button js-only" data-href="/__SERVICE_ID__/chs/ctd/7108660907">
reply
</button>
You can see that it has no "id" tag but it is a button with a specific class. You may copy right away the "class" part -> class="reply-button js-only"
Now you can check if is it unique enough:
driver.find_elements_by_xpath('//*[#class="reply-button js-only"]')
If "find elements" returns a single result, usually you should be OK. You can see that all I did is that I pasted the class inside this: driver.find_elements_by_xpath('//*[# and this ]').
If you need more accuracy, you can specify that it is a button:
driver.find_element_by_xpath('//button[#class="reply-button js-only"]')
Or it is the direct child of the element with class: class="actions-combo", so a more safe path would be:
driver.find_element_by_xpath('//*[#class="actions-combo"]/button[#class="reply-button js-only"]')
This pattern works for all webelement attributes, not just for classes. You could use the role="button" too for more filtering. Look up for Xpath, it is a pretty neat stuff.
I am working with python and selenium to click on the Photo/Video button on a facebook page. The HTML associated with this seems to have a list item (li) inside a ui. The html is as in the following image. The button circles is the one I am trying to press.
Can anyone please tell me how should I press the Photo/Video button?
Can you try this code?
I used the xPath method and contains() to compare the text in the div.
By the way, the found object does not have the click related function, and the click function seems to be a tag among its parents
The syntax for finding a parent in xPath is /.. and I used this
https://stackoverflow.com/a/3655588/12582501
driver.find_element_by_xpath('//div[contains(text(),"Photo/Video")]/../../../a').click()
Facebook has an intresting thing: testids
With this IDs you can click all of clickable elements on the site
driver.find_element_by_xpath('//div[#data-testid="photo-video-button"]').click()
In this case you can exec your code, when on page will be another element with text "Photo/Video"
I have a button that when I click it I will get a sort of a dropdown list. My problem is that I want to click one of the options in this dropdown list but I don't see how to refer to it.
I have tried to act as if this was a list box and I used the "Select" module but I failed with exceptions. My purpose is to be able to refer to any of the options in this dropdown list. Could it be that the HTML code is missing a unique href value ?
<input name="Port 19" value="Uplink" class="ExtendedButton" onclick="SelectFrame('Uplink-200')" id="Port-19" style="width: 84px; display: inline;" type="button">
<script>writeUplinkDropDown()</script>
<div class="dropdown-content">
200G
100G #1
100G #2
</div>
First thing you will need to do is open the dropdown menu. Once the menu is open you can click on any of those options you posted by using any of the following selector examples:
driver.find_elements_by_css_selector('a[onclick="SelectFrame(\"Uplink-200\")"]')
driver.find_elements_by_css_selector('a[onclick="SelectFrame(\"Port-19\")"]')
driver.find_elements_by_css_selector('a[onclick="SelectFrame(\"Port-20\")"]')
Selenium's Select class only works with native <select> elements. Because you have implemented a dropdown with custom HTML, you won't be able to use it.
Instead, in order to select one of the options in your custom dropdown, you'll need to perform each of the actions that a real user would:
clicking the button that opens the dropdown, then
clicking the link for the desired option.
Note: When searching for elements on a page, always try to use the same criteria that a real user would. A real user would look for a link with some meaningful text, e.g., "200G"; they would not go scouring the source code looking for a particular onclick attribute. (What's more, the onclick attribute is a part of the implementation of the page, not the interface, and shouldn't be relied upon as such, as it could change at any time.)
1. Using Selenium
Selenium doesn't provide an explicit method for finding buttons, but you can use CSS or XPath to do that:
driver.find_element_by_css_selector("input[type='button'][value='Uplink']")
driver.find_element_by_xpath("//input[#type = 'button'][#value = 'Uplink']")
To find links, Selenium conveniently provides find_element_by_link_text():
driver.find_element_by_link_text("200G").click()
driver.find_element_by_link_text("100G #1").click()
driver.find_element_by_link_text("100G #2").click()
2. Using Capybara (which uses Selenium)
Bare Selenium can be fickle. The link may not yet be in the DOM. Or it may not yet be visible.
capybara-py addresses these problems transparently:
page.click_button("Uplink")
page.click_link("200G")
page.click_link("100G #1")
page.click_link("100G #2")
I have a python function that should click through all options of a product:
submit_button = driver.find_element_by_id('quantityactionbox')
elementList = submit_button.find_elements_by_tag_name("option")
for x in elementList:
x.click()
After I clicked 2 elements I get this error:
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
Can you maybe tell me why this error appaer and what can I do to go successfully through all elements?
The easy way to overcome many of these types of errors is to just add some sort of delay:
import time
time.sleep(1)
DOM manipulation after an event is fired usually takes a bit of time so you're not really losing that much of performance.
You have the explanation and the solution on The Element is not Attached to the DOM:
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.
In my case it was because the page had changed and the element no longer existed but my script was trying to call it. It was not readily obvious because the page did have essentially the same element but it had been reloaded and was therefore, not the exact same element and not available in the current page document. I had to redefine the element after the page was reloaded.