I am trying to select an element which resides inside an iframe and probably in other iframes.
Is it somehow possible to select an element within some (sub-)iframe in (python)selenium without selecting the iframes before? Is there a way to 'loop' over every iframe somehow and to check where to find my element...?
And how to do that in the case elements and html stuff and iframes might just about being loaded...?
No, it is not possible to interact with any WebElement within an <iframe> through Selenium without switching to the respective iframe.
Reason :
When a page is loaded, Selenium's focus by default remains on the Top Window. The Top Window contains the other <iframes> and the framesets. So when we need to interact with a WebElement which is within an iframe we have to switch to the respective <iframe> through one of the below-mentioned methods :
Frame Switching Methods :
We can switch over to frames by 3 ways.
By Frame Name :
Name attribute of iframe through which we can switch to it.
Example:
driver.switch_to.frame("iframe_name")
By Frame ID :
ID attribute of iframe through which we can switch to it.
Example:
driver.switch_to.frame("iframe_id")
By Frame Index :
Suppose if there are 10 frames in the page, we can switch to the iframe by using the index.
Example:
driver.switch_to.frame(0)
driver.switch_to.frame(1)
Switching back to the Main Frame :
We can switch back to the main frame by using default_content() or parent_frame()
Example:
driver.switch_to.default_content()
driver.switch_to.parent_frame()
A Better Approach to Switch Frames:
A better way to switch frames will be to induce WebDriverWait for the availability of the intended frame with expected_conditions set to frame_to_be_available_and_switch_to_it as follows :
Through Frame ID:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it(By.ID,"id_of_iframe"))
Through Frame Name:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.NAME,"name_of_iframe")))
Through Frame Xpath:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"xpath_of_iframe")))
Through Frame CSS:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"css_of_iframe")))
Reference
You can find a relevant detailed discussion in:
Ways to deal with #document under iframe
Writing your own recursive finder should be easy enough. Apologies, don't know python but in Java it would be something like:
public void findInAllFrames(WebElement e, String targetIdStr) {
List<WebElement> l = e.findElements(By.tagName("iframe"));
for(int inx=0; inx<l.size(); inx++) {
List<WebElement> targets = l.get(inx).findElements(By.id(targetIdStr));
if(targets.size()>0) {
// Do something with your targets
}
findInAllFrames(l.get(inx), targetIdStr);
}
}
Related
I am trying to click on the "connect" button of linkedin and I can not, I have already tried in every possible way.
Explicit wait:
WebDriverWait espere_estar_pronto_para_clicar = new WebDriverWait(driver, 50); espere_estar_pronto_para_clicar.until(ExpectedConditions.elementToBeClickable(By.partialLinkText("Conectar")));
HTML Page Structure:
<button aria-label="Conecte-se a Eduardo G. K. Perez." class="search-result__actions--primary button-secondary-medium m5" data-ember-action="" data-ember-action-1876="1876">Conectar</button>
Click Attempts:
Attempt 1:
List<WebElement> conectar = driver.findElements(By.xpath("//button[text()='Conectar']"));
conectar.click();
Attempt 2:
JavascriptExecutor jse = (JavascriptExecutor)driver;
jse.executeScript("scroll(98,75, 32)");
Attempt 3:
driver.findElement(By.xpath("//*[#class='search-result__actions--primary.button-secondary-medium.m5']/button/text()")).click();
error:
Expected condition failed: waiting for element to be clickable:
By.partialLinkText: Conectar (tried for 50 second(s) with 500
MILLISECONDS interval)
Have you tried to take the XPath direct from a browser? (Chrome example below)
Right click on the element
Inspect Element
Right click on DOM
Select Copy
Click XPath
Paste in By.xpath(TEXT_COPIED)
If isn't that, I guess is the way you are trying to wait the button load.
The below code will do tested on my local, assuming you are going on
particular user profile page whom you want to connect once you signed-in and then clicking on Connect page
driver.findElement(By.xpath("//div[contains(#class,'pv-top-card-section__actions')]")).findElement(By.xpath("//span[contains(#class,'default-text') and contains(text(), 'Connect')]")).click();
Explanation :
1. driver.findElement(By.xpath("//div[contains(#class,'pv-top-card-section__actions')]")) this is parent div which has the Inmail and connect button within it, so I am locating the parent div keeping my driver to a limited area.
findElement(By.xpath("//span[contains(#class,'default-text') and contains(text(), 'Connect')]")).click(); fairly straightforward locating the element to be click on based on the class name and to be more accurate giving a context of text() which 'Connect' and clicking on the element.
Let me know if this does not works or in case you are facing any issues.
the automation is looking for an element that has not yet loaded, because the page loads the elements as the Scroll is triggered, so, the element searched was not yet on the page, because the Scroll had not yet been triggered.
JavascriptExecutor jsx = (JavascriptExecutor)driver;
//Go down 1000px
jsx.executeScript("window.scrollBy(0,1000)", "");
//up 1000px
jsx.executeScript("window.scrollBy(0,-1000)", "");
I'm trying to load a parent page into an object tag, and whilst I can get an alert to show I've got the code, I cannot get it into the <object> Any clues?
var page=parent.document.documentElement.innerHTML;
alert(page);
document.getElementById('close_skin').style.visibility="visible";
document.getElementById('show_skin').style.visibility="visible";
document.getElementById('show_skin').setAttribute("data",page);
Assuming I can get the code to appear, how can I "toggle" to a different set of styles? The parent uses ".xxx_fixed" classes, but the code in the object needs to use the ".xxx_float" classes that are also in the template CSS at top of page. (When I did it in another PERL program, it was easy just to rename the tags in the <body> from "class='xxx_fixed' " to "class='xxx_float' " (Can't do that so easily with global javascript replace as that would also rename the classes at top of code as well!)
I have just tried adding some script to the top of the var page object - which MAY work if I can get the code to appear ...
+'document.getElementById(\'icon_outer\').setAttribute(\'class\', \'icon_outer_float\')'
If you're interested as to the "why", the "fixed" keep menu / top bar fixed in one place when viewed in full screen browser, but the 'float' makes everything move in unison within the <object> "window" allowing viewer to pan around the template page like a static magnifying glass;
.menu_back_float{position:relative;top:0px;background-color:#f5eca4;min-height:520px;height:auto}
.menu_back_fixed{position:relative;top:35px;background-color:#f5eca4;min-height:550px;height:auto}
To load the parent page into an <object> element, you have to specify the URL of the page. To get the code and place it in a alert box, you can use jQuery's $.get() method (as long as you load it via a proxy domain), like this:
HTML:
// I'm just using W3Schools just to save time
<object data="http://www.w3schools.com/" width="1500" height="1200">
<embed src="http://www.w3schools.com/" width="1500" height="1200"></embed>
Your browser does not support the object tag.
</object>
JavaScript:
window.jQuery.get("http://www.yourdomain.com/?url=www.w3schools.com", function(response) {
window.alert(response);
}
For the second part of your question, you can use jQuery to change the name of the class from .xxx_fixed to .xxx_float:
function main() {
$('object').removeClass('xxx_fixed').addClass('xxx_float');
}
$(document).ready(main);
Hopefully I have answered your question correctly and thouroughly, and if I haven't, feel free to let me know so I can edit this.
I am using firebug to inspect the xpath, one block of firebug shows the element your finding is in window or iframe and so on. I am facing similar kind of problem where it shows me 2 options
1: Top window
2: iframe#mainframe
now the problem is,when i inspect the element it shows in iframe#mainFrame, i tried switching the driver control from main window to iframe and detect the element using webdriver but it didn't work for me,I wrote the following set of code:
driver.switchTo().frame("mainFrame");
driver.findElement(By.xpath("//div[#class='div_img']/img[#src='http://ser/themes/20/3/Flor/CF.jpg']")).click();
Note: when i checked the iframe in the html code,it doesn't contain another document inside it, It only contains id and other style and all and src tag.
Kindly suggest if there is some other way through which i can detect the element.
There are various ways to switch to iframe like using frame id, name, index and through webElement.
Might be in your case, there might be no iframe id or name. Best way is to create an xpath that uniquely identifies the iframe, create a webelement using this xpath and pass this webelement to switch frame. This is more reliable.
Suppose you have markup like below:-
<div id='noticeRichText'>
<iframe height="83px" frameborder="0" width="100%" scrolling="NO" '1331808552380'"="" +="" src="initialize.do?init=header&cacheBuster=" marginheight="0" marginwidth="0">
</div>
Create a WebElement for iframe:-
WebElement iframe=
driver.findElement(By.xpath("//*[#id='noticeRichText']/iframe"));
Switch to frame using above webelement:-
driver.switchTo().frame(iframe);
Perform the required actions on frame and then switch back to parent window using:-
driver.switchTo().defaultContent();
for the window handling you can use this...
String baseWindow = driver.getWindowHandle();
driver.switchTo().window(baseWindow);
I am trying to read the particular contents of an child IFrame wrapped in a div tag from parent window. I am using
detailsValue = window.frames['myIframe'].document.getElementById('result').innerHTML;
with this I'm able to access the entire content of that frame. But I need to access only a portion of that content. The problem is that the div which wraps the content that I am looking for contains only class and no ID.
<div class="watINeed"> <table class="details"> </table> </div>
I am unable to access the content which is in a form of table (with no id and only class).
Any help.
Edit1: I need to access the content of the table to check for char length and also for some html tags present in that content.
You can do this either using plain Javascript (as mentioned by Notulysses):
window.frames['myIframe'].document.querySelector('.watINeed .details')
or using jQuery (since you aded jquery) by specifying the iframe's document as context to $:
$(".watINeed .details", window.frames['myIframe'].document)
In the latter case you've a fullfeatured jQuery object.
Note that in either case the iframe's document has to be on the same domain otherwise you'd run into cross origin issues.
Tested against jQuery 2.0.x
Update
If you're running the selector during page load of the including page, you'll have to listen to the load event of the iframe before accessing its content:
$(window.frames['myIframe']).on("load", function(){
// above query here
});
If your are looking for a vanilla Javascript, and your target div is a direct children of starting selector, it is a simple task
var detailsValue = window.frames['myIframe'].document.getElementById('result').innerHTML;
var target;
for(var i = 0; i< detailsValue.children.lenght; i ++){
if(detailsValue.children[i].getAttribute('class')== 'watINeed'){
target = detailsValue.children[i] ;
}
}
otherwise, have to write a recursive method to scrap all children of structure
As i wrote above, it can be done using the following:
document.querySelectorAll(".className")[0] or $(".className")[0]
those are basically the same as both return a list of nodes and the [0] simply means taking the first result from the list.
there are 2 things to pay attention to:
the iframe loads the content asynchronously therefore when you execute the query its most likely that the elements you are searching for did not load yet.
executing the code after DOM loads is not enough.
the solution is simply put your code in a block that executes once all the asynchronous content is loaded:
window.onload=function(){
window.frames['myIframe'].document.querySelectorAll(".watINeed")[0];
}
or the jQuery alternative:
$(window).load(function(){
window.frames['myIframe'].document.querySelectorAll(".watINeed")[0];
});
the second thing is, according to the page Here, you can access the iframe's document using contentWindow.document:
window.onload=function(){
window.frames['myIframe'].contentWindow.document.querySelectorAll(".watINeed")[0];
}
or the jQuery alternative:
$(window).load(function(){
window.frames['myIframe'].contentWindow.document.querySelectorAll(".watINeed")[0];
});
live example: Fiddle
I'm using the Google Chrome JavaScript console and I was just looking at the Gmail page and just practicing manipulating the DOM. However, when I do the following it just comes back as null:
document.getElementById('gbx3');
There is a div element in the page that has an id of 'gbx3' - so why is it returning null? What would/could be causing this? The same thing occurs using the Firefox web console.
If you try and access the 'gb' id (this is the main top toolbar) in the same Gmail page it comes back null, but if you access this element at google.com it will come back with the element.
GMail is composed of frames. This one works:
frames[3].document.getElementById('gbx3');
In general, if you know the ID of the iframe element, the contentDocument property can be used (provided that you don't have same-origin problems, and the document is loaded):
document.getElementById('hist_frame').contentDocument.getElementById('gbx3');
As per my comment:
My best bet is that gbx is inside the iframe that contained the search
result, and document.getElementById works on the same scope as your
console
You can search all the frames to find what you need:
for(var i = 0; i < frames.length; i++) {
var curDoc = frames[i].document;
if(curDoc.getElementById('gbx')) console.log(curDoc.getElementById('gbx'));
}