Debugging Javascript
Total Page:16
File Type:pdf, Size:1020Kb
6803.book Page 451 Thursday, June 15, 2006 2:24 PM APPENDIX ■ ■ ■ Debugging JavaScript In this appendix, I will introduce you to some tricks and tools to debug your JavaScript code. It is very important to get acquainted with debugging tools, as programming consists to a large extent of trying to find out what went wrong a particular time. Some browsers help you with this problem; others make it harder by having their debugging tools hidden away or returning cryptic error messages that confuse more than they help. Some of my favorites include philo- sophical works like “Undefined is not defined” or the MSIE standard “Object doesn’t support this property or method.” Common JavaScript Mistakes Let’s start with some common mistakes that probably every JavaScript developer has made during his career. Having these in the back of your head when you check a failing script might make it a lot quicker to spot the problem. Misspellings and Case-Sensitivity Issues The easiest mistakes to spot are misspellings of JavaScript method names or properties. Clas- sics include getElementByTagName() instead of getElementsByTagName(), getElementByID() instead of getElementById() and node.style.colour (for the British English writers). A lot of times the problem could also be case sensitivity, for example, writing keywords in mixed case instead of lowercase. If( elm.href ) { var url = elm.href; } There is no keyword called If, but there is one called if. The same problem of case sensi- tivity applies to variable names: var FamilyGuy = 'Peter'; var FamilyGuyWife = 'Lois'; alert( 'The Griffins:\n'+ familyGuy + ' and ' + FamilyGuyWife ); This will result in an error message stating “familyGuy is not defined”, as there is a variable called FamilyGuy but none called familyGuy. 451 6803.book Page 452 Thursday, June 15, 2006 2:24 PM 452 APPENDIX ■ DEBUGGING JAVASCRIPT Trying to Access Undefined Variables We talked about it in the first chapter of the book—you define variables either by declaring them with or without an additional var keyword (the latter is necessary to define the scope of the variable). Stewie = "Son of Peter and Lois"; var Chris = "Older Son of Peter and Lois"; If you try to access a variable that hasn’t been defined yet, you’ll get an error. The alert() in the following script throws an error as Meg is not defined yet. Peter = "The Family Guy"; Lois = "The Family Guy's Wife"; Brian = "The Dog"; Stewie = "Son of Peter and Lois"; Chris = "Older Son of Peter and Lois"; alert( Meg ); Meg = "The Daughter of Peter and Lois"; This is easy when it is an obvious example like this one, but how about trying to guess where the bug in the following example is? exampleFamilies.html function getFamilyData( outptID, isTree, familyName ) { var father, mother, child; switch( familyName ) { case 'Griffin': father = "Peter"; mother = "Lois"; child = "Chris"; break; case 'Flintstone': father = "Fred"; mother = "Wilma"; child = "Pebbles"; break; } var out = document.getElementById( outputID ); if( isTree ) { var newUL = document.createElement( 'ul' ); newUL.appendChild( makeLI( father ) ); newUL.appendChild( makeLI( mother ) ); newUL.appendChild( makeLI( child ) ); out.appendChild( newUL ); 6803.book Page 453 Thursday, June 15, 2006 2:24 PM APPENDIX ■ DEBUGGING JAVASCRIPT 453 } else { var str = father + ' ' + mother + ' ' + child; out.appendChild( document.createTextNode( str ) ); } } getFamilyData( 'tree', true, 'Griffin' ); Microsoft Internet Explorer tells you that there is an error in line 23—“‘outputID’ is unde- fined,” as shown in Figure A-1. Figure A-1. MSIE showing an error on line 23 However, if you look at the code in line 23 as shown in Figure A-2, nothing seems to be wrong. Figure A-2. The code shown in UltraEdit with a highlight on line 23 The culprit is a typo in the function parameter, highlighted in Figure A-3, which means that outputID is not defined but outptID is. 6803.book Page 454 Thursday, June 15, 2006 2:24 PM 454 APPENDIX ■ DEBUGGING JAVASCRIPT Figure A-3. The misspelled function parameter that caused the error Typos in parameters are a very confusing bug, as browsers tell you the error occurred in the line where the variable is used and not where you made the mistake. Incorrect Number of Closing Braces and Parentheses Another very common mistake is not closing curly braces or keeping an orphaned closing brace in the code when deleting some lines. Say, for example, you don’t need the isTree option any longer and you remove it from the code: exampleCurly.html function getFamilyData( outputID, familyName ) { var father, mother, child; switch( familyName ) { case 'Griffin': father = "Peter"; mother = "Lois"; child = "Chris"; break; case 'Flintstone': father = "Fred"; mother = "Wilma"; child = "Pebbles"; break; } var out = document.getElementById( outputID ); var newUL = document.createElement( 'ul' ); newUL.appendChild( makeListElement( father ) ); newUL.appendChild( makeListElement( mother ) ); newUL.appendChild( makeListElement( child ) ); 6803.book Page 455 Thursday, June 15, 2006 2:24 PM APPENDIX ■ DEBUGGING JAVASCRIPT 455 out.appendChild( newUL ); } } getFamilyData( 'tree', true, 'Griffin' ); The orphan closing brace shown in bold will cause a “syntax error in line 30.” The same problem occurs when you don’t close all the braces in a construct, a mistake that can easily happen when you don’t indent your code: exampleMissingCurly.html function testRange( x, start, end ) { if( x <= end && x >= start ) { if( x == start ) { alert( x + ' is the start of the range'); } if( x == end ) { alert(x + ' is the end of the range'); } if( x! = start && x != end ) { alert(x + ' is in the range'); } else { alert(x + ' is not in the range'); } } Running this example will cause an “expected ‘}’ in line 27” error, which is the last line of the script block. This means that somewhere inside the conditional construct we forgot to add a closing curly brace. Where the missing brace is supposed to be is rather hard to find, but a lot easier when the code is properly indented. exampleMissingCurlyFixed.html function testRange( x, start, end ) { if( x <= end && x >= start ) { if( x == start ) { alert(x + ' is the start of the range'); } if( x == end ) { alert(x + ' is the end of the range'); } if( x != start && x != end ) { alert(x + ' is in the range'); } } else { alert( x + ' is not in the range' ); } } 6803.book Page 456 Thursday, June 15, 2006 2:24 PM 456 APPENDIX ■ DEBUGGING JAVASCRIPT The previously missing curly brace is shown in bold (following the “is in the range” alert() message). Not closing or closing too many parentheses is another common problem. This happens when you nest functions in if() conditions and later on delete some of them. For example: if (all = parseInt(getTotal()){ doStuff(); } This causes an error, as you forgot to close the parentheses of the condition itself. if (all = parseInt(getTotal())){ ... } It can also happen when you nest too many methods and returns: var elm=grab(get(file).match(/<id>(\w+)<\/id>/)[1]; This one lacks the closing parentheses after the [1]: var elm=grab(get(file).match(/<id>(\w+)<\/id>/)[1]); In general, this kind of concatenation of functions is not good coding style, but there are situations where you will encounter examples like this one. The trick is to count the opening and the closing parentheses from left to right—good editors also highlight opening and closing parentheses automatically. You could also write a utility function to do this for you, which in itself is a test of your attention to detail when it comes to coding syntax: exampleTestingCodeLine.html function testCodeLine( c ) { if( c.match( /\(/g ).length != c.match( /\)/g) .length ) { alert( 'closing ) missing' ); } } c = "var elm=grab(get('demo.xml')" + ".match( /<id>(\w+)<\/id>/ )[1] );"; testCodeLine( c ); Concatenation Gone Wrong Concatenation is happening a lot when you use JavaScript to output HTML. Make sure that you don’t forget the + signs in between the different parts to concatenate to a whole. father = "Peter"; mother = "Lois"; child = "Chris"; family = father+" "+mother+" "child; 6803.book Page 457 Thursday, June 15, 2006 2:24 PM APPENDIX ■ DEBUGGING JAVASCRIPT 457 The preceding lacks one addition sign before the child variable: father = "Peter"; mother = "Lois"; child = "Chris"; family = father+" "+mother+" "+child; Another obstacle is to make sure you don’t concatenate the wrong data types. father = "Peter"; fAge = 40; mother = "Lois"; mAge = 38; child = "Chris"; cAge = 12; family = father + ", " + mother + " and " + child + " Total Age: " + fAge + mAge + cAge; alert( family ); This will not show the desired result, but this instead: Peter, Lois and Chris Total Age: 403812 The mistake is that you concatenate strings and numbers as the + operator works from left to right. You need to add parentheses around the age term: father = "Peter"; fAge = 40; mother = "Lois"; mAge = 38; child = "Chris"; cAge = 12; family = father + ", " + mother + " and " + child + " Total Age: " + (fAge + mAge + cAge); alert(family); This results in the desired outcome: Peter, Lois and Chris Total Age: 90 6803.book Page 458 Thursday, June 15, 2006 2:24 PM 458 APPENDIX ■ DEBUGGING JAVASCRIPT Assigning Instead of Testing the Value of a Variable When testing the value of a variable, it is all too easy to assign it instead of testing it: all you need to do is forget an equal sign. if(Stewie = "talking") { Brian.hear(); } This will entice Brian to hear all the time, not only when Stewie has something to say; however, adding one equal sign does make Brian hear only when Stewie talks: if(Stewie == "talking") { Brian.hear(); } Tracing Errors with alert() and “Console” Elements The easiest way to trace errors is to use alert() wherever you want to test a certain value. The alert() method will stop the script execution (with the exception of Ajax calls that might still be going on in the background) and provide you with information about the value of a certain vari- able, and you can deduce if that value is correct or if it is the cause of the error.