I'm currently working on a script that allows a user to embed a JavaScript file inside an SVG document to enable panning and zooming of the content. I've tried pretty much every variation of SVG panning and zooming I could locate and have only found one that has consistent cross-browser support. Unfortunately, the script was written with the assumption that the SVG would be written out in the HTML rather than embedded through the object tag. Since I'm working with quite a few pre-rendered SVGs, it would be more efficient to just link to an external JavaScript file in each SVG. Everything has been going well with the conversion thus far, but I've run into a bit of a snag. The script relies on mouse events hitting a div that wraps the SVG object. It looks like the following:
<div id="svgwindow">
<div id="wrapper">
<object id="svg" data="test.svg" type="image/svg+xml"></object>
</div>
</div>
An example of the JavaScript:
parent.document.getElementById("svgwindow").addEventListener("mousewheel", Zoom, false);
This script successfully adds the event so long as the event occurs in an area of svgwindow that the svg object is not populating. As soon as the event occurs overtop of the object the event is not captured. I haven't been able to find any similar instances pf this to help troubleshoot the error, so any help would be great.
Since the <object> tag will capture all events and there's no way to stop it, all you can do is put another <div> absolutely positioned on top of the wrapper <div> that will receive the events.
Of course if you swap to an <image> tag rather than an <object> tag that won't capture events but there are restrictions on images e.g. all data must be in one file and no scripting allowed that may make it unsuitable for you. If it is suitable then that's easiest way to go though.
Related
For the portal I am testing now, I came with the problem that I could not create any xpath locators, after some time I figured out that it was because of an '#document', this cuts the path and makes the simple "copy xpath" to direct the path to a completely different element.
<iframe id="FRAMENAME" src="/webclient/workspace/launch-task/REMbl?ds=BP" width="100%" height="100%" frameborder="0" data-navitemname="navitemname" style="" xpath="1">
#document
<html>
CODE....
</html>
I found the solution for this is it is simply add a switchTo like this:
driver.switchTo().frame("FRAMENAME");
This works and makes the rest of the code to work properly but, takes some extra time processing this command till the code moves to the next line.
So I would like to ask, is there is a better solution for this? something smarter/faster?
I am concerned that when the point where I have lots of scripts comes, the execution time will take too long.
I don't use id locators for example because they are all dynamic so sometimes a xpath is required.
Thank you!
To work with elements inside iframe you must switch to this specific iframe.
Your solution .switchTo().frame("FRAMENAME"); is correct. Selenium does not have any other ways to work with iframe wrappers.
inline frames
As per the documentation in Using inline frames, an inline frame is a construct which embeds a document into an HTML document so that embedded data is displayed inside a subwindow of the browser's window. This does not mean full inclusion and the two documents are independent, and both them are treated as complete documents, instead of treating one as part of the other.
iframe structure and details
Generally, an iframe element is in the form of:
<iframe src="URL" more attributes>
alternative content for browsers which do not
support iframe
</iframe>
Browsers which support iframe display the document referred to by the URL in a subwindow, typically with vertical and/or horizontal scroll bars. Such browsers ignore the content of the iframe element (i.e. everything between the start tag <iframe...> and the end tag </iframe>). Browsers which do not support iframe (or have such support disabled) does the opposite, i.e. process the content as if the <iframe...> and </iframe> tags were not there. Thus, the content matters, despite being ignored by some browsers.
So to summarize, inline frames do not mean an include feature, although it might sometimes serve similar purposes.
Note that, when inline frames are used, the browser (if it supports them) sends a request to the server referred to by the URL in the iframe element, and after getting the requested document displays it inside an inline frame. In this sense inline frames are a joint browser-server issue, but only the browser needs to be specifically iframe-aware; from the server's point of view, there's just a normal HTTP request for a document, and it sends the document without having (or needing) any idea on what the browser is going to do with it.
Something Smarter
As per the best practices while switching to an iframe you need to induce WebDriverWait as follows:
Switch through Frame Name (Java Sample Code):
new WebDriverWait(driver, 20).until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.name("frame_name")));
Switch through iframe XPath (Python Sample Code):
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"//iframe[#id='ptifrmtgtframe' and #name='TargetContent']")))
Switch through iframe CssSelector (C# Sample Code):
new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.FrameToBeAvailableAndSwitchToIt(By.CssSelector("iframe#twitter-widget-0")));
Reference
You can find a couple of relevant discussions in:
Python: How can I select a html element no matter what frame it is in in selenium?
Java: Is it possible to switch to an element in a frame without using driver.switchTo().frame(“frameName”) in Selenium Webdriver Java?
C#: How to wait for a frame to load before locating an element?
tl; dr
Inline frames vs. normal frames
For the portal I am testing now, I came with the problem that I could not create any xpath locators, after some time I figured out that it was because of an '#document', this cuts the path and makes the simple "copy xpath" to direct the path to a completely different element.
<iframe id="FRAMENAME" src="/webclient/workspace/launch-task/REMbl?ds=BP" width="100%" height="100%" frameborder="0" data-navitemname="navitemname" style="" xpath="1">
#document
<html>
CODE....
</html>
I found the solution for this is it is simply add a switchTo like this:
driver.switchTo().frame("FRAMENAME");
This works and makes the rest of the code to work properly but, takes some extra time processing this command till the code moves to the next line.
So I would like to ask, is there is a better solution for this? something smarter/faster?
I am concerned that when the point where I have lots of scripts comes, the execution time will take too long.
I don't use id locators for example because they are all dynamic so sometimes a xpath is required.
Thank you!
To work with elements inside iframe you must switch to this specific iframe.
Your solution .switchTo().frame("FRAMENAME"); is correct. Selenium does not have any other ways to work with iframe wrappers.
inline frames
As per the documentation in Using inline frames, an inline frame is a construct which embeds a document into an HTML document so that embedded data is displayed inside a subwindow of the browser's window. This does not mean full inclusion and the two documents are independent, and both them are treated as complete documents, instead of treating one as part of the other.
iframe structure and details
Generally, an iframe element is in the form of:
<iframe src="URL" more attributes>
alternative content for browsers which do not
support iframe
</iframe>
Browsers which support iframe display the document referred to by the URL in a subwindow, typically with vertical and/or horizontal scroll bars. Such browsers ignore the content of the iframe element (i.e. everything between the start tag <iframe...> and the end tag </iframe>). Browsers which do not support iframe (or have such support disabled) does the opposite, i.e. process the content as if the <iframe...> and </iframe> tags were not there. Thus, the content matters, despite being ignored by some browsers.
So to summarize, inline frames do not mean an include feature, although it might sometimes serve similar purposes.
Note that, when inline frames are used, the browser (if it supports them) sends a request to the server referred to by the URL in the iframe element, and after getting the requested document displays it inside an inline frame. In this sense inline frames are a joint browser-server issue, but only the browser needs to be specifically iframe-aware; from the server's point of view, there's just a normal HTTP request for a document, and it sends the document without having (or needing) any idea on what the browser is going to do with it.
Something Smarter
As per the best practices while switching to an iframe you need to induce WebDriverWait as follows:
Switch through Frame Name (Java Sample Code):
new WebDriverWait(driver, 20).until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.name("frame_name")));
Switch through iframe XPath (Python Sample Code):
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"//iframe[#id='ptifrmtgtframe' and #name='TargetContent']")))
Switch through iframe CssSelector (C# Sample Code):
new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.FrameToBeAvailableAndSwitchToIt(By.CssSelector("iframe#twitter-widget-0")));
Reference
You can find a couple of relevant discussions in:
Python: How can I select a html element no matter what frame it is in in selenium?
Java: Is it possible to switch to an element in a frame without using driver.switchTo().frame(“frameName”) in Selenium Webdriver Java?
C#: How to wait for a frame to load before locating an element?
tl; dr
Inline frames vs. normal frames
I use cefpython inside PySide on Python 2.7. To create advanced 3d transitions between div's I have to cut the div in e.g. 20 parts which I can animate separately. I found no way to do that "natively" so I have to simulate the split by taking a screenshot of a specific element in the dom (I could also take a screenshot of the whole dom and cut out the required element by coordinates.).
How can I take a screenshot of a specific div in my DOM or how could I take a screenshot of the whole visibile part of a Webpage in cefpython and PySide.
JavaScript functions often just render the html into a picture but I need a completely real picture since the user should not know that the animation is created from a picture instead of a real div.
Is there an easy way to capture an element (or the whole currently visible part) of a Webpage using cefpython and PySide (or JavaScript and HTML)?
( By the way: Cefpython embed a chromium browser into PySide.)
You can render contents of the window that embeds a browser to a bitmap. In pyqt/pyside you can render QWidget to QImage. For an example google "qwidget to qimage": https://www.google.com/search?q=qwidget+to+qimage
So sometimes when working with the Moovweb SDK a client will AJAX in a content spot, but it will be in the incorrect area?
I cannot use tritium to move this content, because the area I want to move it to is inserted after page load!
Example:
<div class="where-i-want-to-move-it"></div>
<div class="content-area-i-want-it-moved-from">
<p class="content-i-want-moved">Hi! This was ajaxed in at a later date!</p>
</div>
How can I detect that this p tag was added through AJAX when I cannot control their JS to fire a specific event or fire a different call back?
One option would be to listen to node changes -- see this post -- and assuming you can target the correct mutation event, move the content manually in JavaScript.
Our firm avoids using MutationObserver because older versions of Android and IE don't recognize it.
Instead, we have begun implementing CSS Animations. You can insert CSS into your .ts file to assign animation #keyframes to an element. We then add a JS listener to listen for "webkitAnimationStart". So now when the AJAX'd content loads, the listener is instantly triggered, and we can now manipulate the new DOM using JS.
It is an excellent trick, and is cross-browser compliant when fully implemented.
You can embed an SVG file into into an (X)HTML 5 document:
<object data="anim.svg" id="svganim"/>
or
<img src="anim.svg" alt="embedded SVG"/>
But if anim.svg is animated, the animation will start playing as soon as the page loads.
How can you embed an animated SVG file such that the animation starts out paused? The user can then play the animation by pressing a button (using unpauseAnimations() in Javascript)
An inelegant way
window.onload = function() {
var svg_anim = document.getElementById('svganim').contentDocument.rootElement;
svg_anim.pauseAnimations();
};
Disadvantage: this doesn't work if the embedded SVG is in a different security context from the parent document. Is there a better way?
One way to solve this is to make the animations not start automatically by changing the svg file (to add begin="indefinite" to any animations that start automatically). These animations can then be triggered by a call to beginElement() to the animation elements you want to trigger. If you have many such elements, it's probably easier to use pauseAnimations().
However, with <img> elements you can't start the animations since no events will ever be fed into the svg itself, so that's not going to work there. And scripting is disabled in this case too, so you can't trigger or prevent animations that way either.
With <object>, <embed> or <iframe> you can do scripting, e.g like your suggestion. You can add a script tag as one of the first children of the svg root element of the svg file and in that <script> element call pauseAnimations. But you can also do it from the main document as you suggested.