Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Debugging JavaScript 1

Status
Not open for further replies.

dwarfthrower

Programmer
Apr 22, 2003
1,625
AU
I was playing around with some stuff and thought some people here might find it useful.

We're familiar with the standard error messages our browser gives us, error description and line number, which help us track down the error. Sometimes we need a bit more info, for example if we have a function that is called from several places, or a function that processes user input.

What I came up with this afternoon was a bit of script to append a trace of the functions that were called, along with their arguments to the standard error info:
Code:
//Constructor for my ErrorObject
function ErrorObject(){
  this.errorNumber = "";
  this.lineNumber = "";
  this.errorType = "";
  this.errorDesc = "";
  this.errorMessage = "";
  this.errorLocation = "";
  this.triggerElement = "";
  this.triggerElementID = "";
  this.eventType = "";
  this.errorFunction = "";
  this.trace = new Array();
}

//Function to get the error information in a formatted
//string
function getErrorReport(){
  var detailedMessage = "A " + this.errorType + 
                        " error has been encountered: " + 
                        this.errorMessage +
                        "\nIn the " + this.errorFunction + 
                        " function on line " + 
                        this.lineNumber + 
                        "\nof the page at " + 
                        this.errorLocation +
                        "\n\nError Details:\n" + 
                        this.errorNumber + ": " + 
                        this.errorDesc +
                        "\n\nFunction Trace:\n" +
                        this.triggerElement + " (" +
                        this.triggerElementID +	") on" +
                        this.eventType + " event fired";

  //Loop back through all the traced functions
  for(var i = this.trace.length - 1; i >= 0; i--){
    detailedMessage = detailedMessage + 
                      "\nWhich called " + this.trace[i]
  }						
  return detailedMessage;
}
ErrorObject.prototype.errorReport = getErrorReport;

//function to get the information about the calling function
function getCallTrace(objError){
  // Get a handle to the requesting function
  var callingFunction = getCallTrace.caller;
  
  if(callingFunction != null){
    //Extract the name of the function that called us
    var strFName = "" + callingFunction
    strFName  = strFName .slice(0, strFName .indexOf("("));
    strFName  = strFName  + "(";
    //Get all it's arguments
    for(var i = 0; i < callingFunction.arguments.length; i++){
      strFName  = strFName  + 
                  (i > 0 ? ", " : "") +  
                  callingFunction.arguments[i];
    }
    strFName  = strFName  + ")";

    //Check to see if this was the function that triggered
    //the error
    if(GlobalError.errorFunction == ""){
      GlobalError.errorFunction = strFName .slice(9,strFName .indexOf("("));
    }
    //Check to make sure we're not being called at the highest
    //(document) level
    if(strFName  != "function anonymous()"){
      //Append the function to the trace
      GlobalError.trace[GlobalError.trace.length] = strFName ;
    }
  }
  //Get the type of the event that triggered the chain
  GlobalError.eventType = event.type;
  //Get a handle on the element that fired the event
  var trigger = event.srcElement;
  //If we get a null it means we were called from the document
  //itself rather than some lower level element
  var strEl = (trigger == null ? "document" : trigger.tagName);
  GlobalError.triggerElement = strEl;
  //Get some identifying feature from the triggering element
  var strElID = ""
  if(trigger == null){
    //if it was the document use the "body" to identify
    strElID = "body";
  }
  else{
    if(trigger.id == ""){
      if(trigger.name == ""){
        //If there's no ID and no NAME, give up
        strElID = "UNIDENTIFIED";
      }
      else{
        //If theres no ID but a NAME attribute, use that
        strElID = "name = " + 
                              trigger.name;
      }
    }
    else{
      //If there's an ID attribute we'll take that instead
      strElID = "id = " + trigger.id;
    }
  }
  GlobalError.triggerElementID = strElID;
  //Put some error info into the object
  GlobalError.errorNumber = (objError.number & 0xFFFF);
  GlobalError.errorDesc = objError.description;
  GlobalError.errorType = objError.name;
  
  //Throw the error back up the chain for further processing
  throw(objError);
}

//Function to append standard info and display message
//Gets called after the error has been passed all the way
//back up the chain
function getErrorDetails(message, href, line){
  //Add the standard info into the error object
  GlobalError.errorMessage = message;
  GlobalError.errorLocation = href;
  GlobalError.lineNumber = line;

  //Show the message (or you could do something like save it
  //somewhere)
  alert(GlobalError.errorReport());

  //Clear the GlobalError until next time
  GlobalError = new ErrorObject();

  //Stop the browser trying to handle the event
  return true;
}

//Set up the GlobalError Object
var GlobalError = new ErrorObject();

//Assign our getErrorDetails function to the window's 
//onerror event
window.onerror = getErrorDetails;

OK, so now we need to make sure our scripts can use this, wrap each function up in a try/catch block where the error is passed to getCallTrace for processing, eg:
Code:
function divideA(intA){
  try{
    var retVal = divideB(intA / 4);
	return retVal;
  }
  catch(e){
    getCallTrace(e);
  }
}

function divideB(intB){
  try{
	var retVal = divideC(intB / 3);
	return retVal;
  }
  catch(e){
    getCallTrace(e);
  }
}

function divideC(intC){
  try{
    var retVal = intC / 5;
	var piArray = new Array(3.14);
	return retVal;
  }
  catch(e){
    getCallTrace(e);
  }
}

Then test it out:
Code:
<button onclick="divideA(34)" id="mybutton">Click</button>

Please post any alterations, bugs and suggestions here and I'll wrap it up FAQ-style for future reference.

Never be afraid to share your dreams with the world.
There's nothing the world loves more than the taste of really sweet dreams.
 
I haven't been through this line by line yet, but this is incredibly useful and educational!

One interesting improvement (although I don't know how to do it), would be to figure out a way to take existing code and, without having to add try/catch blocks to EVERY function, add a few lines so that every function behaves AS IF it has a try/catch block around its body. Is that possible?

--Dave
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top