How to get an HtmlElement value inside Frames/IFrames?
The Web page you linked contains IFrames.
An IFrame
contains its own HtmlDocument. As of now, you're parsing just the main Document container.
Thus, you need to parse the HtmlElements
TAGs of some other Frame
.
The Web Page Frames list is referenced by the WebBrowser.Document.Window.Frames property, which returns an HtmlWindowCollection.
Each HtmlWindow in the collection contains it own HtmlDocument
object.
Instead of parsing the Document
object property returned by a WebBrowser
, we, most of the time, need to parse each HtmlWindow.Document
in the Frames
collection; unless, of course we already know that the required Elements are part of the main Document or another known Frame
.
An example (related to the current task):
- Subscribe the DocumentCompleted event of the WebBrowser Control/Class.
- Check the WebBrowser.ReadyState property to verify that a Document is loaded completely.
Note:
Remembering that a Web Page may be composed by multiple Documents contained in Frames/IFrames, we won't be surprised if the event is raised multiple times with a ReadyState = WebBrowserReadyState.Complete
.
Each Frame's Document
will raise the event when the WebBrowser
is done loading it.
- Parse the
HtmlDocument
of each Frame in theDocument.Window.Frames
collection, using the Frame.Document.Body.GetElementsByTagName() method. - Extract the
HtmlElements
Attibute
using the HtmlElement.GetAttribute method.
Note:
Since the DocumentCompleted
event is raised multiple times, we need to verify that an HtmlElement
Attribute value is not stored multiple times, too.
Here, I'm using a support custom Class that holds all the collected values along with the HashCode of each reference Link (here, relying on the default implementation of GetHasCode()
).
Each time a Document is parsed, we check whether a value has already been stored, comparing its Hash.
- Stop the parsing when we verify that a duplicate Hash has been found: the Frame Document Elements have already been extracted.
Note:
While parsing the HtmlWindowCollection
, it's inevitable to raise some specific Exceptions:
- UnauthorizedAccessException: some Frames cannot be accessed.
- InvalidOperationException: some Elements/Descendants cannot be accessed.
There's nothing we can do to avoid this: the Elements are not null
, they simply throw these exceptions when we try to access any of their properties.
Here, I'm just catching and ignoring these specific Exceptions: we know we will eventually get them, we cannot avoid it, move on.
public class MovieLink
{
public MovieLink() { }
public int Hash { get; set; }
public string VideoLink { get; set; }
public string ImageLink { get; set; }
}
List<MovieLink> moviesLinks = new List<MovieLink>();
private void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var browser = sender as WebBrowser;
if (browser.ReadyState != WebBrowserReadyState.Complete) return;
var documentFrames = browser.Document.Window.Frames;
foreach (HtmlWindow Frame in documentFrames) {
try {
var videoElement = Frame.Document.Body
.GetElementsByTagName("VIDEO").OfType<HtmlElement>().FirstOrDefault();
if (videoElement != null) {
string videoLink = videoElement.Children[0].GetAttribute("src");
int hash = videoLink.GetHashCode();
if (moviesLinks.Any(m => m.Hash == hash)) {
// Done parsing this URL: remove handler or whatever
// else is planned to move to the next site/page
return;
}
string sourceImage = videoElement.GetAttribute("poster");
moviesLinks.Add(new MovieLink() {
Hash = hash, VideoLink = videoLink, ImageLink = sourceImage
});
}
}
catch (UnauthorizedAccessException) { } // Cannot be avoided: ignore
catch (InvalidOperationException) { } // Cannot be avoided: ignore
}
}
How can I select a html element no matter what frame it is in in selenium?
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
Get element from within an iFrame
var iframe = document.getElementById('iframeId');
var innerDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
You could more simply write:
var iframe = document.getElementById('iframeId');
var innerDoc = iframe.contentDocument || iframe.contentWindow.document;
and the first valid inner doc will be returned.
Once you get the inner doc, you can just access its internals the same way as you would access any element on your current page. (innerDoc.getElementById
...etc.)
IMPORTANT: Make sure that the iframe is on the same domain, otherwise you can't get access to its internals. That would be cross-site scripting.
How can I get an element from within a frameset frame using JavaScript?
You need to get the Document object for the frame.
window.frames[1].document.getElementById('someElementId')
Get element value inside iframe which is nested inside Frame in javascript?
Here's a modified snippet of my answer linked in a comment. Function returns the window
object of an (i)frame with id
passed (id
), or null
, if the (i)frame
is not found. This method works only, if all the (i)frame
s are in the same domain. Also id
s given to (i)frame
elements must be unique throughout all documents involved in the window structure.
function searchFrame(id) { // id = the id of the wanted (i)frame
var result = null, // Stores the result
search = function (iframes) { // Recursively called function
var n; // General loop counter
for (n = 0; n < iframes.length; n++) { // Iterate through all passed windows in (i)frames
if (iframes[n].frameElement.id === id) { // Check the id of the (i)frame
result = iframes[n]; // If found the wanted id, store the window to result
}
if (!result && iframes[n].frames.length > 0) { // Check if result not found and current window has (i)frames
search(iframes[n].frames); // Call search again, pass the windows in current window
}
}
};
search(window.top.frames); // Start searching from the topmost window
return result; // Returns the wanted window if found, null otherwise
}
This function can find a frame
or iframe
with the passed id
regardless where it is placed in the window structure. Also the function can be placed and called in any window. I'd put this to the main page (as global). If the method is needed in subwindows, just call with top.searchFrame(...)
.
Instead of id
s, also name
s can be used, as long as they are also unique. In that case the id
check in searchFrame()
needs to be edited to name
check.
In your case, first you need to give an id
to the target iframe
, then you can use the code for example like this:
var txtClinic = searchFrame('IFRAME_ID').document.getElementById('clinicFlag');
The method above might be a bit overkilling to get a single reference, but it's very helpful, if you have to get these cross-window references multiple times within different windows.
The specific task of yours could be done like this:
var txtClinic = parent.frames[1].frames[0].document.getElementById('clinicFlag');
name
s of the (i)frame
are also handy to use with frames
collection. Instead indices you can use names. Just always chain the reference starting from the main page, i.e. from top
. For example:
var iframeWin = top.frames['frame2'].frames['iframe1'];
Useful reading:
window.top
window.frameElement
window.frames
How to Get element of Iframe?
Try
HtmlDocument.contentWindow.body
I have no idea of c# and what you are doing (I'm a PHP man mainly these days) but it looks to me like you are casting an IFrame element to a HTMLDocument type? This may be wrong, as the IFrame element is not identical with the body
. Is that casting really necessary? Maybe cast it to a HTMLElement instead?
Access in IFrame and SetAttribute value
Your code looks like it should work if you change this line:
coll = webBrowser1.Document.Window.Frames[0].Document.GetElementsByTagName("iframe")
to
coll = webBrowser1.Document.Window.Frames[0].Document.GetElementsByTagName("input");
Here is a working example...
HtmlWindow iframe = webBrowser1.Document.Window.Frames[0];
HtmlElement input = iframe.Document.GetElementsByTagName("input")[0];
input.SetAttribute("value", "Test");
This assumes obviously that you only have at least one iframe element and at least one input element in the child document.
getElementById on element within an iframe
Try this:
Windows.Forms.HtmlWindow frame = WebBrowser1.Document.GetElementById("decrpt_ifr").Document.Window.Frames["decrpt_ifr"];
HtmlElement body = frame.Document.GetElementById("tinymce");
body.InnerHtml = "Hello, World!";
That gets the frame and treats it as a different document (because it is) and then it tries to get the element from its id. Good luck.
Edit: This should do the trick taking advantage of the dynamic
datatype, and InternetExplorer
interface:
private void Form1_Load(object sender, EventArgs e)
{
foreach (InternetExplorer ie in new ShellWindows())
{
if (ie.LocationURL.ToString().IndexOf("tinymce") != -1)
{
IWebBrowserApp wb = (IWebBrowserApp)ie;
wb.Document.Frames.Item[0].document.body.InnerHtml = "<p>Hello, World at </p> " + DateTime.Now.ToString();
}
}
}
Is it possible to pick up DOM element inside of iFrame on click?
I found out why I'm getting UnauthorizedAccessException. It's because my main page is http://localhost/index.html while iFrame src is which violates cross-frame scripting security according to:
http://msdn.microsoft.com/en-us/library/ms171715.aspx
http://msdn.microsoft.com/en-us/library/ms533028.aspx
I've changed iFrame src to use same domain, http://localhost/secondPage.html
then it start working!
Related Topics
Convert Ienumerable to Datatable
How to Get an Htmlelement Value Inside Frames/Iframes
Execute Insert Command and Return Inserted Id in Sql
Jquery Ui Dialog With ASP.NET Button Postback
Append Lines to a File Using a Streamwriter
Jwt Authentication For ASP.NET Web API
Execute a Large SQL Script (With Go Commands)
Setting a Property by Reflection With a String Value
Delete a File Being Used by Another Process
How to Get the Connection String from a Database
Reading Settings from App.Config or Web.Config in .Net
How to Convert Byte Array to String