How to address "WebDriverException: Message: TypeError: can't access dead object" and wait for a frame to be available using Selenium and Python
Better approach is to make your own expected_condition. For example:
class nested_frames_to_be_available_and_switch:
def __init__(self, *args):
"""
:param args: locators tuple of nested frames (BY.ID, "ID1"), (BY.ID, "ID2"), ...
"""
self.locators = args
def __call__(self, driver):
try:
for locator in self.locators:
driver.switch_to.frame(driver.find_element(*locator))
except WebDriverException:
driver.switch_to_default_content()
return False
return True
WebDriverWait(driver, 300).until(nested_frames_to_be_available_and_switch((By.ID, 'frame1'), (By.ID, 'frame1')))
But maybe there is no need for that.. To tell so I need to see your html DOM.
This error message...
WebDriverException: Message: TypeError: can't access dead object
...implies that there was an error while switching between <iframes>
.
Some more information in terms of:
- The relevant HTML
- Presence of Frameset
- Presence of Frames
- Hierarchy of Nested Frames
- Sequence of Frame Loading
- Presence of JavaScript and AJAX Calls within the respective
<iframe>
tags
would have helped us to analyze the issue in a better way. However, at this point it is worth to mention that initially Selenium always gets the focus on the default_content. Here are a few approaches to work with nested <frames>
and <framesets>
:
-
If both frame1 and frame2 are at same level i.e. under the Top Level Browsing Context you need to:
self.driver.switch_to.frame(self.driver.find_element_by_id("frame1")) self.driver.switch_to_default_content() self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
-
If frame2 is nested within frame1, to switch from frame1 to frame2 you need to:
self.driver.switch_to_default_content() self.driver.switch_to.frame(self.driver.find_element_by_id("frame1")) //code self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
-
If frame2 and frame3 is within frame1 then, to switch from frame2 to frame3 you need to:
self.driver.switch_to.frame(self.driver.find_element_by_id("frame2")) self.driver.switch_to.frame(self.driver.find_element_by_id("frame1")) self.driver.switch_to.frame(self.driver.find_element_by_id("frame3"))
-
If frame2 and frame3 is within a frameset23 which is within frame1 then, to switch from frame2 to frame3 you need to:
self.driver.switch_to.frame(self.driver.find_element_by_id("frame2")) #ignore presence of frameset23 self.driver.switch_to.frame(self.driver.find_element_by_id("frame1")) #ignore presence of frameset23 self.driver.switch_to.frame(self.driver.find_element_by_id("frame3"))
Better approach with proper WebDriverWait
While dealing with iframe and frameset you need to induce WebDriverWait inconjunction with expected_conditions:
frame_to_be_available_and_switch_to_it()
As an example to switch from Top Level Browsing Context to an <iframe>
an effective line of code will be:
-
Using frame ID:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.ID,"frameID")))
-
Using frame NAME:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.NAME,"frameNAME")))
-
Using frame CLASS_NAME:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.frame_CLASS_NAME,"u_0_f")))
-
Using frame CSS_SELECTOR:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"frame_CSS_SELECTOR")))
-
Using frame XPATH:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"frame_XPATH")))
tl; dr
Ways to deal with #document under iframe