I have a page with an iframe in it. Within the iframe, the default page runs some javascript to open a child window, login to an external service, then display some content from that external service back in the iframe.
My code works fine in Chrome and IE but in Safari and Firefox it does not. In those browsers the parent window seems to ignore that fact that the user is now logged in to the external service and displays their "Not logged in" message instead of the info that is supposed to display.
Here's the code for what I'm trying to do.
HTML (main page):
<iframe id="brief" name="brief" src="contents/somepage.php?var=WXYZ" width="962" height="600">
<p>Your browser does not support iframes.</p>
</iframe>
somepage.php
<html>
<head>
<script type="text/javascript">
window.onload = function()
{
var code = 'WXYZ';
var login = http://www.externalsite.com/brief/html.asp?/cgi-bin/service?msg=0048&usr=username&pwd=pass';
//OPEN CHILD WINDOW AND LOGIN USING VARIABLES ABOVE, THEN CLOSE WINDOW
childWindow=window.open(login,'','width=30,height=30,location=no');
var cmd = 'childWindow.close()';
setTimeout(cmd,2000);
//REDIRECT THIS IFRAME TO BRIEFING INFORMATION
var uri = 'http://www.externalsite.com/brief/html.asp?/cgi-bin/service?msg=0092&AID=NOSEND&MET=1&NTM=1&LOC='+code;
self.location.href=uri;
}
</script>
</head>
<body>
</body>
</html>
I have tried adjusting various setTimeout functions to try to delay certain aspects of the script to wait for something else to happen but it doesn't seem to help.
Is there some cross-browser problem with this code that I am missing?
Thanks
setTimeout(cmd,2000); is not going to block script execution for two seconds; rather, it sets up an event that will fire in approximately 2 seconds. Immediately after your call to setTimeout, the remaining parts of the script will execute:
// This happens right away
uri = 'http://www.externalsite.com/brief/html.asp?/cgi-bin/service?msg=0092&AID=NOSEND&MET=1&NTM=1&LOC='+code;
self.location.href = uri;
The fact that it works in any browser is just lucky timing. If you want the iframe to refresh after the popup closes, add that code to your callback (you don't need to and shouldn't use a string for your timer handler, by the way):
setTimeout(function() {
childWindow.close();
var uri = 'http://www.externalsite.com/brief/html.asp?/cgi-bin/service?msg=0092&AID=NOSEND&MET=1&NTM=1&LOC='+code;
self.location.href = uri;
}, 2000);
Even this solution won't work if the popup's content doesn't load within two seconds. The best solution is to have the popup close itself after loading. Then, you could detect when it closes and know that you're ready to reload your iframe. But if this is a third-party site that you don't have control over, you're probably stuck using a less-than-ideal solution like this.
Related
I want to save some JavaScript code as a bookmark in chrome so it automatically opens my university login site and clicks on on the login button. I am completely inexperienced in JavaScript, so I have no clue how to do this. I snipped together the following code, which opens the correct website, but then does not click on anything. The first URL automatically puts me to the login site (third URL in the code) in case I have not logged in yet in this window.
(function() {
window.location.replace("https://lms.uzh.ch/auth/MyCoursesSite/0");
window.onload = function(){
if (current_url.startswith('https://lms.uzh.ch/auth/MyCoursesSite/0')) {
return;
}
if (current_url.startsWith('https://lms.uzh.ch/dmz/')) {
document.getElementById("wayf_submit_button").click();
}
};
})();
I'm sorry if this is too obvious a question and annoys any experts but as I said I am a complete beginner. I would of course add the "javascript:" at the beginning for chrome to understand the bookmark.
When you use window.location.replace, you change the address and you code can't work anymore.
I can suggest using some browser extension, your "click function" should work then.
I guess you could also try to make some simple html page with iframe, you call your "click function" at this page, but you target it to the iframe. After that you can change browser's location to the university's webpage as you should already be logged in.
<html>
<head>
</head>
<body>
<iframe src="your_university_address.com" id="some_id" onload="click_button()"></iframe>
<script>
function click_button()
{
my_iframe=document.getElementById('some_id');
my_iframe.contentDocument.getElementById('your_button_id').click();
}
</script>
</body>
</html>
Very simple but it should do that job.
(You can achieve similar result by using window.open() I guess)
Say I have a simple script
var i = 0;
test();
function test() {
console.log(i++);
setTimeout(test, 1000);
}
I put it in a Google Chrome console. How do I make it continue to run after the page navigates to another (should continue to print out numbers when browsing the web)?
Maybe save the variable 'i' in onbeforeunload() function, and launch a new script with that saved variable?
How do I make it continue to run after the page navigates to another
you can't, the script cannot continue on another page, it's the browser that runs the javascript in the page, and that will stop it when moving to another page.
(or) should continue to print out numbers when browsing the web?
you have yourself answered this. You can certainly save the counter in localstorage and resume counting on the next page, provided this next page contains the same or similar script and the logic to restore the counter from localStorage.
Or, you can move part of this logic to a server-side script.
I suppose this script is an example and displaying numbers is not really what you want to do.
If you are looking for something to run script even when you have left the browser, I suggest you take a look at Service workers.
If you want more resources, you can check Jake Archibald's blog. He is a chrome developer and he is always talking about service workers. An introduction here.
I didn't see any good suggestions posted already for what I was trying to do but I came up with something that worked for me. I wanted to add a navigation element on the page and not have it go away after navigating. This was on a website that was not managed by me. I removed the innerHtml of the body of the page, added an iframe and pointed it at the page I was on, set it to 100% width and height and removed the border. Then I could navigate within the iframe, but still have my script function run in a set timeout to add the navigation element back to the page after it navigated. Something like this:
document.body.innerHTML = ''
iframe = document.createElement('iframe');
iframe.setAttribute('id', 'iframe');
document.body.appendChild(iframe);
iframe.setAttribute('src', window.location.href);
iframe.style.height = "100%";
iframe.style.width = "100%";
iframe.style.border = "0";
function addContent(){
setTimeout(()=>{
elementToAddTo = iframe.contentWindow.document.getElementById('my-element-id')];
contentToAdd = document.createElement('div');
contentToAdd.innerHTML = `<p>My new content</p>`
elementToAddTo.insertBefore(contentToAdd, elementToAddTo.childNodes[0]);
}, 1000);
}
addContent()
Then in that new content somewhere I had an onchange event which would navigate and call the addContent function by saying window.top.addContent();
onchange="window.location.href = window.location.href.replace(/(param1=.*)/, 'param1='+myNewParamValue); window.top.addContent();">
I Understand this approach makes a lot of assumptions about what you're trying to do and maybe it is only working for me because I'm only changing a param value, but I want to leave this hear in case it helps somebody trying to figure out how to do something similar.
I have a page that has an iframe with external content. I don't want infinite loops in the external content to crash my whole page. Is there any way to get around this.
I tried to set something up where the parent postMessages the child iframe every so often and if the child Iframe doesn't respond for too long a time the parent changes the iframes src, but this doesn't seem to work. The parent's setTimeout functions no longer execute once the iframe starts looping. See my code here (note that it will crash your tab if you execute it, open the console before execution to view the logging):
<html>
<head>
</head>
<body>
<script type="text/javascript">
var scr = 'script';
var html = '<html><head><script>\n' +
' window.addEventListener("message", answer, false);' +
' function answer() { console.log("answered"); parent.postMessage(\'hi\', \'*\');}' +
' setTimeout("while(1){console.log(\'in loop\')};", 3000)' +
"</" + scr + "></head><body>IFRAME</body</html>";
var lastAnswer = (new Date()).getTime();
var iframe = document.createElement('iframe');
queryChild();
window.addEventListener("message", receive, false);
function receive() {
lastAnswer = (new Date()).getTime();
console.log('got answer');
}
function queryChild() {
console.log('querying');
if((new Date()).getTime() - lastAnswer > 5000) {
console.log('killing');
iframe.src = '';
} else if(iframe.contentWindow){
iframe.contentWindow.postMessage('hi', '*');
}
setTimeout(queryChild, 2000);
};
document.body.appendChild(iframe);
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(html);
iframe.contentWindow.document.close();
</script>
</body>
</html>
Any suggestions on how to solve this problem?
My experience with this kind of problem is that unless you can access the external code before feeding it to the iframe (either as an URL or via the srcdoc attribute), the loop will completely interrupt any JavaScript execution.
Whatever kind of timeout functionality you implement, it will not be called due to the iframe code execution consuming 100% resources until the browser reports a crash.
Your options are:
Sanitize the code automatically before adding it to the iframe, which proves impractical since there are infinite ways to loop infinitely, and you will not be able to catch them all. You would have to write a scanner script that could detect infinite loops while not crashing in the course of scanning the code.
Use a sandboxing solution like Google Caja to sanitize the code. However, this will change the code structurally if not configured heavily.
In case of an application that has capabilites of creating virtual environments and monitoring them, you could execute the iframe code (let's say on a virtual machine of sorts), check if the process locks up and use that outcome to determine if you can safely set the iframe.src property to your code's URL. This might be the only solution that can guarantee some sort of guarantee that this code will not lock up immediately (however, there are many ways to have race conditions at some later point of execution, so there will not be a sure way to say it will never lock up the browser).
Summary: Unless you can find a way to test the code extensively before showing it in the iframe, you can not guarantee that the iframe code will not lock up the browser tab.
It depends highly on the browser, some browser uses one thread for each page (or iframe), in this case your script cannot be executed until the iframe execution is over (the infinite loop). Some others have one thread per page (or iframe) and maybe you are able to do it.
What I'm sure is than if you expect to support enterprise browsers (like IE8) you can't.
This
console.log('killing');
iframe.src = '';
Will not kill the iframe. according to same origin policy you can not manipulate external domains from your domain. Just changing the src of an iframe doesn't trigger a navigation in the iframe. src will change, but iframe will not get navigated.
If your get the message from inner iframe you should remove the iframe from your document tree and insert a new one in document tree in order to kill the iframe using removeChild
document.body.removeChild(iframe);
document.body.appendChild(newiframe);
Look at this simple demonstration I've created here: http://jsbin.com/avodeb/1/
Try this:
<script>
document.getElementById('iframeID').onload= function() { //When the iframe loads quickly
clearTimeout(killerTimer); // Stop the Killer Timer
};
var killerTimer = setTimeout( function() {
document.getElementById("iframeID").setAttribute("src",""); //Otherwise, kill the iframe
}, 3000 );
</script>
I've looked around here and saw wonderful solutions how to print the content of a div using javascript by instantiating a new window and porting markup there.
My problem with that solution in SharePoint is that SP.*.js libraries load asynchronously and it freezes the print dialog screen or the browser itself.
Anybody was able to workaround this issue?
With sharepoint you can use ExecuteOrDelayUntilScriptLoaded to wait until SP.js loaded to make sure nothing freezes.
<script type="text/javascript">
function printPage(){
}
window.onload = function(){ ExecuteOrDelayUntilScriptLoaded(printPage, "sp.js"); };
</script>
I don't know SharePoint specifically, so I can't provide a specific example. However, it sounds like you are thinking of a technique like the one outlined on this other stackoverflow question.
Notice this is all one block of code that will execute sequentially. So we open a window, set the markup and print right away. No waiting around between those steps.
I would suggest that you don't call print at that point, but rather open the window, set the markup AND inject some more javascript in the new window markup. This new injected javascript will be where the actual printing takes places.
That injected script should have some logic to wait until certain script resources have finished loading after which it calls window.print(). Apparently window.onload doesn't fire until after all resources have finished loading. (onload doesn't fire until all content is either loaded or has failed to load). Here's a jquery example on stackoverflow. Also scripts are executed in the order the appear in the markup. So if your injected print window javascript is last, every other script above it must already be done. (So anything SharePoint injects in the markup should already have been executed).
For bonus points, if everything is going well, you could inject a 'loading mask' over the new print window and then hide the mask just before your javascript does its window.print().
The code given below will work with sharepoint, ASPX pages and with any browser. I tested the code with Sharepoint 2010 Visual webparts, and it works like a charm.
Place this Javascript code inside your webpart ASCX file
<script type="text/javascript">
function printPartOfPage(elementId) {
var printContent = document.getElementById(elementId);
var windowUrl = '';
var uniqueName = new Date();
var windowName = 'Print' ;
var printWindow = window.open(windowUrl, windowName, 'left=-20,top=-20,width=0,height=0');
printWindow.document.write('<HTML><Head><Title></Title>');
printWindow.document.write('</Head><Body style="margin-left:50px;margin-top:50px;font-size:10pt;">');
printWindow.document.write(printContent.innerHTML);
printWindow.document.write('</Body></HTML>');
printWindow.document.close();
printWindow.focus();
printWindow.print();
printWindow.close();
}
</script>
& Update the print button ClientClick Event with
<img alt="" src="~/_layouts/images/WebpartCollection/Printer-30X30.jpg"
onclick="JavaScript:printPartOfPage('YourDivClientID');" />
I hope this will help you out.
I have numerous iframes that load specific content on my pages. Both the parent and iframe are on the same domain.
I have a scrollbar inside the iframe that doesn't seem to load correctly in all browsers. But when I refresh the iframe it loads perfect. I have no idea why it does this.
I have used the meta refresh, which works but I don't want the page to refresh constantly, just once.
The solution I'm looking for will reload the iFrame content after the iFrame is opened, with a minimal delay.
Edit
I realized that my page loads all of my iframes when the index is loaded. The iframes appear in a jQuery overlay, which is also loaded but visibility:hidden until called. So on this call is when I would want the iframe to be reloaded.
Could anyone help me come up with a Javascript function that reloads the iFrame when I click the link to the iFrame? I've gotten close but I know nothing about js and I keep falling short. I have a function that reloads the page, but I can't figure out how to get it called just once.
I have this so far:
<script type="text/javascript">
var pl;
var change;
pl=1;
function ifr() {
if (pl=1) {
document.location.reload([true]);
alert("Page Reloaded!");
change=1;
return change;
}
change+pl;
}
So basically it uses the document.location.reload which works to reload the page. I'm trying to then make pl change to something other than 1 so the function doesnt run again. I've been calling this JS from the body with onLoad.
All the leads on this went dead, but I did find a code snippet that worked. Not written by me, and I don't remember where it came from. Just posting to help someone should they ever have the same question.
<div class="overlay-content"> //Content container needed for CSS Styling
<div id="Reloader">
//iFrame will be reloaded into this div
</div>
//Script to reload the iframe when the page loads
<script>
function aboutReload() {
$("#Reloader").html('<iframe id="Reloader" height="355px" width="830px" scrolling="no" frameborder="0" src="about.html"></iframe>');
}
</script>
</div>
Basically just loads the iFrame source when the window with the iFrame opens, as opposed to the iFrame loading when the original page loads.
Beyond the scope of the original question, however this jQuery snippit works with cross domain iframe elements where the contentDocument.location.reload(true) method won't due to sandboxing.
//assumes 'this' is the iframe you want to reload.
$(this).replaceWith($(this).clone()); //Force a reload
Basically it replaces the whole iframe element with a copy of itself. We're using it to force resize embedded 3rd party "dumb" widgets like video players that don't notice when their size changes.
On the iframe element itself, set an onload:
iframe.onload = function() {this.contentWindow.location.reload(); this.onload = null;};
(Only works if the iframe's location is in the same domain as the main page)
Here's a complete solution to the original question:
<iframe onload="reloadOnce(this)" src="test2.html"></iframe>
<script>
var iframeLoadCount = 0;
function reloadOnce(iframe) {
iframeLoadCount ++;
if (iframeLoadCount <= 1) {
iframe.contentWindow.location.reload();
console.log("reload()");
}
}
</script>
The updated question is not really clear (what's "the link to the iFrame" and where is it in your snippet?), but you have a few issues with the code:
"calling this JS from the body with onLoad", assuming you mean an iframe's body, means the variable you're hoping to use to avoid infinite reloading will get clobbered along with the rest of the iframe's page when it's reloaded. You need to either load a slightly different URL in the iframe (and check the URL on iframe's onload before reloading) or put the flag variable in the outer page (and access it with parent.variableName - that should work I think)
if (pl=1) { should use ==, as = is always an assignment.
change+pl; has no effect.