How to send/dispatch keypress events to an embedded iframe in React? - javascript

I have an iframe of Google Slides which I embedded in my web app.
The iframe implementation is:
<iframe ref={slidesRef} src={src} width='100%' height='100%' allowFullScreen={true}/>
I want to listen to click events on the main web page (while the iframe is not necessarily in focus), then create their corresponding keypress events - and pass them to the iframe, thus making the slideshow iframe to trigger and react to the user key presses.
I tried something like this:
function onButtonClick(e) {
e.stopPropagation();
slidesRef.current.focus();
slidesRef.current.dispatchEvent(new KeyboardEvent('keypress', {
key: 'ArrowRight', which : 39
}));
}
But it doesn't work - it only focuses on the iframe, but doesn't send the arrow key event.
I saw some solutions using the parent document inside the iframe or sending messages with postMessage, but I can't add any code inside my iframe since its origin is an external link.

Due to restrictions imposed by the Same Origin Policy implemented by all modern browsers, I don't think you'll be able to do what you intend to do - this is not related to React, the browser will just not allow for a script on the parent window to trigger any kind DOM event on the child iframe programmatically.
That's why you saw references to the postMessage API when you searched for a solution. That API was introduced to enable cross-origin communication between window objects in a safer, more controlled manner.
The code you've posted would execute any event listener that might appended to the parent window's DOM iframe element, so something like this:
<iframe onkeydown="console.log('keydow')" src="https://google/..." />
Should be triggered by your code. But this is happening on the parent window, the javascript "running" within iframe (i.e. Google's code responsible for changing the current slide) doesn't have access nor is able to "see" that event.
If both the iframe and your webpage were serviced from the same domain (in other words, if they had the same "origin"), what I think you're trying to achieve would be something more in the lines of:
function onButtonClick(e) {
e.stopPropagation()
// Note that we're retrieving a DOM element from the iframe's window object,
// not the parent's one
const iframeBody = slidesRef.current.contentDocument.body;
// Get an element on the iframe's document which has an event listener attached
// to it that changes the current slide
const sliderEl = iframeBody.querySelector('...')
sliderEl.click()
}
But again this'd only work if both the iframe and parent window were considered to have the "same origin" by the browser (which isn't your case).

Albeit significantly more complicated, you could probably achieve something close to what you're looking for (at the cost of rebuilding the design of some webpages, at least) by querying Google Slides using the corresponding Google API.
Check out the corresponding documentation: https://developers.google.com/slides/quickstart/javascript
It may look scary at first, but once you've got used to it, Google APIs don't bite that much, you can even create a service account and grant it access to your document so that your app can read the document and update it without asking the user to authorize your app.

Related

How can I prevent Iframe messing browser's history after interactions with it?

So in my case I use Iframes to attach Grafana to my page (which provides me beautiful and easy to use graphs).
It's possible to notice that Grafana's Iframes triggers a kind of refresh on my Angular page after each interaction of zoom in or zoom out (using mouse clicks) on the graph thus messing broswer's history. I don't see any changes on Iframe's src to justify this page refresh and it doesn't trigger anything apparently (doesn't trigger any onload, for example).
Is this a normal behavior? How can I prevent this?
I am using a scripted dashboard of Grafana version 6.2.2 along with Angular 6.1.
Hoping to help out, some things that I might try in your scenario:
A blank html page with only a grafana Iframe in it. See if it still refreshes the parent page. If not, then maybe the problem is with angular.
You said sandbox breaks the iframe? Maybe play around with different sandbox values. Like allow-scripts and see if it needs one of those values to work
https://www.w3schools.com/tags/att_iframe_sandbox.asp
Maybe try putting the grafana iframe in another iframe. I've never done this before, but maybe it will try to refresh the parent iframe instead of the parent page.
It could be helpful to post your angular html code to the question too. Might be some hints in there.
Without the effective implementation of the iframe is difficult to suggest the best way to act.
The simplest solution that comes in mind is iframe's sandbox attribute:
<iframe src="my_iframe.html" sandbox></iframe>
What's an iframe sandbox ?
The sandbox attribute enables an extra set of restrictions for the content in the iframe.
When the sandbox attribute is present, and it will:
treat the content as being from a unique origin
block form submission
block script execution
disable APIs
prevent links from targeting other browsing contexts
prevent content from using plugins (through , , , or other)
prevent the content to navigate its top-level browsing context
block automatically triggered features
The value of the sandbox attribute can either be just sandbox (then
all restrictions are applied), or a space-separated list of
pre-defined values that will REMOVE the particular restrictions.
Ref: https://www.w3schools.com/tags/att_iframe_sandbox.asp
You can overwrite the <iframe>'s pushState and replaceState functions:
iframe.contentWindow.history.pushState = new Proxy(iframe.contentWindow.history.pushState, {
apply: () => {},
});
iframe.contentWindow.history.replaceState = new Proxy(iframe.contentWindow.history.replaceState, {
apply: () => {},
});

Remove one of the two play button on google drive iframe embed

Can anybody help me on this, when I embed a google drive video using an iframe it has two play button, how to remove one of this? This happens only in Chrome and Safari so please test it on those browsers.
<iframe src="https://drive.google.com/file/d/1mNaIx2U3m7zL9FW-wksaI1m_rL5Oh47v/preview" width="400" height="300" allowfullscreen="true"></iframe>
As you can see on the iframe that you have to click the play button twice.
Also I cannot use html5 player since most of the videos are large.
here is my fiddle https://jsfiddle.net/1tav74q8/
TL;DR; it's impossible to directly modify the content inside the iframe that comes from different origin that you don't own.
If your iframe and the host have the same origin (domain), interaction between them is easy, simply access the document object to get the element. Example using jQuery:
To hide a button on host element from iframe, use: window.parent.jQuery('button').hide().
To hide a button on iframe element from host, use: jQuery('iframe')[0].contentWindow.jQuery('button').hide()
HOWEVER, if the host and the iframe doesn't have same origin, interaction between each of them are strictly limited. you cannot instruct certain operation directly from the host to the iframe's javascript window or document, and vice versa. And from that, it's safe to say that accessing directly the iframe's DOM element from the host is definitely impossible.
Explanation about Cross-origin script API accessSection from MDN.
JavaScript APIs such as iframe.contentWindow, window.parent, window.open and window.opener allow documents to directly reference each other. When the two documents do not have the same origin, these references provide very limited access to Window and Location objects, as described in the next two sections.
To communicate further between documents from different origins, use window.postMessage.
You can use the window.postMessage function and "message" event listener, to send and receive a message between host and iframe (and vice versa). In your case you would need to sent a message from host to instruct the iframe to hide a button. Then on the receiver end (iframe), get the desired button then hide it. But this technique only works if you own those two origin, you need to declare the "message" event on the iframe end, and since your iframe source is drive.google.com which I assume you are not the owner, then it's definitely impossible.
More explanation: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
As far as i am aware of you can not edit iframe content which does not originate on your own server. But i am not sure..
Check this post for a sample
Courseweb
Stackoverflow
Also interesting from this link:
stackoverflow
Get the unique video identifier (0B6VvIWR7judDX25yUGxVNERWUj)
Put into this html:

Issue Tracking mouse movement in Iframe

Details :-
In Our Project, we need to implement a functionality where we need to logout the user if he/she is inactive for 5 minutes. We need to consider user as inactive if he/she has not moved mouse for 5 mins.
We are able to achieve this in all normal pages but facing difficulties to implement in one page where we have an iframe in a page and that iframe plays videos and other swf, mp4 files. Also inside an iframe, there is another frame which acts as a wrapper for the content.
In above page, we are not able to track mouse movement and hence not able to detect whether user is active or not.
After lots of research, we have implemented code which works fine if we run through browser console but doesn't work when I keep this on page.
Has someone worked on similar functionality or know anything related to this? It would be great if someone can help me for this functionality.
Note :- Also there are click in Iframes
The iframe's source is technically speaking its own document (documentception?). The trick is to listen to the mouse events inside this document. Though I don't if this is allowed by the same origin policy, you could always give it a try.
Using plain JS:
var iframeDocument = document.querySelector('iframe').contentDocument;
iframeDocument.addEventListener('mousemove', function (event) {
console.log(event.pageX, event.pageY);
});
Using jQuery:
$('iframe').contents().mousemove(function(e) {
console.log(e.pageX, e.pageY);
});
You could go even further by dispatching all events made inside the iframe's document to the parent document. This gives you the advantage to use just one listener for all events, whether they come from inside the <iframe> or not.

iFrame alternative for getting cross-domain URL

I'm creating a web-based game. As a part of it, the player must go to the home page of a website, and follow a series of link until he reaches to the "destination page" in the minor time possible. Think of it as a sort of link treasure hunt.
In order to control the game, I created a page with a javascript timer on the top, and an iFrame showing the website to surf (which is not mine and I have no control over it).
However this has a huge fault: it is not possible to detect the current cross-domain URL of the iFrame, so my page cannot know when the user has arrived to the destination page and stop the game.
Is there any alternative way I could achieve this?
Is there any alternative way I could achieve this?
No, there is not.
The Same Origin Policy completely prevents you from accessing properties of any window object that displays content from another domain.
(If one was a subdomain of the other, then document.domain would help. But from your problem description it doesn’t sound like that was the case.)
And besides the technical issue: Generating traffic and clicks (that will skew their statistics/analytics) on a site that you don’t own, just for the purpose of being able to present your own game, would be rather unfair (aka a “dick move”.)
You can wait upto the contentWindow of the iFrame is resolved and you have a reference to the iFrame's window, after you changed the source pragmatically.
once you have that, you can add a onload handler on iFrame's <body> and inject some postMessage call inside that handler, so once the body is loaded of the iFrame's window it wil call postMessage from the onload handler of body and it will notify your outer window, of-course you have to add message listener in outer window using window.addEventListener("message", callback)

Webpage limitations when wrapped inside an IFrame?

I am developing a webpage which our customers want to insert on their websites by wrapping my page in an iframe (cross domain). I don't need to interact with the parent or know anything about whats outside the iframe.
I am using HTML, CSS, Javascript and Webservices.
Question: How am I limited inside an iframe compared to if my page was running outside the iframe?
You're not. Any JS linked within the iframe from your domain will act in the context of the iframe. Aside from being crammed into an unusual container it should work the same as it would if it was loaded independently.
If your needs should change however, there are ways to send signals between parent frame and iframe if both pages have JS written to cooperate. There's methods using the # in URLs which can be read by the parent and don't force page reloads and I believe they share the window.resize event which can be fired manually without actually resizing the window.
UPDATE: There are far better ways to communicate between cross-domain iframes now than there used to be. Naturally you'll still require cooperating JS on both ends but you can use window.postMessage rather than triggering messages via window.resize and data after a hash symbol in the URL. That was a cool trick though.
When creating links you should have in mind to maybe use the target-attribute of the a-tag if you want to create a link for the parent window. Otherwise the new page would be loaded into the iframe.

Categories

Resources