How do i check if the page has pending AJAX or HTTP GET/POST requests? I use javascript and/or python for this checking.
what i wanted to do is execute a script if a page has finished all requests. onload doesn't work for me, if you used firebugs net panel, you would know. onload fires when the page is loaded but there is a possibility that there are still pending request hanging around somewhere.
thank you in advance.
figured it out. thanks for the effort guys.
just plain and simple javascript.
interValRef = 0;
interValRef = setInterval("checkState();",100)
function checkState(){
if(document.readyState == 'complete'){
clearInterval(interValRef);
myFunc();
}
}
Here is the best way of checking it.
var loadingDone = document.readyState=="complete" && jQuery.active === 0;
So loadingDone will be true, if Ajax calls are done. If its false then you can add waiting.
I see you mention you are using Prototype.js. You can track active requests with Prototype by checking the Ajax.activeRequestCount value. You could check this using setTimeout or setInterval to make sure that any requests triggered on page load have completed (if that's what you're looking to do)
Assuming you are using prototype.js you could keep track with a counter of all your request objects
var ACTIVE_REQUESTS = 0; // GLOBAL
ACTIVE_REQUESTS++
new Ajax.Request('/your/url', {
onSuccess: function(response) {
ACTIVE_REQUESTS--;
// Handle the response content...
}
}));
console.log("there are " + ACTIVE_REQUESTS + " open AJAX requests pending");
You would need to keep track of each XMLHttpRequest and monitor whether it completes or the asynchronous callback is executed.
I think you want to know if the HTML is fully loaded. In this case you can use the dom:loaded event. Here is an example on how to use it with prototype (but there must be a variant for other JS frameworks):
document.observe("dom:loaded", function() {
// do whatever you want to do
});
This event will fire as soon as the DOM tree is loaded. So even before all the images or external data (including iframe) are loaded.
I can confirm that document.readyState can still be 'complete' even if there are pending requests in network tab.
The code I developed for now to check if the page has been loaded completely or not is:
var prevSize= document.body.innerHTML.length;
var id= setInterval(()=>{
let currSize=document.body.innerHTML.length;
if(prevSize===currSize){
clearInterval(id);
console.log("page loaded successfully");
}
else{
console.log("loading...");
prevSize=currSize;
}
}, 3000)
This checks document.body.innerHTML length in every 3s.
Not a best solution, but atleast it works.
document.addEventListener("readystatechange", function(event) {
if (document.readyState === "complete") {
console.log("complete");
}
});
Related
I need to implement a cross-site comet http server push mechanism using script tag long polling. (phew...) For this, I dynamically insert script tags into the DOM and the server sends back short js scripts that simply call a local callback function that processes the incoming messages. I am trying to figure out a way to associate each one of these callback calls with the script tag that sent it, to match incoming replies with their corresponding requests.
Clearly, I could simply include a request ID in the GET url, which is then returned back in the js script that the server generates, but this creates a bunch of unnecessary traffic and doesn't strike me as particularly elegant or clever.
What I would like to do is to somehow associate the request ID with the script tag that I generate and then read out this request ID from within the callback function that is called from inside this script tag. That way, all the request management would remain on the client.
This leads me to the following question: Is there a way to ask the browser for the DOM element of the currently executing script tag, so I can use the tag element to pass arguments to the contained javascript?
I found this thread:
Getting the currently executing, dynamically appended, script tag
Which is asking exactly this question, but the accepted answer isn't useful to me since it still requires bloat in the server-returned js script (setting marker-variables inside the script) and it relies on unique filenames for the scripts, which I don't have.
Also, this thread is related:
How may I reference the script tag that loaded the currently-executing script?
And, among other things, suggests to simply grab the last script in the DOM, as they are executed in order. But this seems to only work while the page is loading and not in a scenario where scripts are added dynamically and may complete loading in an order that is independent of their insertion.
Any thoughts?
PS: I am looking for a client-only solution, i.e. no request IDs or unique callback function names or other non-payload data that needs to get sent to and handled by the server. I would like for the server to (theoretically) be able to return two 100% identical scripts and the client still being able to associate them correctly.
I know you would like to avoid discussions about changing the approach, but that's really what you need to do.
First, each of the script tags being added to the DOM to fire off the poll request is disposable, i.e. each needs to be removed from the DOM as soon as its purpose has been served. Else you end up flooding your client DOM with hundreds or more dead script tags.
A good comparable example of how this works is jsonp implementations. You create a client-side named function, create your script tag to make the remote request, and pass the function name in the request. The response script wraps the json object in a function call with the name, which then executes the function on return and passes the json payload into your function. After execution, the client-side function is then deleted. jQuery does this by creating randomly generated names (they exist in the global context, which is really the only way this process works), and then deletes the callback function when its done.
In regards to long polling, its a very similar process. Inherently, there is no need for the response function call to know, nor care, about what script tag initiated it.
Lets look at an example script:
window.callback = function(obj){
console.log(obj);
}
setInterval(function(){
var remote = document.createElement('script');
remote.src = 'http://jsonip.com/callback';
remote.addEventListener('load', function(){
remote.parentNode.removeChild(remote);
},false);
document.querySelector('head').appendChild(remote);
}, 2000);
This script keeps no references to the script elements because again, they are disposable. As soon as their jobs are done, they are summarily shot.
The example can be slightly modified to not use a setInterval, in which case you would replace setInterval with a named function and add logic into the remote load event to trigger the function when the load event completes. That way, the timing between script tag events depends on the response time of your server and is much closer to the actual long polling process.
You can extend this even further by using a queueing system to manage your callbacks. This could be useful if you have different functions to respond to different kinds of data coming back.
Alternatively, and probably better, is to have login in your callback function that handles the data returned from each poll and executes whatever other specific client-side logic at that point. This also means you only need 1 callback function and can get away from creating randomly generated callback names.
If you need more assistance with this, leave a comment with any specific questions and I can go into more detail.
It's most definitely possible but you need a little trick. It's a common technique known as JSONP.
In JavaScript:
var get_a_unique_name = (function () {
var counter = 0;
return function () {
counter += 1;
return "function_" + counter;
}
}()); // no magic, just a closure
var script = document.createElement("script");
var callback_name = get_a_unique_name();
script.src = "/request.php?id=12345&callback_name=" + callback_name;
// register the callback function globally
window[callback_name] = function (the_data) {
console.log(the_data);
handle_data(the_data); // implement this function
};
// add the script
document.head.appendChild(script);
The serverside you can have:
$callback_name = $_GET["callback_name"];
$the_data = handle_request($_GET["id"]); // implement handle_request
echo $callback_name . "(" . json_encode($the_data) . ");";
exit; // done
The script that is returened by /request.php?id=12345&callback_name=XXX will look something like this:
function_0({ "hello": "world", "foo" : "bar" });
There may be a solution using onload/onreadystate events on the script. I can pass these events a closure function that carries my request ID. Then, the callback function doesn't handle the server reply immediately but instead stores it in a global variable. The onload/onreadystate handler then picks up the last stored reply and tags it with the request ID it knows and then processes the reply.
For this to work, I need to be able to rely on the order of events. If onload is always executed right after the corresponding script tag finishes execution, this will work beautifully. But, if I have two tags loading simultaneously and they return at the same time and there is a chance that the browser will execute both and afterwards execute botth onload/onreadystate events, then I will loose one reply this way.
Does anyone have any insight on this?
.
Here's some code to demonstrate this:
function loadScript(url, requestID) {
var script = document.createElement('script');
script.setAttribute("src", url);
script.setAttribute("type", "text/javascript");
script.setAttribute("language", "javascript");
script.onerror = script.onload = function() {
script.onerror = script.onload = script.onreadystatechange = function () {}
document.body.removeChild(script);
completeRequest(requestID);
}
script.onreadystatechange = function () {
if (script.readyState == 'loaded' || script.readyState == 'complete') {
script.onerror = script.onload = script.onreadystatechange = function () {}
document.body.removeChild(script);
completeRequest(requestID);
}
}
document.body.appendChild(script);
}
var lastReply;
function myCallback(reply) {
lastReply = reply;
}
function completeRequest(requestID) {
processReply(requestID, lastReply);
}
function processReply(requestID, reply) {
// Do something
}
Now, the server simply returns scripts of the form
myCallback(message);
and doesn't need to worry at all about request IDs and such and can always use the same callback function.
The question is: If I have two scripts returning "simultaneously" is it possible that this leads to the following calling order:
myCallback(message1);
myCallback(message2);
completeRequest(requestID1);
completeRequest(requestID2);
If so, I would loose the actual reply to request 1 and wrongly associate the reply to request 2 with request 1.
It should be quite simple. There is only one script element for each server "connection", and it can easily be stored in a scoped, static variable.
function connect(nameOfCallback, eventCallback) {
var script;
window[nameOfCallback] = function() { // this is what the response invokes
reload();
eventCallback.call(null, arguments);
};
reload();
function reload() {
if (script && script.parentNode)
script.parentNode.removeChild(script);
script = document.createElement(script);
script.src = "…";
script.type = "text/javascript";
document.head.appendChild(script);
// you might use additional error handling, e.g. something like
// script.onerror = reload;
// but I guess you get the concept
}
}
I'm working on an application that makes a lot of usage of AJAX capabilities. I'm currently working on a page where there are a large number of ajax calls made to render an initial page. All these calls are routed through the same AJAX script, so they have to be made one at a time by the browser. (Yeah, it's a bit inefficient)
I've got an event that loads another page, but unfortunately it needs to make an ajax call before it can re-render the page. The result of this is that if a user clicks on that link right when the page is first loading, the browser waits for all other ajax calls before making the call to re-render the page according to the event raised by the user's click. This has been resulting in some noticeably slow load times for the page when clicking on the link to it right as the page is loading.
My question is: Is there a way to use Javascript or something to have the web browser cancel http calls to a web script so that another event can then make a call to that same script? I know that it would be nice to make each call to different scripts so they could happen concurrently, but unfortunately the application I'm working on funnels all ajax requests through a central script, and that's not going to change anytime soon.
Use the abort() method on the XMLHttpRequest object to cancel any ongoing request and return the object to a reusable state.
You could wrap it in a helper object, eg:
function HttpQueue(uri) {
var queue= [];
var xhr= new XMLHttpRequest();
xhr.onreadystatechange= function() {
if (xhr.readyState===4 && queue.length>=1) {
queue.unshift()[1](xhr);
if (queue.length>=1)
next();
}
};
this.send= function (data, callback, isurgent) {
if (isurgent && queue.length>=1) {
queue.length= 0;
xhr.abort();
}
queue.push([data, callback]);
if (queue.length==1)
next();
};
function next() {
xhr.open('POST', uri, true);
xhr.send(queue[0][0]);
}
}
var queue= new HttpQueue('/script');
queue.send('action=foo', function() {
alert('task 1 done');
}, false);
queue.send('action=bar', function() {
alert('task 2 done');
}, false);
queue.send('action=bof', function() {
alert('task 3 is urgent! tasks 1 and 2 can get knotted');
}, true);
(not tested, may work)
You can use the jQuery $.ajax method.
at which point you would specify a timeout for each call, and then set the aSync settings to false. That way you would only run one ajax request at a time.
Documentation can be found here:
http://api.jquery.com/jQuery.ajax/
I have a web page whose large parts are constructed on the front-end, in JavaScript. When the basic HTML is parsed and the DOM tree is constructed, the "ready" event triggers. At this event, a JavaScript code launches that continues with the construction of the web page.
$().ready(function() {
$(document.body).html('<img src = "img.png" />');
$.ajax({
url: 'text.txt'
});
});
As you can see in this tiny example,
new elements are added to DOM
more stuff, like images is being loaded
ajax requests are being sent
Only when all of this finishes, when the browser's loading progress bar disappears for the first time, I consider my document to be actually ready. Is there a reasonable way to handle this event?
Note that this:
$().ready(function() {
$(document.body).html('<img src = "img.png" />');
$.ajax({
url: 'text.txt'
});
alert('Document constructed!');
});
does not suffice, as HTTP requests are asynchronous. Maybe attaching handlers to every single request and then checking if all have finished is one way to do what I want, but I'm looking for something more elegant.
Appendix:
A more formal definition of the event I'm looking for:
The event when both the document is ready and the page's dynamic content finishes loading / executing for the first time, unless the user triggers another events with mouse moving, clicking etc.
As described here, you can sign up for load events for individual images when they finally load. However, as also described there, that may not be reliable across browsers or if the image is already in the browser's cache. So I think at least part of that is not 100% trustworthy. With that said, you can easily hook to a bunch of different asynchronous events and then only react when all of them have completed. For example:
$(document.body).html('<img src="img.png" id="porcupine"/>');
var ajaxPromise1 = $.get(...);
var ajaxPromise2 = $.get(...);
var imgLoadDeferred1 = $.Deferred();
$("#porcupine").load(
function() {
imgLoadDeferred1.resolve();
}
);
$.when(ajaxPromise1, ajaxPromise2, imgLoadDeferred1).done(
function () {
console.log("Everything's done.");
}
);
If you want to allow for the fact that the image load may never generate an event, you could also set a timer which would go off eventually and try to resolve the imgLoadDeferred1 if it is not already resolved. That way you don't get stuck waiting for an event that never happens.
You are looking for .ajaxStop().
Used like so:
$("#loading").ajaxStop(function(){
$(this).hide();
});
It does't really matter what DOM element you attach to unless you want to use this as is done in the above example.
Source: http://api.jquery.com/ajaxStop/
Add global counter to ajax calls in way that when ajax request is ready it calls addToReadyCounter(); function that will +1 to glogal var readyCount;.
Within same function check your readyCount and trigger event handler
var readyCount = 0, ajaxRequestCount = 5;
function addToReadyCount() {
++readyCount;
if (readyCount >= ajaxRequestCount) {
documentReadyEvent();
}
}
$.ajax(
...
success: function(data){
addToReadyCount();
}
...);
If you are only using success: ... for addToReaadyCount documentReadyEvent() only triggers if all call succeed, use other .ajax() event handlers to increment counter in case of error.
I'm trying to find a way to get if the browser is currently busy from JavaScript. I'm looking at making a Firefox extension to inject a Boolean value or something if the current page is loading something (either through ajax or just normal page loads), or the same with a Greasemonkey script, or through some JavaScript API (this would be the best solution, but from what I can see, nothing of the sort exists).
I was wondering what the best way to do this would be. I've been looking for Firefox Addon / Greasemonkey tutorials for making something like this and can't find anything. Does anyone have any tips or resources they could point me towards or better solutions for solving this?
Thanks
Edit: and by busy, I mostly just need to know if the browser is sending or receiving data from a server.
jQuery, a great javascript framework for DOM manipulation and performing ajax calls, provides two great hooks for determining when ajax calls are in progress:
$.ajaxStart() and $.ajaxStop()
Both of these hooks take a handler function that will be called when an ajax call is about to start, and when all ajax calls have ceased, respectively. These functions can be bound to any element on the page. You could set a global boolean value in your $.ajaxStart() handler to true and set it back to false in your $.ajaxStop() handler.
You could then check that boolean flag and determine whether ajax calls are in progress.
Something along these lines:
$(document).ajaxStart(function() {
window.ajaxBusy = true;
});
$(document).ajaxStop(function() {
window.ajaxBusy = false;
});
As far as determining when the browser is loading the current page, you could check
document.readyState. It returns a string of "loading" while the document is loading and a string of "complete" once it has loaded. You can bind a handler to document.onreadystatechange and set a global boolean that will indicate whether the document is still loading or not.
Something like this:
document.onreadystatechange = function() {
switch (document.readyState) {
case "loading":
window.documentLoading = true;
break;
case "complete":
window.documentLoading = false;
break;
default:
window.documentLoading = false;
}
}
EDIT:
It appears that $.ajaxStart() and $.ajaxStop() do NOT work for ajax calls invoked without jQuery. All XMLhttprequest objects have an event called readystatechange that you can attach a handler to. You could utilize this functionality to determine whether or not that individual call is done. You could push all references to outstanding calls onto an array, and in a setInterval() check that array's length. If it > 1, there are out standing ajax calls. It's a rough approach, and only one way of getting about it. There are probably other ways to do this. But here's the general approach:
// declare array to hold references to outstanding requets
window.orequets = [];
var req = XMLHttpRequest();
// open the request and send it here....
// then attach a handler to `onreadystatechange`
req.onreadystatechange = function() {
if (req.readyState != 4 || req.readyState != 3) {
// req is still in progress
orequests.push(req);
window.reqPos = orequests.length -1
} else {
window.orequests = orequests.slice(reqPos, reqPos + 1);
}
}
Do the above for each XMLHttpRequest() you will be sending, of course changing the request name for each one. Then run a setInterval() that runs every x amount of milliseconds, and checks the length property of orequests. If it is equal to zero, no requests are happening, if it is greater than zero, requests are still happening. If no requests are happening, you can either clear the interval through clearInterval() or keep it running.
Your setInterval might look something like this:
var ajaxInterval = setInterval(function() {
if (orequests.length > 0) {
// ajax calls are in progress
window.xttpBusy = true;
} else {
// ajax calls have ceased
window.xttpBusy = false;
// you could call clearInterval(ajaxInterval) here but I don't know if that's your intention
},
3000 // run every 3 seconds. (You can decide how often you want to run it)
});
Here's what I think I'll end up doing. This solution is like the one Alex suggested with the Jquery events, except that it works with anything that uses the XMLHttpRequest (Including Jquery):
var runningAjaxCount = 0;
var oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
oldOnReady = this.onreadystatechange;
this.onreadystatechange = function() {
oldOnReady.call(this);
if(this.readyState == XMLHttpRequest.DONE) {
ajaxStopped();
}
}
ajaxStarted();
oldSend.apply(this, arguments);
}
function ajaxStarted() {
runningAjaxCount++;
}
function ajaxStopped() {
runningAjaxCount--;
}
function isCallingAjax() {
return runningAjaxCount > 0;
}
function isBrowserBusy() {
return document.readyState != "complete" && isCallingAjax();
}
The browser technically isn't ever "busy". Business is a very subjective term. Let's assume that the main thread is performing a simple while loop which blocks execution. This could be considered busy, but what if you have something like this:
function busy() {setTimeout(busy, 0);do_something();}
busy();
The browser isn't being blocked (per se), so whether or not the page is "busy" is very unclear. Also, that doesn't even begin to touch on web workers and code in the chrome.
You're going to be hard-pressed to do this, and even if you do, it likely won't work how you expect it to. Good luck, nonetheless.
I can detect when the content of an iframe has loaded using the load event. Unfortunately, for my purposes, there are two problems with this:
If there is an error loading the page (404/500, etc), the load event is never fired.
If some images or other dependencies failed to load, the load event is fired as usual.
Is there some way I can reliably determine if either of the above errors occurred?
I'm writing a semi-web semi-desktop application based on Mozilla/XULRunner, so solutions that only work in Mozilla are welcome.
If you have control over the iframe page (and the pages are on the same domain name), a strategy could be as follows:
In the parent document, initialize a variable var iFrameLoaded = false;
When the iframe document is loaded, set this variable in the parent to true calling from the iframe document a parent's function (setIFrameLoaded(); for example).
check the iFrameLoaded flag using the timer object (set the timer to your preferred timeout limit) - if the flag is still false you can tell that the iframe was not regularly loaded.
I hope this helps.
This is a very late answer, but I will leave it to someone who needs it.
Task: load iframe cross-origin content, emit onLoaded on success and onError on load error.
This is the most cross browsers origin independent solution I could develop. But first of all I will briefly tell about other approaches I had and why they are bad.
1. iframe That was a little shock for me, that iframe only has onload event and it is called on load and on error, no way to know it is error or not.
2. performance.getEntriesByType('resource'). This method returns loaded resources. Sounds like what we need. But what a shame, firefox always adds Resource in resources array no matter it is loaded or failed. No way to know by Resource instance was it success. As usual. By the way, this method does not work in ios<11.
3. script I tried to load html using <script> tag. Emits onload and onerror correctly, sadly, only in Chrome.
And when I was ready to give up, my elder collegue told me about html4 tag <object>. It is like <iframe> tag except it has fallbacks when content is not loaded. That sounds like what we are need! Sadly it is not as easy as it sounds.
CODE SECTION
var obj = document.createElement('object');
// we need to specify a callback (i will mention why later)
obj.innerHTML = '<div style="height:5px"><div/>'; // fallback
obj.style.display = 'block'; // so height=5px will work
obj.style.visibility = 'hidden'; // to hide before loaded
obj.data = src;
After this we can set some attributes to <object> like we'd wanted to do with iframe. The only difference, we should use <params>, not attributes, but their names and values are identical.
for (var prop in params) {
if (params.hasOwnProperty(prop)) {
var param = document.createElement('param');
param.name = prop;
param.value = params[prop];
obj.appendChild(param);
}
}
Now, the hard part. Like many same-like elements, <object> doesn't have specs for callbacks, so each browser behaves differently.
Chrome. On error and on load emits load event.
Firefox. Emits load and error correctly.
Safari. Emits nothing....
Seems like no different from iframe, getEntriesByType, script....
But, we have native browser fallback! So, because we set fallback (innerHtml) directly, we can tell if <object> is loaded or not
function isReallyLoaded(obj) {
return obj.offsetHeight !== 5; // fallback height
}
/**
* Chrome calls always, Firefox on load
*/
obj.onload = function() {
isReallyLoaded(obj) ? onLoaded() : onError();
};
/**
* Firefox on error
*/
obj.onerror = function() {
onError();
};
But what to do with Safari? Good old setTimeout.
var interval = function() {
if (isLoaded) { // some flag
return;
}
if (hasResult(obj)) {
if (isReallyLoaded(obj)) {
onLoaded();
} else {
onError();
}
}
setTimeout(interval, 100);
};
function hasResult(obj) {
return obj.offsetHeight > 0;
}
Yeah.... not so fast. The thing is, <object> when fails has unmentioned in specs behaviour:
Trying to load (size=0)
Fails (size = any) really
Fallback (size = as in innnerHtml)
So, code needs a little enhancement
var interval = function() {
if (isLoaded) { // some flag
return;
}
if (hasResult(obj)) {
if (isReallyLoaded(obj)) {
interval.count++;
// needs less then 400ms to fallback
interval.count > 4 && onLoadedResult(obj, onLoaded);
} else {
onErrorResult(obj, onError);
}
}
setTimeout(interval, 100);
};
interval.count = 0;
setTimeout(interval, 100);
Well, and to start loading
document.body.appendChild(obj);
That is all. I tried to explain code in every detail, so it may look not so foolish.
P.S. WebDev sucks
I had this problem recently and had to resort to setting up a Javascript Polling action on the Parent Page (that contains the IFRAME tag). This JavaScript function checks the IFRAME's contents for explicit elements that should only exist in a GOOD response. This assumes of course that you don't have to deal with violating the "same origin policy."
Instead of checking for all possible errors which might be generated from the many different network resources.. I simply checked for the one constant positive Element(s) that I know should be in a good response.
After a pre-determined time and/or # of failed attempts to detect the expected Element(s), the JavaScript modifies the IFRAME's SRC attribute (to request from my Servlet) a User Friendly Error Page as opposed to displaying the typical HTTP ERROR message. The JavaScript could also just as easily modify the SRC attribute to make an entirely different request.
function checkForContents(){
var contents=document.getElementById('myiframe').contentWindow.document
if(contents){
alert('found contents of myiframe:' + contents);
if(contents.documentElement){
if(contents.documentElement.innerHTML){
alert("Found contents: " +contents.documentElement.innerHTML);
if(contents.documentElement.innerHTML.indexOf("FIND_ME") > -1){
openMediumWindow("woot.html", "mypopup");
}
}
}
}
}
I think that the pageshow event is fired for error pages. Or if you're doing this from chrome, then your check your progress listener's request to see if it's an HTTP channel in which case you can retrieve the status code.
As for page dependencies, I think you can only do this from chrome by adding a capturing onerror event listener, and even then it will only find errors in elements, not CSS backgrounds or other images.
Doesn't answer your question exactly, but my search for an answer brought me here, so I'm posting just in case anyone else had a similar query to me.
It doesn't quite use a load event, but it can detect whether a website is accessible and callable (if it is, then the iFrame, in theory, should load).
At first, I thought to do an AJAX call like everyone else, except that it didn't work for me initially, as I had used jQuery. It works perfectly if you do a XMLHttpRequest:
var url = http://url_to_test.com/
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status != 200) {
console.log("iframe failed to load");
}
};
xhttp.open("GET", url, true);
xhttp.send();
Edit:
So this method works ok, except that it has a lot of false negatives (picks up a lot of stuff that would display in an iframe) due to cross-origin malarky. The way that I got around this was to do a CURL/Web request on a server, and then check the response headers for a) if the website exists, and b) if the headers had set x-frame-options.
This isn't a problem if you run your own webserver, as you can make your own api call for it.
My implementation in node.js:
app.get('/iframetest',function(req,res){ //Call using /iframetest?url=url - needs to be stripped of http:// or https://
var url = req.query.url;
var request = require('https').request({host: url}, function(response){ //This does an https request - require('http') if you want to do a http request
var headers = response.headers;
if (typeof headers["x-frame-options"] != 'undefined') {
res.send(false); //Headers don't allow iframe
} else {
res.send(true); //Headers don't disallow iframe
}
});
request.on('error',function(e){
res.send(false); //website unavailable
});
request.end();
});
Have a id for the top most (body) element in the page that is being loaded in your iframe.
on the Load handler of your iframe, check to see if getElementById() returns a non null value.
If it is, iframe has loaded successfully. else it has failed.
in that case, put frame.src="about:blank". Make sure to remove the loadhandler before doing that.
If the iframe is loaded on the same origin as the parent page, then you can do this:
iframeEl.addEventListener('load', function() {
// NOTE: contentDocument is null if a connection error occurs or if
// X-Frame-Options is not SAMESITE (which could happen with
// 4xx or 5xx error pages if the corresponding error handlers
// do not specify SAMESITE). If error handlers do not specify
// SAMESITE, then networkErrorOccurred will incorrectly be set
// to true.
const networkErrorOccurred = !iframeEl.contentDocument;
const serverErrorOccurred = (
!networkErrorOccurred &&
!iframeEl.contentDocument.querySelector('#well-known-element')
);
if (networkErrorOccurred || serverErrorOccurred) {
let errorMessage;
if (networkErrorOccurred) {
errorMessage = 'Error: Network error';
} else if (serverErrorOccurred) {
errorMessage = 'Error: Server error';
} else {
// Assert that the above code is correct.
throw new Error('networkErrorOccurred and serverErrorOccurred are both false');
}
alert(errorMessage);
}
});