18

I would like to pass errors to an alert to warn the user they made mistake in their code even if they don't have console open.

    var doc=(frame.contentWindow.document || obj.contentDocument|| obj.contentWindow);
    var head = doc.getElementsByTagName('head')[0];
    var scriptElement = doc.createElement('script');
    scriptElement.setAttribute('type', 'text/javascript');
    scriptElement.text = scripts;

    try{
        head.appendChild(scriptElement);
    }
     catch(e){ alert("error:"+e.message +"  linenumber:"+e.lineNumber);}

The appendChild throws an error when the scripts contain an error. It goes straight to the console though, and I want it to display in an alert, because it is for kids and they might not check the console. The try catch block does not catch the error. I tried it with eval(scripts).

   try{
   eval(scripts);} catch(e){ alert("error:"+e.message +"  linenumber:"+e.lineNumber);}

this does work but it means that the code is executed twice, and that is very inconvenient in some cases.

I tried monkey patching the console.error:

       console.log=function(){alert("taking over the log");}
       console.error=function(){alert("taking over the log");}

but that only works when I literally use console.error. Not when an actual error is thrown. What function sends the error to the console in the case of a real error,if it isn't console.error? and can I access it and change it? Any ideas? Help would be really appreciated. Thanks Jenita

joeytwiddle
  • 29,306
  • 13
  • 121
  • 110
Jenita
  • 291
  • 1
  • 3
  • 15

3 Answers3

25

Whilst try ... catch will work on the code that the script runs initially, as Jenita says it won't catch Syntax Errors, and also it won't catch errors thrown by callback functions which execute later (long after the try-catch has finished). That means no errors from any functions passed to setTimeout or addEventListener.

However, you can try a different approach. Register an error listener on the window.

window.addEventListener("error", handleError, true);

function handleError(evt) {
    if (evt.message) { // Chrome sometimes provides this
      alert("error: "+evt.message +" at linenumber: "+evt.lineno+" of file: "+evt.filename);
    } else {
      alert("error: "+evt.type+" from element: "+(evt.srcElement || evt.target));
    }
}

This will be called when an exception is thrown from a callback function. But it will also trigger on general DOM errors such as images failing to load, which you may not be interested in.

It should also fire on Syntax Errors but only if it was able to run first so you should put it in a separate script from the one that may contain typos! (A Syntax Error later in a script will prevent valid lines at the top of the same script from running.)

Unfortunately, I never found a way to get a line number from the evt in Firefox. (Edit: Poke around, I think it might be there now.)


I discovered this when trying to write FastJSLogger, an in-page logger I used back when the browser devtools were somewhat slow.

Desperate to catch line numbers, I started to experiment with wrappers for setTimeout and addEventListener that would re-introduce try-catch around those calls. For example:

var realAddEventListener = HTMLElement.prototype.addEventListener;

HTMLElement.prototype.addEventListener = function(type,handler,capture,other){
    var newHandler = function(evt) {
        try {
            return handler.apply(this,arguments);
        } catch (e) {
            alert("error handling "+type+" event:"+e.message +"  linenumber:"+e.lineNumber);
        }
    };

    realAddEventListener.call(this,type,newHandler,capture,other);
};

Obviously this should be done before any event listeners are registered, and possibly even before libraries like jQuery are loaded, to prevent them from grabbing a reference to the real addEventListener before we have been able to replace it.

joeytwiddle
  • 29,306
  • 13
  • 121
  • 110
  • I can't seem to get a trigger here for e.g., Uncaught (in promise) TypeError: Cannot read properties of undefined thrown in the Chrome console. Would we expect that case to be handled too? – Cheetara Oct 01 '21 at 16:22
  • @Cheetara I can't see your code. But a common issue with promises is that the `then(_)` callback is a separate function, which only gets called on a future tick, so an outer try-catch won't catch its errors. You can either: 1. Always add a trailing .catch() to handle any errors ([details here](https://gist.github.com/joeytwiddle/0e73697280ea9226f1c0)). 2. Use async-await instead of .then() because try-catch does work there. 3. Listen for [unhandledrejection event](https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event) to catch any uncaught promise errors. – joeytwiddle Oct 05 '21 at 00:01
3

Ok so the less elegant but highly efficient way of doing this is 'refactoring' your innate console functions. Basically any error or warnings you get are being outputted there by a javascript function that is pretty similar to the familiar console.log() function. The functions that I am talking about are console.warn(), console.info() and console.error(). now let's 're-map' what each of those do:

//remap console to some other output
var console = (function(oldCons){
    return {
        log: function(text){
            oldCons.log(text);
            //custom code here to be using the 'text' variable
            //for example: var content = text;
            //document.getElementById(id).innerHTML = content
        },
        info: function (text) {
            oldCons.info(text);
            //custom code here to be using the 'text' variable
        },
        warn: function (text) {
            oldCons.warn(text);
            //custom code here to be using the 'text' variable
        },
        error: function (text) {
            oldCons.error(text);
           //custom code here to be using the 'text' variable
        }
    };
}(window.console));

//Then redefine the old console
window.console = console;

Now, generally I would highly advise against using something like this into production and limit it to debugging purposes, but since you are trying to develop a functionality that shows the output of the console, the lines are blurry there, so I'll leave it up to you.

liviu blidar
  • 351
  • 4
  • 16
1

You could wrap the script in its own try/catch, something like:

var doc=(frame.contentWindow.document || obj.contentDocument|| obj.contentWindow);
var head = doc.getElementsByTagName('head')[0];
var scriptElement = doc.createElement('script');
scriptElement.setAttribute('type', 'text/javascript');
scriptElement.text = "try{"+scripts+"}catch(e){console.error(e);alert('Found this error: ' + e +'. Check the console.')}"
head.appendChild(scriptElement);
Wyatt
  • 2,491
  • 15
  • 12
  • Hi I tried it but that didn't work. Does someone understand why the error isn't caught by the try catch block around appendChild. Is it because appendChild calls another function(maybe evals) after its own code, and that function catches the error and sends the error straight to the console. Does anyone know how errors are sent to the console. Is it a method of the error class? – Jenita Nov 27 '13 at 23:06
  • 1
    actually it does work for typeerrors , but not for syntax errors – Jenita Nov 27 '13 at 23:35
  • It also won't work for errors thrown by callback functions (e.g. from setTimeout) or event handlers (e.g. from addEventListener). – joeytwiddle Nov 28 '13 at 02:05