In Selenium you can wait for a DOM element to load using WebDriverWait and EC.presence_of_element()
WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.TAG_NAME, 'button')))
However, this queries the entire DOM. This does not suit scenarios where you have multiple button's on the the page and are querying for a specific one. The presence_of_element_located() will trigger on the first instance of a button
<body>
<div class="sign-in">
<button>Sign In!</button>
</div>
<div class="controls">
<button>play!</button> <!-- The button we care about-->
</div>
</body>
I want to perform a WebDriverWait for a the button inside of .controls specifically
Selenium allows you to perform nested queries by chaining .find_element()
control_button = driver.find_element(
By.CLASS_NAME, 'controls').find_element(
By.TAG_NAME, 'button')
is there a comparable technique for presence_of_element()?
Solution 1
Using By.CSS_SELECTOR
WebDriverWait(driver, 5).until(
EC.presence_of_element_located((
By.CSS_SELECTOR, '.controls button')))
Solution 2
Passing the parent WebElement as the driver to the WebDriverWait()
controls = driver.find_element(By.CLASS_NAME, 'controls')
WebDriverWait(controls, 5).until(
EC.presence_of_element_located((By.TAG_NAME, 'button')))
Related:
Selenium - wait until presence of child element of another element
Explicitly wait for element that is the child of another element
Related
I want to use the click() function of selenium on these buttons in the following manner(the first two clicks):
For reference the 1st button I want to click and its code:
2nd button with its code:
I tried using
driver.find_element(By.XPATH,"//button[contains(#type,\"button\")]").click()
on the first button (the "X" mark) but it didn't work and got this error:
selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element <button type="button" class="el-button yellowBtn el-button--default">...</button> is not clickable at point (606, 507). Other element would receive the click: <img src="https://d2g38dx0j6xqav.cloudfront.net/online/img/app-inner/popup.png" alt="" style="width: 100%;">
If I somehow get it to click the first button then getting it to click the second button will be much easier.
selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted
Above exception implies that the element you are trying to action upon isn't clickable, as some other element is overshadowing it.
Try by inducing wait(see below):
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH,"Your XPath expression here"))).click()
if the above doesn't work, try using execute_script() method(see below):
driver.execute_script("arguments[0].click();", WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "Your XPath expression here"))))
Required imports statements:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
I think you should wait until the element is visible in the modal dialog box. U can use both the Explicit and Implicit waits methods for the above problem.
Always read exception and/or error messages carefully to see what you can learn about the problem. In this case, it tells you exactly what happened. I reformatted and shortened it to make it more readable.
ElementClickInterceptedException:
Message: element click intercepted: Element <button...> is not clickable.
Other element would receive the click: <img...>
Selenium is telling you that it tried to click the BUTTON but the click was intercepted (it never got clicked because there was another element covering it). The element that got clicked instead was an IMG.
Without seeing the full HTML of the page it's hard to say but your BUTTON locator looks pretty generic and there's a good chance that you clicked a different BUTTON than you intended. I would use the locator below instead to click on the I tag.
wait = WebDriverWait(driver, 10)
close_icon = (By.CSS_SELECTOR, "i.el-dialog__close")
wait.until(EC.element_to_be_clickable(close_icon)).click() # click the close icon
wait.until(EC.invisibility_of_element_located(close_icon)) # wait for the close icon to disappear indicating the dialog is fully closed
# continue script
If that still doesn't work, you will probably need to use a JS click. If you are writing UI tests, then you should avoid JS clicks unless no other solution can be found because it allows the script to do things that a user can't. If you aren't writing tests, then it likely doesn't matter.
wait = WebDriverWait(driver, 10)
close_icon = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "i.el-dialog__close")))
driver.execute_script("arguments[0].click()", close_icon)
# continue script
You desired element is:
But your line of code:
driver.find_element(By.XPATH,"//button[contains(#type,\"button\")]").click()
is attempting to click on:
<button type="button" class="el-button yellowBtn el-button--default">...</button>
which isn't the desired element and still the click attempt is intercepted by:
<img src="https://d2g38dx0j6xqav.cloudfront.net/online/img/app-inner/popup.png" alt="" style="width: 100%;">
Solution
To click element you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following locator strategies:
Using CSS_SELECTOR:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.el-dialog__headerbtn[aria-label='Close'] > i.el-dialog__close.el-icon.el-icon-close"))).click()
Using XPATH:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//button[#class='el-dialog__headerbtn' and #aria-label='Close']/i[#class='el-dialog__close el-icon el-icon-close']"))).click()
Note: You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
I have a python script where I need to click a button. My function is as follows:
def inviteuser():
invitebutton.click()
time.sleep(2.5)
addressbox = driver.find_element_by_xpath('/html/body/div[9]/div/div/div[2]/div/div[1]/div/div/div/div/div[3]/div/div/div[1]')
time.sleep(2.5)
addressbox.send_keys(email)
time.sleep(2.5)
sendbutton = driver.find_element_by_xpath('/html/body/div[8]/div/div/div[3]/div[2]')
sendbutton.click()
When running the script at the button clicking part, I get this message:
selenium.common.exceptions.ElementClickInterceptedException: Message: Element <div class="c-sk-modal_footer_actions"> is not clickable at point (834,677) because another element <div class="ReactModal__Overlay ReactModal__Overlay--after-open c-popover c-popover--z_above_fs c-popover--fade"> obscures it
I tried searching for that div, but the search in the browser could not find it.
I also tried
driver.find_element_by_css_selector('.c-button .c-button--primary .c-button--medium').click()
HTML code of the items
<div class="c-sk-modal_footer_actions">
<button class="c-button c-button--primary c-button--medium c-button--disabled" data-qa="invite-to-workspace-modal-invite-form-send-button" type="button" aria-disabled="true">
"Send"
::after
</button>
</div>
If it helps at all, this is for the invite people box in slack admin portal
EDIT:
So I basically figured out the issue but can't figure out how to fix the issue...
So just using the variable sys.argv[1] puts in the email address, but I need to either press space bar , comma, or enter key after. I can get it to work if I specify what the variable email is (email = "test#test.com" then confirm = " ") and adding a second line addressbox.send_keys(confirm) but if I make the variable what I need it to be so it's called from powershell (sys.argv[1]) It doesn't work. It's like it removes what I entered and only puts what's in the variable "confirm"
Try the execute script method which can have a better chance when getting that error. Also you could use implicitly wait instead of time waits to be more effecient.
driver.implicitly_wait(5)
addressbox = driver.find_element_by_xpath('/html/body/div[9]/div/div/div[2]/div/div[1]/div/div/div/div/div[3]/div/div/div[1]')
driver.execute_script("arguments[0].click();", addressbox)
Generally <div> elements aren't clickable. Presumably you need to crosscheck if the following element is your desired element:
<div class="c-sk-modal_footer_actions">
Where as, the element from your second attempt:
<div class="c-button c-button--primary c-button--medium...">
looks as a perfect clickable candidate.
Solution
The desired element is a React element, so to click() on the element you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following locator strategies:
Using CSS_SELECTOR:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".c-button.c-button--primary.c-button--medium"))).click()
Using XPATH:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[#class='c-button c-button--primary c-button--medium']"))).click()
Note: You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
I have an element that clears a query after the query is made, but I can't get selenium to locate it.
Here is the xpath for the element:
/html/body/main/div/section[1]/div/a[2]
This is the html for the element:
<a onclick="onClearClicked()" class="btn btn--secondary btn--sm">clear</a>
Things that I have tried:
Finding by using the whole xpath, css selector, class name, text
Using css to find all buttons on the page and iterating over them until I find the right one (it doesn't find any buttons with text)
buttons = mydriver.find_elements_by_css_selector("button")
for button in buttons:
print(button.text)
if button.text.strip() == "clear":
button.click()```
Exiting the iframe I was in before and using the full xpath
driver.switch_to().defaultContent()
I have a work around that involves quitting the driver and reopening it for every query, but this would involve logging in and navigating to the right page every time and I'd much rather be able to just use the clear button.
Your html doesn't have any "button" element; it's an "a" (hyperlink) element. So, you should do:-
buttons = mydriver.find_elements_by_css_selector("a")
Using full xpath not a good way , so try the following code:
buttons =driver.find_elements_by_css_selector("[class='btn btn--secondary btn--sm']")
for button in buttons:
print(button.text)
if button.text.strip() == "clear":
button.click()
driver.find_element(By.XPATH,"//a[.='clear']").click()
If you want to click the element with the text clear you can just look for an a tag with that text.
The element is a <a> tag which have the innerText as clear
<a onclick="onClearClicked()" class="btn btn--secondary btn--sm">clear</a>
Solution
To click on the clickable element you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following locator strategies:
Using LINK_TEXT:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.LINK_TEXT, "clear"))).click()
Using CSS_SELECTOR:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a.btn.btn--secondary.btn--sm[onclick^='onClearClicked']"))).click()
Using XPATH:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='btn btn--secondary btn--sm' and starts-with(#onclick, 'onClearClicked')]"))).click()
Note: You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
I'm trying to pull a specific number out of a div class in Python Selenium but can't figure out how to do it. I'd want to get the "post_parent" ID 947630 as long as it matches the "post_name" number starting 09007.
I'm looking to do this across multiple "post_name" classes, so I'd feed it something like this: search_text = "0900766b80090cb6", but there will be multiple in the future so it has to read the "post_name" first then pull the "post_parent" if that makes sense.
Appreciate any advice anyone has to offer.
<div class="hidden" id="inline_947631">
<div class="post_title">Interface Converter</div>
<div class="post_name">0900766b80090cb6</div>
<div class="post_author">28</div>
<div class="comment_status">closed</div>
<div class="ping_status">closed</div>
<div class="_status">inherit</div>
<div class="jj">06</div>
<div class="mm">07</div>
<div class="aa">2001</div>
<div class="hh">15</div>
<div class="mn">44</div>
<div class="ss">17</div>
<div class="post_password"></div>
<div class="post_parent">947630</div>
<div class="page_template">default</div>
<div class="tags_input" id="rs-language-code_947631">de</div>
</div>
If you see <div class="post_name">0900766b80090cb6</div> this and <div class="post_parent">947630</div> are siblings nodes to each other.
You can use xpath -> following-sibling like this:
Code:
search_text = "0900766b80090cb6"
post_parent_num = driver.find_element(By.XPATH, f"//div[#class='post_name' and text()='{search_text}']//following-sibling::div[#class='post_parent']").text
print(post_parent_num)
or Using ExplicitWait:
search_text = "0900766b80090cb6"
post_parent_num = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, f"//div[#class='post_name' and text()='{search_text}']//following-sibling::div[#class='post_parent']"))).get_attribute('innerText')
print(post_parent_num)
Imports:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
Update:
NoSuchElementException:
Please check in the dev tools (Google chrome) if we have unique entry in HTML-DOM or not.
xpath that you should check :
//div[#class='post_name' and text()='0900766b80090cb6']//following-sibling::div[#class='post_parent']
Steps to check:
Press F12 in Chrome -> go to element section -> do a CTRL + F -> then paste the xpath and see, if your desired element is getting highlighted with 1/1 matching node.
If this is unique //div[#class='post_name' and text()='0900766b80090cb6']//following-sibling::div[#class='post_parent'] then you need to check for the below conditions as well.
Check if it's in any iframe/frame/frameset.
Solution: switch to iframe/frame/frameset first and then interact with this web element.
Check if it's in any shadow-root.
Solution: Use driver.execute_script('return document.querySelector to have returned a web element and then operates accordingly.
Make sure that the element is rendered properly before interacting with it. Put some hardcoded delay or Explicit wait and try again.
Solution: time.sleep(5) or
WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//div[#class='post_name' and text()='0900766b80090cb6']//following-sibling::div[#class='post_parent']"))).text
If you have redirected to a new tab/ or new windows and you have not switched to that particular new tab/new window, otherwise you will likely get NoSuchElement exception.
Solution: switch to the relevant window/tab first.
If you have switched to an iframe and the new desired element is not in the same iframe context then first switch to default content and then interact with it.
Solution: switch to default content and then switch to respective iframe.
I don't see any specific relation between "post_parent" ID 947630 and "post_name" number starting 09007. Moreover, the parent <div> is having class="hidden".
However, to pull the specific number you can use either of the following locator strategies:
Using css_selector:
print(driver.find_element(By.CSS_SELECTOR, "div[id^='inline'] div.post_parent").text)
Using xpath:
print(driver.find_element(By.XPATH, "//div[starts-with(#id, 'inline_')]//div[#class='post_parent']").text)
Ideally you need to induce WebDriverWait for the presence_of_element_located() and you can use either of the following locator strategies:
Using CSS_SELECTOR:
print(WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.CSS_SELECTOR, "div[id^='inline'] div.post_parent"))).text)
Using XPATH:
print(WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//div[starts-with(#id, 'inline_')]//div[#class='post_parent']"))).text)
Note: You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
You can create a method and use the following xpath to get the post_parent text based on post_name text.
def getPostPatent(postname):
element=driver.find_element(By.XPATH,"//div[#class='post_name' and starts-with(text(),'{}')]/following-sibling::div[#class='post_parent']".format(postname))
print(element.get_attribute("textContent"))
getPostPatent('09007')
This will return value if it is matches the text starts-with('09007')
It seems parent class is hidden you need to use textContent to get the value.
Here are all the parts of the element I am trying to click according to 'Inspect':
<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
::before
<div class="ui-dialog-buttonset">
<button type="button" class="done ui-button ui-corner-all ui-widget">
Done</button>
</div>
::after
</div>
When I want to click it, I assume it is on the ::before part as it is showing and clickable. In the code, I make sure to scroll and wait two seconds before clicking to make sure that the button is visible and yet I get:
selenium.common.exceptions.ElementNotVisibleException: Message: element not interactable
But I don't understand how it's not interactable. When I hover over it in the inspector, everything in <button>...</button> highlights, so the button I want to click must be in there, right?
Here are few things that I have tried:
browser.find_element_by_css_selector('button[type=button]').click()
browser.find_elements_by_xpath("//*[contains(text(), 'Done')]").click()
# The above returns a list for some reason?
browser.find_elements_by_css_selector('done.ui-button.ui-corner-all.ui-widget')
I wish I could remember all that I tried, but regardless I hope someone can help me.
To click on the element as the desired element is a dynamic element you have to induce WebDriverWait for the element to be clickable and you can use either of the following solutions:
Using CSS_SELECTOR:
WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.ui-dialog-buttonpane.ui-widget-content.ui-helper-clearfix"))).click()
Using XPATH:
WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[#class='ui-dialog-buttonpane ui-widget-content ui-helper-clearfix']"))).click()
Note : You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
You can find a detailed discussion in selenium.common.exceptions.ElementNotVisibleException: Message: element not interactable using Selenium
When you use .find_elements (note the plural) it will return a list instead of a single element like .find_element (singular) does. Have you tried
browser.find_element_by_xpath("//button[.='Done']")
If you get a len() on these .find_elements calls... is it 1? I'm wondering if there isn't more than one button that matches the locators you are using and the first isn't visible but you want the second or third, etc.
You can use ActionChains to move to element
from selenium.webdriver.common.action_chains import ActionChains
element = driver.find_elements_by_css_selector("div.ui-dialog-buttonpane.ui-widget-content.ui-helper-clearfix")
actions = ActionChains(driver)
actions.move_to_element(element).perform()
or u can scroll till element is in view using scrollIntoView():
driver.execute_script("arguments[0].scrollIntoView();", element)