Suppressing HTMLImageElement onerror [duplicate] - javascript

This question already has an answer here:
Check if file exists but prevent 404 error in console from showing up [duplicate]
(1 answer)
Closed 8 years ago.
I'm running a script that is dynamically loading images depending on a few criteria. The script does not know beforehand whether a specific image source actually exists and will thus need to check before displaying the image. I do this by replacing the onerror handler on the element with a function that attempts to gracefully handle the event.
At first glance this works rather well, however even though I have replaced the event, the browser still audits 404 errors in the console which I don't want. Even worse is that IE displays the infamous JS error icon in the status bar which I find rather awkward.
I've tried to summarise the problem in a JSFiddle.
var img = new Image();
img.onerror = function (ev) {
alert("This does not exist!");
return true;
}
img.onload = function (ev) {
document.body.appendChild(this);
}
img.src = "foo.png"; //Missing image
Basically, I want to suppress all error reporting for this element such that the console doesn't get flooded with superfluous error output.
I know that I could solve this by prefetching and evaluating the HTTP headers with AJAX and server side scripting, which while technically a possible solution, is something I would prefer to avoid. However, while I only use standard JS in my example, JQuery code is also acceptable.
I have tried reading up on the event specification, but since web scripting is still the mess of confusing ECMAScript, HTML DOM, client, pixy dust and now HTML5 definitions that we all love to hate, it really didn't make me any wiser. The closest I got was Mozilla's documentation (that interestingly doesn't even state the correct function parameters) which suggested that letting the event return true would suppress errors, but that didn't really work.

I believe you can not check if image link is broken/does not exist without getting 404 error. Which is actually is information about link is broken.
You mentioned that other way is ajax to check existance...
function UrlExists(url) {
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
return http.status != 404;
}
UrlExists('img_url');
but still you will get 404 in console.

Related

img.onerror() does not appear to fire if URL has a bad domain

First, let's everybody look at this adorable owl:
http://happy.fm/wp-content/uploads/2011/10/random-owl.jpg
I'm using that as a test image to see if I can detect, using client-side Javascript, whether a given URL is a legit image. I hunted around online and found some simple code that's supposed to work:
const img = new Image();
img.onload = () => {
console.log("HOORAY! SUCCESS!", img);
}
img.onerror = () => {
console.log("BOO! FAIL!")
}
img.src = values.image;
If I put in a URL which has a valid domain, but bad file path, like this:
http://happy.fm/wp-content/uploads/2011/10/random-owlDOESNOTEXIST.jpg
...then onerror() fires reliably. However, if the domain is bogus, like this:
http://happy.fmDOESNOTEXIST/wp-content/uploads/2011/10/random-owl.jpg
...then onerror() never fires. Is this, like, expected behavior? Is there any way around this?
And yes, I realize doing only client-side validation isn't the best way to really handle this issue, but pretend for the moment that this is the only way.
EDIT: it's worth mentioning that I'm seeing this error in a Node.js/Webpack local environment, running on top of the webpack-dev-server. Maybe this becomes some sort of permissions problem.
Okay, I'm an idiot. I had an upstream check to validate whether or not the inbound URL was in a valid format. When I checked the domain, I did this:
http://happy.f/wp-content/uploads/2011/10/random-owl.jpg
It's not legal to have a top-level domain that's a single character, so my validate routine caught that. As soon as I put in a validly-formatted-but-still-bogus top-level domain, like this:
http://happy.fasdfasdfasdfasdf/wp-content/uploads/2011/10/random-owl.jpg
my onerror() method fired.
So...d'oh.

How do we track Javascript errors? Do the existing tools actually work?

Today I find the need to track and retrieve a Javascript error stacktrace to solve them.
Today we were able to capture all rest calls, the idea is that once you get an error, automatically posts the stacktrace of that error plus the responses of the rest saved services so we can detect, reproduce, and solve the problems in almost an identical environment/situation.
As a requirement we were asked to make a module that can be included without being intrusive, for example:
Include the module that contains the hook logic in one JS, would be not invasive, include several lines of code in various JS files would be invasive.
The goal is to make a tool that can be included in a system already developed and track error events (like console).
I've read about this trackers logic:
errorception.com/
trackjs.com/
atatus.com/
airbrake.io/
jslogger.com/
getsentry.com/
muscula.com/
debuggify.net/
raygun.io/home
We need to do something like that, track the error and send it to our server.
As "Dagg Nabbit" says... "It's difficult to get a stack trace from errors that happen "in the wild" right now"...
So, we got a lot of paid products, but how did they really works?
In Airbrake they use stacktrace and window.onerror:
window.onerror = function(message, file, line) {
setTimeout(function() {
Hoptoad.notify({
message : message,
stack : '()#' + file + ':' + line
});
}, 100);
return true;
};
But i cant figure out when the stacktrace really used.
At some point, stacktrace, raven.js and other trackers need try / catch.
what happens if we found a way to make a global wrapper?
Can we just call stacktrace and wait for the catch?
How can I send a stack trace to my server when an unexpected error occurs on the client? Any advice or good practices?
It's difficult to get a stack trace from errors that happen "in the wild" right now, because the Error object isn't available to window.onerror.
window.onerror = function(message, file, line) { }
There is also a new error event, but this event doesn't expose the Error object (yet).
window.addEventListener('error', function(errorEvent) { })
Soon, window.onerror will get a fifth parameter containing the Error object, and you can probably use stacktrace.js to grab a stack trace during window.onerror.
<script src="stacktrace.js"></script>
<script>
window.onerror = function(message, file, line, column, error) {
try {
var trace = printStackTrace({e: error}).join('\n');
var url = 'http://yourserver.com/?jserror=' + encodeURIComponent(trace);
var p = new printStackTrace.implementation();
var xhr = p.createXMLHTTPObject();
xhr.open('GET', url, true);
xhr.send(null);
} catch (e) { }
}
</script>
At some point the Error API will probably be standardized, but for now, each implementation is different, so it's probably smart to use something like stacktracejs to grab the stack trace, since doing so requires a separate code path for each browser.
I'm the cofounder of TrackJS, mentioned above. You are correct, sometimes getting the stack traces requires a little bit of work. At some level, async functions have to be wrapped in a try/catch block--but we do this automatically!
In TrackJS 2.0+, any function you pass into a callback (addEventListener, setTimeout, etc) will be automatically wrapped in a try/catch. We've found that we can catch nearly everything with this.
For the few things that we might now, you can always try/catch it yourself. We provide some helpful wrappers to help, for example:
function foo() {
// does stuff that might blow up
}
trackJs.watch(foo);
In latest browsers, there is a 5th parameter for error object in window.onerror.
In addEventListener, you can get error object by event.error
// Only Chrome & Opera pass the error object.
window.onerror = function (message, file, line, col, error) {
console.log(message, "from", error.stack);
// You can send data to your server
// sendData(data);
};
// Only Chrome & Opera have an error attribute on the event.
window.addEventListener("error", function (event) {
console.log(e.error.message, "from", event.error.stack);
// You can send data to your server
// sendData(data);
})
You can send data using image tag as follows
function sendData(data) {
var img = newImage(),
src = http://yourserver.com/jserror + '&data=' + encodeURIComponent(JSON.stringify(data));
img.crossOrigin = 'anonymous';
img.onload = function success() {
console.log('success', data);
};
img.onerror = img.onabort = function failure() {
console.error('failure', data);
};
img.src = src;
}
If you are looking for opensource, then you can checkout TraceKit. TraceKit squeezes out as much useful information as possible and normalizes it. You can register a subscriber for error reports:
TraceKit.report.subscribe(function yourLogger(errorReport) {
// sendData(data);
});
However you have to do backend to collect the data and front-end to visualize the data.
Disclaimer: I am a web developer at https://www.atatus.com/ where you can track all your JavaScript errors and filter errors across various dimensions such as browsers, users, urls, tags etc.
#Da3 You asked about appenlight and stacktraces. Yes it can gather full stacktraces as long as you wrap the exception in try/catch block. Otherwise it will try reading the info from window.onerror which is very limited. This is a browser limitation (which may be fixed in future).

Phonegap App Error - connection to the server was unsuccessful

I have the following issue and I'm a bit new to Phonegap! On my index page I have three functions that will create a Javascript Prompt asking the user for their name, email and title (position) and store each to the localStorage. Three items like this:
function promptName(){
var salesPName = prompt("Bitte geben Sie Ihren Namen","");
if(salesPName == null || salesPName == ""){
promptName()
}else{
localStorage.setItem("salesP", salesPName);
}
}
Then using $(document).ready I call these three functions:
$(document).ready(function(){
if(!localStorage.getItem("salesP")){
promptName();
promptEmail();
promptPosition();
}
});
This is all working well, however when deploying to my Android device I get the prompts but before I can complete all three I get an error dialog stating:
The connection to the server was unsuccessful (file:///android_asset/www/appname/index.html)
Removing the prompts removes the error but I need this functionality. I have tried different ways of calling the functions, for example on the body tag's onload event or using .load(). I still get this error. I thought about setting a Javascript interval to call this after a few seconds (once the page is loaded) as I'm sure the problem is due to Javascript's blocking nature. Has anyone come across this before?
Please note that I added the following to the com.mypackage.xxx.java file (as advised from phonegap, connection to server unsuccessful)
super.setIntegerProperty("loadUrlTimeoutValue", 10000);
And I still get the problem!
with nothing working I put a setTimeout() around my condition like so...
$(document).ready(function(){
setTimeout(function (){if(!localStorage.getItem("salesP")){
// item doesn't exist... so let's raise some dialogs to capture the name, email address and title
promptName();
promptEmail();
promptPosition();
}
}, 5000)
});
now it works fine... a bit of a fudge but so what, if anyone has any ideas on a better solution or any objections to this please let me know

Is it possible to know the target DOMWindow for an HTTPRequest?

I'm developing a firefox extension which requires me to intercept page loads by filtering out some HTTPRequests.
I did that using the instructions given here. Please note that my question draws from the content of this link.
I used the method given under the section of HTTPObservers. And it worked, I am indeed able to extract the respective urls of the Requests being sent out.
However, another thing which I really require is to get the target DOM Window where the contents pertaining to the HTTPRequest were about to be loaded. Is it possible using HTTPObservers?
In the link above, another way has been described using WebProgressListeners.
I tried that out as well. The onLocationChange() method only returns location changes in the url bar. Is it somehow possible to get the HTTPRequest urls using any of these progress listeners? Because if so, then if I understand correctly, aWebProgress.DOMWindow would give me the window I require.
Note: I am using gwt for the extension and the JSNI for the above mentioned part.
You can usually do that by using nsILoadContext interface (sadly barely documented) attached to the request or its load group. Here is how you would do that:
function getWindowForRequest(request)
{
if (request instanceof Components.interfaces.nsIRequest)
{
try
{
if (request.notificationCallbacks)
{
return request.notificationCallbacks
.getInterface(Components.interfaces.nsILoadContext)
.associatedWindow;
}
} catch(e) {}
try
{
if (request.loadGroup && request.loadGroup.notificationCallbacks)
{
return request.loadGroup.notificationCallbacks
.getInterface(Components.interfaces.nsILoadContext)
.associatedWindow;
}
} catch(e) {}
}
return null;
}
Note that this function is expected to return null occasionally - not every HTTP request is associated with a window.

Detect failure to load contents of an iframe

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);
}
});

Categories

Resources