Reload an IFRAME without adding to the history - javascript

I'm changing an IFRAME's src in order to reload it, its working fine and firing the onload event when its HTML loads.
But it adds an entry to the history, which I don't want. Is there any way to reload an IFRAME and yet not affect the history?

Using replace() is only an option with your own domain iframes. It fails to work on remote sites (eg: a twitter button) and requires some browser-specific knowledge to reliably access the child window.
Instead, just remove the iframe element and construct a new one in the same spot. History items are only created when you modify the src attribute after it is in the DOM, so make sure to set it before the append.
Edit: JDandChips rightly mentions that you can remove from DOM, modifiy, and re-append. Constructing fresh is not required.

You can use javascript location.replace:
window.location.replace('...html');
Replace the current document with the
one at the provided URL. The
difference from the assign() method is
that after using replace() the current
page will not be saved in session
history, meaning the user won't be
able to use the Back button to
navigate to it.

Like Greg said above, the .replace() function is how to do this. I can't seem to figure out how to reply to his answer*, but the trick is to reference the iFrames contentWindow property.
var ifr = document.getElementById("iframeId");
ifr.contentWindow.location.replace("newLocation.html");
*Just learned I need more reputation to comment on an answer.

An alternative method to recreating the iframe would be to remove the iframe from the DOM, change the src and then re add it.
In many ways this is similar to the replace() suggestion, but I had some issues when I tried that approach with History.js and managing states manually.
var container = iframe.parent();
iframe.remove();
iframe.attr('src', 'about:blank');
container.append(iframe);

One solution is to use the object tag rather than the iframe tag.
Replacing this:
<iframe src="http://yourpage"/>
By this:
<object type="text/html" data="http://yourpage"/>
will allow you to update the data attribute without affecting the history. This is useful if you use a declarative framework such as React.js where you are not supposed to do any imperative command to update the DOM.
More details on differences between iframe and object: Use of Iframe or Object tag to embed web pages in another

Try to use this function to replace old iframe with new iframe which is copied from old one:
function setIFrameSrc(idFrame, url) {
var originalFrame = document.getElementById(idFrame);
var newFrame = document.createElement("iframe");
newFrame.id = originalFrame.getAttribute("id");
newFrame.width = originalFrame.getAttribute("width");
newFrame.height = originalFrame.getAttribute("height");
newFrame.src = url;
var parent = originalFrame.parentNode;
parent.replaceChild(newFrame, originalFrame);
}
Use it like this:
setIFrameSrc("idframe", "test.html");
This way will not add URL of iframe to browser history.

Use the replace method to bypass the addition of the iframe's URL to history:
HTML:
<iframe id="myIframe" width="100%" height="400" src="#"></iframe>
JavaScript:
var ifr = document.getElementById('mIframe')
if (ifr) {
ifr.contentWindow.location.replace('http://www.blabla.com')
}

JDandChips answer worked for me on a cross origin iframe (youtube), here it is in vanilla JS (without JQuery):
const container = iframe.parentElement;
container.removeChild(iframe);
iframe.setAttribute('src', 'about:blank');
container.appendChild(iframe);

The most simple and fast loading solution
Use window.location.replace to not update the history when loading the page or the frame.
For links it looks like this:
<a href="#" onclick="YourTarget.location.replace ('http://www.YourPage.com');">
The targeted Link</a>
or
<a href="javascript:YourTarget.location.replace ('http://www.YourPage.com');">
The targeted Link</a>
But if you want it not to act from link but to act automatically when loading the frame then from the iframe you should put this in the iframe file body section:
onload="window.location.replace ('http://www.YourPage.com');"
If for some reason the onload does not load in the iframe then put your target frame name instead of window in the syntax like this:
onload="YourTarget.location.replace ('http://www.YourPage.com');"
NOTE: For this script all onclick, onmouseover, onmouseout , onload and href="javascript:" will work.

#JDandChips has a great answer above, but the syntax should be updated from parent() to parentElement:
var container = iframe.parentElement;
iframe.remove();
iframe.attr('src', 'about:blank');
container.append(iframe);

Related

Iframe manipulation [duplicate]

Is it possible to change styles of a div that resides inside an iframe on the page using CSS only?
You need JavaScript. It is the same as doing it in the parent page, except you must prefix your JavaScript command with the name of the iframe.
Remember, the same origin policy applies, so you can only do this to an iframe element which is coming from your own server.
I use the Prototype framework to make it easier:
frame1.$('mydiv').style.border = '1px solid #000000'
or
frame1.$('mydiv').addClassName('withborder')
In short no.
You can not apply CSS to HTML that is loaded in an iframe, unless you have control over the page loaded in the iframe due to cross-domain resource restrictions.
Yes. Take a look at this other thread for details:
How to apply CSS to iframe?
const cssLink = document.createElement("link");
cssLink.href = "style.css";
cssLink.rel = "stylesheet";
cssLink.type = "text/css";
frames['frame1'].contentWindow.document.body.appendChild(cssLink);
// ^frame1 is the #id of the iframe: <iframe id="frame1">
You can retrieve the contents of an iframe first and then use jQuery selectors against them as usual.
$("#iframe-id").contents().find("img").attr("style","width:100%;height:100%")
$("#iframe-id").contents().find("img").addClass("fancy-zoom")
$("#iframe-id").contents().find("img").onclick(function(){ zoomit($(this)); });
Good Luck!
The quick answer is: No, sorry.
It's not possible using just CSS. You basically need to have control over the iframe content in order to style it. There are methods using javascript or your web language of choice (which I've read a little about, but am not to familiar with myself) to insert some needed styles dynamically, but you would need direct control over the iframe content, which it sounds like you do not have.
Use Jquery and wait till the source is loaded,
This is how I have achieved(Used angular interval, you can use javascript setInterval method):
var addCssToIframe = function() {
if ($('#myIframe').contents().find("head") != undefined) {
$('#myIframe')
.contents()
.find("head")
.append(
'<link rel="stylesheet" href="app/css/iframe.css" type="text/css" />');
$interval.cancel(addCssInterval);
}
};
var addCssInterval = $interval(addCssToIframe, 500, 0, false);
Combining the different solutions, this is what worked for me.
$(document).ready(function () {
$('iframe').on('load', function() {
$("iframe").contents().find("#back-link").css("display", "none");
});
});
Apparently it can be done via jQuery:
$('iframe').load( function() {
$('iframe').contents().find("head")
.append($("<style type='text/css'> .my-class{display:none;} </style>"));
});
https://stackoverflow.com/a/13959836/1625795
probably not the way you are thinking. the iframe would have to <link> in the css file too. AND you can't do it even with javascript if it's on a different domain.
Not possible from client side . A javascript error will be raised "Error: Permission denied to access property "document"" since the Iframe is not part of your domaine.
The only solution is to fetch the page from the server side code and change the needed CSS.
A sort of hack-ish way of doing things is like Eugene said. I ended up following his code and linking to my custom Css for the page. The problem for me was that, With a twitter timeline you have to do some sidestepping of twitter to override their code a smidgen. Now we have a rolling timeline with our css to it, I.E. Larger font, proper line height and making the scrollbar hidden for heights larger than their limits.
var c = document.createElement('link');
setTimeout(frames[0].document.body.appendChild(c),500); // Mileage varies by connection. Bump 500 a bit higher if necessary
Just add this and all works well:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
If the iframe comes from another server, you will have CORS ERRORS like:
Uncaught DOMException: Blocked a frame with origin "https://your-site.com" from accessing a cross-origin frame.
Only in the case you have control of both pages, you can use https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage to safely send messages like this:
On you main site(one that loads the iframe):
const iframe = document.querySelector('#frame-id');
iframe.contentWindow.postMessage(/*any variable or object here*/, 'https://iframe-site.example.com');
on the iframe site:
// Called sometime after postMessage is called
window.addEventListener("message", (event) => {
// Do we trust the sender of this message?
if (event.origin !== "http://your-main-site.com")
return;
...
...
});
Yes, it's possible although cumbersome. You would need to print/echo the HTML of the page into the body of your page then apply a CSS rule change function. Using the same examples given above, you would essentially be using a parsing method of finding the divs in the page, and then applying the CSS to it and then reprinting/echoing it out to the end user. I don't need this so I don't want to code that function into every item in the CSS of another webpage just to aphtply.
References:
Printing content of IFRAME
Accessing and printing HTML source code using PHP or JavaScript
http://www.w3schools.com/js/js_htmldom_html.asp
http://www.w3schools.com/js/js_htmldom_css.asp

iframe with dynamically defined url is empty

I need to set a URL to iframe dynamically, but when I do so, the iframe is empty.
Here is the iframe's definition:
<html:iframe id="myFrame" width="700px" height="500px"></html:iframe>
And here how I tried to set the URL:
var sRedirectUrl = "http://google.com"; //dynamically defined url
var sIframeId = this.getView().byId(this.createId("myFrame")).getId();
$("#"+sIframeId).attr("src",sRedirectUrl);
Here is jsbin example.
I also tried to do the following:
Definition:
<IconTabFilter id="iframe_container">
</IconTabFilter>
Url set:
var sRedirectUrl = "http://google.com"; //dynamically defined url
this.getView().byId(this.createId("iframe_container")).addContent(
new sap.ui.core.HTML({
content: "<iframe scr=\""+sRedirectUrl+"\" width='700px' height='700px'></iframe>"
})
);
And it doesn't work also.
What am I missing here?
Thank you.
UPDATE
Hard coding a generated link into iframe is working fine:
<html:iframe id="myiframe" src="https://generated_link_to_some_site.com?uniqueId"></html:iframe>
You are doing right, but Google does not allow iFrame, try with test.com in your jsbin instead and it will work : http://jsbin.com/vuqujoxupe/1/edit?html,output
The reason why iframe was empty is very simple - I tried to call the element before it was rendered.
It took me 3 days to realize that. Don't be like me.

Loading the Youtube Player API inside an iframe

I have a website with several internal (i.e. without src) iframes inside, where I want to include a Youtube player (using its API). In short, I want players inside iframes, managed by the parent page.
Problem is that the code initialization, AFAIK, is not working with iframes. For example:
var player = new YT.Player('video-player1', {});
The problem, as you might have guessed, is that you can't define the document in which that ID is contained. In jQuery, I would use something like:
$("video-player1", frames["iframe1"].document)
Is there any workaround for this? The only solution I can see is, obviously, loading the YT api in every iframe and working inside of any iframe, but that would mean refactorizing a lot of logic from my application, besides the additional cost of several loads of the Youtube JS files.
I'm sorry mate. The solution to your problem is "easy" to code/implement but painful and difficult to maintain.
Youtube API does not allow to embed a YT.Player object within an Iframe (e.g. div within an iframe), because it looks for the 'player' node within the window object and not in the iframe document.
So, a quick hotfix for this would be to save a copy of the API files and modify them to add this functionality. Obviously, from that moment on, it is your responsability to serve these files and also to update them in order the files do not get deprecated.
The solution would be (I take for granted JQuery is loaded before Youtube API):
Using the base example provided at https://developers.google.com/youtube/iframe_api_reference#Getting_Started I guess you have this
<iframe id="if"></iframe> instead of <div id="player"></div> and that later on you append the player div inside that iframe.
<body>
<iframe id="if"></iframe>
<script>
$('#if').contents().find('body').append($('<div id="player"></div>'));
// ...
So, whem defining onYoutubeAPIReady(), you must add 1 parameter to the YT.Player constructor:
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', { /* options */ }, $('#if') );
That is $('#if'), the iframe element where you want to embed the player.
iframe_api
In this file you just need to modify the src of the script it loads:
from
a.src = 'http://s.ytimg.com/yts/jsbin/www-widgetapi-vflOb0oo1.js
to a.src = './widget.js'; (widget.js is your copy of www-widgetapi-vflOb0oo1.js).
Finally on widget.js:
Here you must modify this 2 functions: function S(a,b) and function Y(a,b).
First function Y(a,b) to function Y(a,b,c) in order to get the iframe parameter. Then in its body you change S.call(this,a,new nb(b)); to S.call(this,a,new nb(b),c);
Second, function S(a,b) to function S(a,b, dom) and c = document to
c= dom === undefined ? document : dom.contents()[0].
Now you have a Youtube player inside your iframe and you are able to use it from the parent window.
I hope it is useful! ;)

Stop a Vimeo Video with Jquery

I need to stop a Vimeo video embedded with new oembed api (universal player) but when I try to add an event I get this error:
Uncaught TypeError: Object #<an HTMLIFrameElement> has no method 'addEvent'
But I don't why I get this error, I added jquery and the frogaloop api, also I added ids to the iframes, but it still doesn't work: :(
The full code is here:
http://tv.bisaccia.info
Eli, please edit your post. As Joe said, you are partially misinformed. While postMessage is needed for cross-domain communication, it is implemented through a DOM method added by a call to "Froogaloop.init();"
is_embed_iframe = _this.iframe_pattern.test(cur_frame.getAttribute('src'));
if (is_embed_iframe) {
cur_frame.api = _that.api;
cur_frame.get = _that.get;
cur_frame.addEvent = _that.addEvent;
}
Note: you will need to grab froogaloop.js (or the min variant) from the Vimeo site.
Be sure the iFrame "src" is set prior to calling init(), otherwise froogaloop will do nothing.
As per Mike's suggestion, invoking:
Froogaloop.init();
Does make the control API work. In my case:
<iframe id="player_1" src="http://player.vimeo.com/video/26859570?js_api=1&js_swf_id=player_1&title=0&byline=0&portrait=0" width="620" height="354" frameborder="0"></iframe>
<script>
$(document).ready(function() {
Froogaloop.init();
$("#player_1").moogaloop({
load: function(element) {
$("#segment1").click(function() { element.moogaloop('seekTo', "7"); });
}
});
});
</script>
Weird... Moogaloop's author demo page does work without the init() call. Anyway, worked for me.
Thanks for your time!
This is not the correct answer, but may work for your situation as it did for mine. I simply wanted to stop my Vimeo from playing when I closed its containing DOM element. I was collapsing its container and that hid it visually but the audio continued to play and use browser resources unnecessarily.
What I do now is simply store the iframe in a variable, remove it from the DOM, then replace it immediately. I have NOT tested across browsers, only the latest version of Chrome and Safari Mobile.
var container = $("#VimeoContainer");
var iframe = container.find("iframe");
iframe.remove();
container.append(iframe);
Again, Froogaloop is really the way to go, however I've had issues with it in the past so for this situation I was looking for something simple. Obviously you could do this without JQuery with the same results.
You can't.
There's no DOM addEvent method.
You don't have cross-domain access to Vimeo, so you are not permitted to have JavaScript interface with the iframe's document or abstract view.
If you wanted to interface with Vimeo via JavaScript, you would have to get them to implement a postMessage API that also accepts your domain.

Is there a light-weight client-side HTML include method?

I'm looking for a light weight method for client-side includes of HTML files. In particular, I want to enable client-side includes of publication pages of researchr.org, on third party web pages. For example, I'd like to export a page like
http://researchr.org/profile/eelcovisser/publications
(probably just the publications box of that page.)
Using an iframe it is possible to include HTML pages:
<iframe class="foo" style="height: 50em;" width="100%" frameborder="0"
src="http://researchr.org/profile/eelcovisser/publications">
</iframe>
However, iframes require specification of a fixed height, while the pages I'm exporting don't have a fixed height. The result has an ugly scrollbar:
http://swerl.tudelft.nl/bin/view/EelcoVisser/PublicationsResearchr
I found one reference to a method that appears to be appealing
http://www.webdeveloper.com/forum/archive/index.php/t-26436.html
It uses an iframe to import the html, and then a javascript call from the included document to a function defined in the including document, which places the contents of the body of the included file in a div of the including file. This does not work in my scenario, probably due to the same origin policy for javascript, i.e. the including and included page are not from the same domain (which is the whole point).
Any ideas for solving this? Which could be either:
a CSS trick to make the height of the iframe flexible
a javascript technique to lift the contents of the iframe to a div in the including page
some other approach I've overlooked
Requirement: the code to include on should be minimal.
No. The same-origin policy prevents you from doing any of that stuff (and rightly). You will have to go server-side, have a script on your server access that page and copy its contents into your own page (prefeably at build-time/in the background; you could do it at access-time or via AJAX but that would involve a lot of scraping traffic between your server and theirs, which may not be appreciated.
Or just put up with the scrollbar or make the iframe very tall.
As far as I know there is no CSS trick, the only way is to query the iFrame's document.documentElement.offsetHeight or scrollHeight, depending on which is higher, take that value and apply it on the iframe's css height ( add the + 'px' ).
try this ajax with cross domain capability
Why don't you use AJAX?
Try this:
<div id="content"></div>
<script type="text/javascript">
function AJAXObj () {
var obj = null;
if (window.XMLHttpRequest) {
obj = new XMLHttpRequest();
} else if (window.ActiveXObject) {
obj = new ActiveXObject("Microsoft.XMLHTTP");
}
return obj;
}
var retriever = new AJAXObj();
function getContent(url)
{
if (retriever != null) {
retriever.open('GET', url, true);
retriever.onreadystatechange = function() {
if (retriever.readyState == 4) {
document.getElementsById('content').innerHTML(retriever.responseText);
}
}
retriever.send(null);
}
}
getContent('http://researchr.org/profile/eelcovisser/publications');
</script>
And then, you can parse the received page content with JS with regular expressions, extracting whatever content you want from that page.
Edit:
Sorry, I guess I missed the fact that it's a different domain. But as ceejayoz said, you could use a proxy for that.
If you're using jQuery, you can use the load method to retrieve a page via AJAX, optionally scrape content from it, and inject it into an existing element. The only problem is that it requires JavaScript.

Categories