I am trying to dynamically adjust the height of an iFrame on a web page depending on the content within the iFrame via some JavaScript.
My problem is when I have the script directly on the page in a <script> tag it works fine. When I stuff the code in to a separate js file and link to it- it doesn't work!
<iframe id='StatusModule' onload='FrameManager.registerFrame(this)' src='http://randomdomain.dk/StatusModule.aspx'></iframe>
<script type='text/javascript' src='http://randomdomain.dk/FrameManager.js'></script>
It gives me the error:
Uncaught ReferenceError: FrameManager is not defined
Can this really be true? Has it something to do with the page life cycle?
Ps. I guess the JavaScript code is irrelevant, as we not it works.
UPDATE: I think this might have something to do with secure http (https) and the different browsers in some weird way. I noticed that the script actually worked in Firefox. Or rather I'm not sure if its the script, or just Firefox's functionality that resizes iframes automatically depending on the content. It doesn't give me any error though.
If I then add https to the script url reference, the scripts work in IE and Chrome - but not in Firefox. Function reference error! This just got weird!
UPDATE #2: Its not a Firefox function that resizes the iframe. Its the actual script that works (without https).
UPDATE #3: The JavaScript. Works fine if I put it directly into a script tag.
var FrameManager = {
currentFrameId: '',
currentFrameHeight: 0,
lastFrameId: '',
lastFrameHeight: 0,
resizeTimerId: null,
init: function () {
if (FrameManager.resizeTimerId == null) {
FrameManager.resizeTimerId = window.setInterval(FrameManager.resizeFrames, 0);
}
},
resizeFrames: function () {
FrameManager.retrieveFrameIdAndHeight();
if ((FrameManager.currentFrameId != FrameManager.lastFrameId) || (FrameManager.currentFrameHeight != FrameManager.lastFrameHeight)) {
var iframe = document.getElementById(FrameManager.currentFrameId.toString());
if (iframe == null) return;
iframe.style.height = FrameManager.currentFrameHeight.toString() + "px";
FrameManager.lastFrameId = FrameManager.currentFrameId;
FrameManager.lastFrameHeight = FrameManager.currentFrameHeight;
window.location.hash = '';
}
},
retrieveFrameIdAndHeight: function () {
if (window.location.hash.length == 0) return;
var hashValue = window.location.hash.substring(1);
if ((hashValue == null) || (hashValue.length == 0)) return;
var pairs = hashValue.split('&');
if ((pairs != null) && (pairs.length > 0)) {
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
if ((pair != null) && (pair.length > 0)) {
if (pair[0] == 'frameId') {
if ((pair[1] != null) && (pair[1].length > 0)) {
FrameManager.currentFrameId = pair[1];
}
} else if (pair[0] == 'height') {
var height = parseInt(pair[1]);
if (!isNaN(height)) {
FrameManager.currentFrameHeight = height;
//FrameManager.currentFrameHeight += 5;
}
}
}
}
}
},
registerFrame: function (frame) {
var currentLocation = location.href;
var hashIndex = currentLocation.indexOf('#');
if (hashIndex > -1) {
currentLocation = currentLocation.substring(0, hashIndex);
}
frame.contentWindow.location = frame.src + '&frameId=' + frame.id + '#' + currentLocation;
}
};
window.setTimeout(FrameManager.init, 0);
UPDATE #4: Alright I did as ShadowWizard and TheZuck suggested:
<script type="text/javascript">
var iframe = document.createElement("iframe");
iframe.src = "http://www.randomdomain.dk/StatusWebModule.aspx";
iframe.width = '100%';
iframe.id = 'StatusModule';
iframe.scrolling = 'no';
if (iframe.attachEvent) {
iframe.attachEvent("onload", function () {
FrameManager.registerFrame(iframe);
});
} else {
iframe.onload = function () {
FrameManager.registerFrame(iframe);
};
}
document.getElementById('framecontainer').appendChild(iframe);
</script>
With HTTP as URL its work on IE and Firefox - not Chrome. If I set it to HTTPS it works on Chrome and IE - Not Firefox. Same error:
"ReferenceError: FrameManager is not defined".
What is going on here?
a couple of things:
I would bet on a race condition when you have two independent
resources which are supposed to be loaded concurrently. You can
easily check this by writing to log (or to document, whichever works
for you) when both finish loading (i.e. add a little script in the
iframe to dynamically add the time to the content or write to log if
you're using chrome, do that in the external script file as well,
and see if they post the time in a specific order when this fails). In your case, if the script appears before the iframe, and you don't mark it as async, it should be loaded before the iframe is fetched, so it would seem strange for the iframe not to find it due to a race condition. I would bet on (3) in that case.
Assuming there is such an issue (and if there isn't now, when you go
out into the real world it will be), a better way to do this is to
make sure both behave well in case the other loads first. In your
case, I would tell the iframe to add itself to a local variable
independent of the script, and would tell the script to check if the
iframe registered when it loads, and after that in recurring
intervals until it finds the iframe.
If the page the script is loaded into is not in the same domain
as the iframe (note that it doesn't matter where the script comes
from, it only matters what the page's domain is), (or even the same
protocol as someone mentioned here), you will not be able to access
the content so you won't be able to resize according to what the
content is. I'm not sure about the onload method, if it's considered part of the wrapping page or part of the internal iframe.
Check out this question, it sounds relevant to your case:
There's also an interesting article here about this.
I think that your frame is loaded before the script, so "FrameManager" does not exist yet when the iframe has finished loading.
Related
Test page: https://jsfiddle.net/y25rk55w/
On this test page you can see 3 <iframe>'s embeded into each other. Each <iframe> contains a <script> tag in it's <head> tag.
The problem is: only the <script> in the first <iframe> will be loaded by the browser. The other two <script> tags will be present in the dom but the browser will never even try to load them. The problem is not browser specific, it can be reroduced in chrome, firefox, ie. The problem cannot be fixed by adding timeouts or waiting before appending the scripts. It seems to be important that all the iframes have programatically generated content; if you replace this iframes with iframes with actual src links, the problem will disappear.
The question is: how can I actually load a script into iframes 2 and 3?
Full test code:
// It doesn't matter if the scripts exist or not
// Browser won't try to load them either way
var scripts = [
'//testdomain.test/script1.js',
'//testdomain.test/script2.js',
'//testdomain.test/script3.js'
];
function createIFrame(win, onCreated) {
var iframe = win.document.createElement('iframe');
iframe.onload = function () {
onCreated(iframe);
};
win.document.body.appendChild(iframe);
}
function loadScript(win, url) {
var script = win.document.createElement('script');
script.src = url;
script.onload = function() {
console.log("Script " + url + " is loaded.");
};
win.document.getElementsByTagName('head')[0].appendChild(script);
}
createIFrame(window, function(iframe1) {
loadScript(iframe1.contentWindow, scripts[0]);
createIFrame(iframe1.contentWindow, function (iframe2) {
loadScript(iframe2.contentWindow, scripts[1]);
createIFrame(iframe2.contentWindow, function (iframe3) {
loadScript(iframe3.contentWindow, scripts[2]);
});
});
});
Your code is working fine --> http://plnkr.co/edit/vQGsyD7JxZiDlg6EZvK4?p=preview
Make sure you execute createIFrame on window.onload or DOMContentLoaded.
var scripts = [
'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.js',
'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.2/jquery.js',
'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js'
];
function createIFrame(win, onCreated) {
var iframe = win.document.createElement('iframe');
iframe.onload = function () {
onCreated(iframe);
};
win.document.body.appendChild(iframe);
}
function loadScript(win, url) {
var script = win.document.createElement('script');
script.src = url;
script.onload = function() {
console.log("Script " + url + " is loaded.");
};
win.document.getElementsByTagName('head')[0].appendChild(script);
}
window.onload = function(){
createIFrame(window, function(iframe1) {
loadScript(iframe1.contentWindow, scripts[0]);
createIFrame(iframe1.contentWindow, function (iframe2) {
loadScript(iframe2.contentWindow, scripts[1]);
createIFrame(iframe2.contentWindow, function (iframe3) {
loadScript(iframe3.contentWindow, scripts[2]);
});
});
});
};
In the question you can see that I was ommiting the protocol:
/* This is valid to omit the http:/https: protocol.
In that case, browser should automatically append
protocol used by the parent page */
var scripts = [
'//testdomain.test/script1.js',
'//testdomain.test/script2.js',
'//testdomain.test/script3.js'
];
The thing is, programatically created iframes have protocol about: (or javascript:, depending on how you create them). I still can't explain why the first script was loading or why the other two scripts were not showing up in the network tab at all, but I guess it's not very important.
The solution: either explicitly use https:// or programatically append protocol using something like the following code:
function appendSchema(win, url) {
if (url.startsWith('//')) {
var protocol = 'https:';
try {
var wPrev = undefined;
var wCur = win;
while (wPrev != wCur) {
console.log(wCur.location.protocol);
if (wCur.location.protocol.startsWith("http")) {
protocol = wCur.location.protocol;
break;
}
wPrev = wCur;
wCur = wCur.parent;
}
} catch (e) {
/* We cannot get protocol of a cross-site iframe.
* So in case we are inside cross-site iframe, and
* there are no http/https iframes before it,
* we will just use https: */
}
return protocol + url;
}
return url;
}
I've been successful using a simpler method than what the OP proposes in the self-answer. I produce the URLs using:
new URL(scriptURL, window.location.href).toString();
where scriptURL is the URL that needs to be fixed to get a proper protocol and window is the parent of the iframe element that holds the scripts. This can take care of scenarios that differ from the OPs example URLs: like relative URLs (../foo.js) or absolute URLs that don't start with a host (/foo.js). The above code is sufficient in my case.
If I were to replicate the search through the window hierarchy that the OP used, I'd probably do something like the following. This is TypeScript code. Strip out the type annotations to get plain JavaScript.
function url(win: Window, path: string): string {
// We search up the window hierarchy for the first window which uses
// a protocol that starts with "http".
while (true) {
if (win.location.protocol.startsWith("http")) {
// Interpret the path relative to that window's href. So the path
// will acquire the protocol used by the window. And the less we
// specify in `path`, the more it gets from the window. For
// instance, if path is "/foo.js", then the host name will also be
// acquired from the window's location.
return new URL(path, win.location.href).toString();
}
// We searched all the way to the top and found nothing useful.
if (win === win.parent) {
break;
}
win = win.parent;
}
// I've got a big problem on my hands if there's nothing that works.
throw new Error("cannot normalize the URL");
}
I don't have a default return value if the window chain yield nothings useful because that would indicate a much larger issue than the issue of producing URLs. There'd be something wrong elsewhere in my setup.
I have read up on all issues regarding Safari and blank printing. It seems that a white flash happens, re-rendering the page, and content of the iframe is lost before a print dialog can grab it.
Here is my javascript - It works in all browsers except safari. It brings up the dialog, but prints a blank page.
function PrintPopupCode(id) {
framedoc = document;
var popupFrame = $(framedoc).find("#" + id + '\\!PopupFrame');
var icontentWindow = popupFrame[0].contentWindow || popupFrame[0].contentDocument;
icontentWindow.focus();
icontentWindow.print();
}
function PrintPopup(id) {
setTimeout(function () { PrintPopupCode(id) }, 3000);
}
I have set a timeout, i previously read it would help if the transfer of content took sometime, but it has not helped.
I have also tried with printElement() function on the icontentWindow variable, but it does not support this method.
Print Element Method
This is all in a .js file, and not on the page. I have tried on the page, but the same thing happens.
Help?
Maybe you should try this:
function PrintPopupCode(id) {
framedoc = document;
var popupFrame = $(framedoc).find("#" + id + '\\!PopupFrame');
var icontentWindow = popupFrame[0].contentWindow || popupFrame[0].contentDocument;
icontentWindow.focus();
setTimeout(icontentWindow.print, 3000);
}
function PrintPopup(id) {
PrintPopupCode(id);
}
Background
The relationship and hierachy of this pages are :
-A.html (domain : domain1.com)
----B.html (domain : domain2.com)
--------C.html (domain : domain1.com)
what's more,
B.html was included inside A.html by means of Iframe (the id of iframe is "mainIframe");
C.html was included inside B.html by means of Iframe (the id of iframe is "proxyIframe");
Issues
I have below functions in C.html(iframe with id "proxyIframe"):
function updateHeight() {
mainIframe = parent.parent.document.getElementById('mainIframe');
newHeigh = parent.parent.frames["mainIframe"].frames["proxyIframe"].location.hash.substr(1);
mainIframe.style.height = newHeigh;
}
function canUpdateHeight() {
if(parent!=null && parent.parent!=null && parent.parent.document!=null &&
parent.parent.document.getElementById('mainIframe')!=null &&
parent.parent.frames["mainIframe"]!=null &&
parent.parent.frames["mainIframe"].frames["proxyIframe"]!=null){
window.setInterval("updateHeight()", 200);
}
}
The issues are : the checking inside the second function won't be passed, so that the first function also won't be triggered.
Please note that these function worked in IE7, Chrome11.x, Chrome16.x, Safari5.x, but have issues in Firefox.
Question
What i want to do is make above functions work, but currently i have no idea what's the issues now. it's incorrect to use "parent.parent" ?
The problem I see is you can't use document.frames in FF.
Also note that parent and parent.parent and parent.parent.parent.parent.parent.parent always exist, even if there are no frames, so I think a lot of those conditions are unnecessary.
Try:
function updateHeight() {
var mainIframe = parent.parent.document.getElementById('mainIframe'),
mainDoc = mainIframe.contentDocument || mainIframe.contentWindow.document,
proxyIframe = mainDoc.getElementById('proxyIframe'),
proxyDoc = proxyIframe.contentDocument || proxyIframe.contentWindow.document;
newHeigh = proxyDoc.location.hash.substr(1);
mainIframe.style.height = newHeigh;
}
function canUpdateHeight() {
if(parent.parent.document.getElementById('mainIframe')
&& parent.document.getElementById('proxyIframe')) {
window.setInterval(updateHeight, 200);
}
}
I have a gwt module I'm trying to load into my host page. It's a pretty standard routine:
public class TimeFvHelper implements EntryPoint {
/**
* This is the entry point method.
*/
public void onModuleLoad() {
ChargeToTreePresenter presenter = new ChargeToTreePresenterImpl(new ChargeToTree());
RootPanel.get("divPicker").add((Widget)presenter.getView());
}
Usually it works fine with out problem. However when i open the webpage using IE9, first time I have no problem. However if I reload the page or back out then come back in i get the following error:
Unable to get value of the property 'location': object is null or undefined
This is in the nocache.js file generated during compilation, specifically in the maybeInjectFrame() method:
var frameInjected;
function maybeInjectFrame(){
if (!frameInjected) {
frameInjected = true;
var iframe = $doc.createElement($intern_36);
iframe.src = $intern_37;
iframe.id = $intern_1;
iframe.style.cssText = $intern_38;
iframe.tabIndex = -1;
$doc.body.appendChild(iframe);
$stats && $stats({moduleName:$intern_1, sessionId:$sessionId, subSystem:$intern_2, evtGroup:$intern_8, millis:(new Date).getTime(), type:$intern_39});
iframe.contentWindow.location.replace(base + initialHtml); // <-- Here
}
}
I've seen alot of posts in the last two days saying that IE9 has issues with iframes. Is that what's going on here? Any ideas of a workaround?
-Ian
Try using the xsiframe linker instead of the default std one.
It still uses an iframe for sandboxing (so that scripts loaded in the page have no impact on GWT's code) but do not change the iframe's location (it instead writes into the iframe); and it has many more features: http://code.google.com/p/google-web-toolkit/issues/list?can=1&q=xsiframe+status%3AFixed
I see the same problem, and I don't have a solution.
But here is the code including the values of the intern'ed strings.
function maybeInjectFrame(){
if (!frameInjected) {
frameInjected = true;
var iframe = $doc.createElement('iframe');
iframe.src = "javascript:''";
iframe.id = 'application';
iframe.style.cssText = 'position:absolute;width:0;height:0;border:none';
iframe.tabIndex = -1;
$doc.body.appendChild(iframe);
$stats && $stats({moduleName:'application', sessionId:$sessionId, subSystem:'startup', evtGroup:'moduleStartup', millis:(new Date).getTime(), type:'moduleRequested'});
iframe.contentWindow.location.replace(base + initialHtml);
}
}
I have a webpage with iframes.
These iframes are for showing some external website data.
But problem arise when those external servers get blocked in a network it gives a error that "The proxy server is refusing connections".
It does not look good to me.
I want to hide all these blocked iframes or want to show some alternate data there.
It's not possible to check whether a page has not loaded. However, it's possible to use onload event handlers.
It's important to not rely on JQuery, because JQuery is also an external source which has to be loaded. Add this code within <script> tags after the last IFRAME element (often, at the end of the body). Code:
//Cannot rely on JQuery, as it has to be loaded
(function(){//Anonymous wrapper.
var iframes = document.getElementsByTagName("iframe");
var num_frames = iframes.length;
//Function to add Event handlers
var addLoad = window.addEventListener ? function(elem, func){
elem.addEventListener("load", func, true);
} : window.attachEvent ? function(elem, func){
elem.attachEvent("onload", func);
} : function(elem, func){
elem.onload = func;
};
var success_load = 0;
for(var i=0; i<num_frames; i++){
addLoad(iframes[i], function(){
this.dataSuccessfullyLoaded = true;
success_load++;
});
}
addLoad(window, function(){
if(success_load < num_frames){
for(var i=num_frames-1; i>=0; i--){
if(!iframes[i].dataSuccessfullyLoaded){
iframes[i].parentNode.removeChild(iframes[i]);
//Or: iframes[i].style.display = "none";
}
}
}
});
})();
Fiddle: http://jsfiddle.net/3vnrg/
EDIT
Your proxy seems to send HTTP pages with status code 200. Another option is to include the CSS file, and check whether a CSS variable exists or not:
/*From http://static.ak.fbcdn.net/rsrc.php/v1/yb/r/CeiiYqUQjle.css*/
#facebook .hidden_elem{display:none !important}
#facebook .invisible_elem{visibility:hidden}
HTML:
<link rel="Stylesheet" href="http://static.ak.fbcdn.net/rsrc.php/v1/yb/r/CeiiYqUQjle.css" />
<div id="facebook"><div class="hidden_elem invisible_elem"></div></div>
JavaScript (execute this code after all resources have been loaded):
if($("#facebook div").css("display") != "none" || $("#facebook div").css("visibility") != "hidden") disableFBFrame();
// Where disableFBFrame(); is a function which hides the frame.