ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
E. Debugging
When a browser doesn't understand a certain JavaScript command, it produces a JavaScript error message. You need these messages for debugging, but unfortunately they're sometimes hard to read. Occasionally the error messages won't help you, or aren't there at all, even though something is clearly wrong with your scripts. In these cases, you have to switch to other bug-hunting techniques. Error messages
First, you must make sure that you see error messages when they occur. In most browsers, this means opening the JavaScript error console:
You'll notice that the error messages vary greatly in quality. Take this wrong bit of script: I accidentally forgot the last 't' of 'test', so the browsers don't know what I'm talking about. Let's see how they react. Figure 3.2. Mozilla's error message. Clear and to the point.
Mozilla's message is the clearest: 'x has no properties' in line 9. Clicking on the link immediately takes you to the correct line. Figure 3.3. Opera, too, gives useful information.
Although its 'could not convert' message is a bit cryptic, Opera shows not only where the error occurred, but also from where the function that contains the error was called. Essentially it follows this track until it finds a function called in response to an event. This is called a stack trace. Figure 3.4. Explorer's line numbers can't handle included scripts.
Explorer, unfortunately, is vague. It says "Object required", but you see this message in about 60% of the problems, and Explorer never specifies which object it requires or why. In addition, Explorer has trouble with included scripts: <script src="/books/1/521/1/html/2/included.js"></script> If you add separate script files to your pagesand in 2C we saw that this is best practice nowadaysExplorer still considers the error to have occurred in the main HTML page. Therefore, its line numbers usually don't make sense. Figure 3.5. Safari's messages are not very useful.
Safari, finally, gives the most cryptic error message of all: "Null value". True, but unenlightening. Since Mozilla has the best error messages, whenever I encounter a problem I immediately switch to Mozilla to get a good, solid error message that actually tells me what's wrong. I advise you to do the same. Of course, if the error occurs only in Explorer or Safari, you have a problem, and it's time for a more in-depth bug hunt.
Dealing with browser bugs
Sometimes an error message will immediately reveal what's wrong and why. Sometimes, though, even Mozilla's errors don't help you any furtheryou just don't know what's going on. Especially worrisome are the occasions when something is clearly wrong, yet no error message appears. In all these cases you have to isolate the bug by hand. This generally involves three steps:
Using alerts
An alert statement stops the execution of the function it's in, which gives you the chance to check if the bug has already occurred. Take the wrongly spelled 'test' example we saw earlier. Assume that I haven't yet spotted the missing 't' and I'm still searching for the cause of the bug. The alert gives a definite clue, because it itself causes the bug. That will quickly lead you to suspect that the value of x is incorrect. In a real situation, I would change the alert: alert(x);
Now an alert pops up that says 'undefined'. Now I'm certain that the bug is caused by x being not defined; I only have to study the assignment to x, and I'll probably spot the missing 't'. Alerts and confirms
Sometimes alerts can be annoying. Let's say an error occurs somewhere in a long for-loop that goes through all <tr>s in an HTML page. I suspect it has something to do with the number of cells in one row, and I want to study the number of cells every row has. An alert allows me to do that: var rows = document.getElementsByTagName('tr'); for (var i=0;i<rows.length;i++) { var cells = rows[i].getElementsByTagName('td'); alert(i + ': ' +cells.length); var name = cells[1].firstChild.nodeValue; }
This works, up to a point. I now see the index number of the row and its number of cells. Unfortunately, the alert pops up every time the script finds a new <tr> to work on, and if your HTML contains hundreds of <tr>s you'll see hundreds of alerts. That gets old in a hurry. Instead, you could use a confirm: var rows = document.getElementsByTagName('tr'); for (var i=0;i<rows.length;i++) { var cells = rows[i].getElementsByTagName('td'); if (!confirm(i + ': ' +cells.length + '. Continue?')) break; var name = cells[1].firstChild.nodeValue; }
The confirm contains exactly the same information as the alert, but in addition it offers you the choice of breaking off the function when you've found the faulty row. If the confirm returns false (i.e., if you press the Cancel button), the for-loop breaks off and you're not forced to wade through more alerts. Error console
During complicated debugging sessions I occasionally create an error console: function initConsole() { var console = document.createElement('div'); console.id = 'errorConsole'; document.body.appendChild(console); } function writeToConsole(message) { var newMessage = document.createElement('p'); newMessage.innerHTML = message; var console = document.getElementById('errorConsole'); console.appendChild(newMessage); }
Now I can use writeToConsole() every time I need a debugging text: function complicated() { var x = [an object that isn't what I expect it to be]; writeToConsole('x is now ' + x.nodeName); }
In the old days something similar was done with popups; 6F contains a code example. Example
In order to explain how to deal with your daily dose of browser bugs, I'm going to give you a real example. When I was preparing Usable Forms for this book, I noticed that Mozilla behaved oddly when I soft-reloaded the page. After a soft reload (i.e., a reload during which the user does not press the Shift button) Mozilla and Explorer retain the values of any form fields. However, Mozilla always checked the radio button above the one that was checked before the reload. It didn't give an error message; as far as it was concerned everything was fine. This was clearly a bug, either in my script or in Mozilla. I needed to find out more. Figure 3.6. The Mozilla bug. Go to Usable Forms, check Divorced, and then soft-reload the page. Now Married will be checked.
From the outset I suspected this was a Mozilla bug, because Explorer uses exactly the same code and didn't have any problems. Therefore Mozilla misinterpreted a bit of code. But which bit? I needed to isolate the bug. I started at step 1 and added a return statement to a function. For instance, here I turned off setDefaults(): [Usable Forms, lines 56-67, condensed and changed] function setDefaults() { return; var y = document.getElementsByTagName('input'); for (var i=0;i<y.length;i++) { if (y[i].checked && y[i].getAttribute('show')) intoMainForm(y[i].getAttribute('show')) } // etc. } If the bug was caused by this function, it would not appear any more. Unfortunately, it did appear, so I was checking the wrong function. I moved the return to the next function and reloaded the page. The bug stubbornly remained active until I closed down the initialization function prepareForm(). Now the bug disappeared. So, it appeared the error was somewhere in prepareForm(). To find the exact line of code that triggers the bug, I switched to alerts: [Usable Forms, lines 16-19, changed] function prepareForm() { if (!compatible) return; var marker = document.createElement(relatedTag); marker.style.display = 'none'; alert('Made it to here'); I placed an alert after the first bit of code in prepareForm() and reloaded the page. The alert popped up, and to my amazement the wrong radio button was already checked. It seemed the bug was caused by the lines hitherto executed. That was odd, because these lines don't do anything to the form. I moved the alert to the very first line of the function: [Usable Forms, lines 16-19, changed] function prepareForm() { alert('Made it to here'); if (!compatible) return; var marker = document.createElement(relatedTag); marker.style.display = 'none';
Lo and behold, the wrong radio button was still checked. Apparently the bug wasn't caused by my script at all. It was already present before the script had had the chance to run. Now what? Was this a native bug in Mozilla? Does Mozilla always check the wrong radio button? I disabled the script and soft-reloaded the page. The second time I did that, the correct radio button remained checked. Then I re-enabled my script, and the buggy behavior recurred, but only the second time I soft-reloaded the page, not the very first time. This was worth a more detailed investigation. I disabled and enabled the script a few times, and found that the bug always occurred when the previous page ran my script. It didn't seem to matter whether the script ran in the current page. Finding browser bugs is a matter of 99% perspiration and 1% inspiration. I'd already done the hard work, and I was now rewarded by a flash of inspiration. If my script was enabled, the previous page hid a few form fields! I created a second form field before the radio buttons, made sure it was also hidden, and behold: the radio check now moved up not one, but two radio buttons. Therefore, the bug was caused by the hidden form fields in the previous page. Apparently Mozilla makes some indexing mistake when form fields are hidden. Now that I had a definite handle on the bug, I created a test page. As I discussed in 3B, this step is absolutely crucial, since it forces you to create exactly those circumstances that trigger the bug, and leave out all irrelevant code, which in turn increases your understanding of the bug. So I created a simple test page with one input and four radio buttons. I checked the last radio and reloaded. Nothing happened. Then I added a little script that removes the single input, and the bug reappeared in all its glory. The bug was now successfully isolated and described. A search in Mozilla's Bugzilla database (see next page) turned up a similar one that has been known since 2002.
So, was I going to leave the bug as it was? Or should I try to solve it, for instance by setting a cookie that remembers the state of all radio buttons? In the end I decided not to bother, because solving the bug would make my script much more complicated, and because the bug affects only one browser in specific circumstances (how often will people reload a form page in Mozilla?). Feel free to disagree with my decision in this specific situation, but sometimes solving a browser bug is more trouble than it's worth. Reporting browser bugs
Once you've successfully isolated a bug, you should report it to the relevant browser vendor. All four major vendors offer bug reporting facilities. Safari
Safari offers the easiest feedback mechanism. Just use its 'Report Bugs to Apple' feature and answer a few questions. Figure 3.7. Safari's bug-reporting feature sends a bug report straight into Apple's database.
Opera
Opera makes it easy, too. Go to http://www.opera.com/support/bugs/ and carefully read the page, since it contains a useful summary of a proper bug reporting process. Opera requires a good test case that has isolated the bug as much as possible. Once you've created that page, you can follow the links to the bug-report form. Mozilla
Reporting a Mozilla bug is harder. The complete bug database of Mozilla is online at http://bugzilla.mozilla.org/, but you need some experience with Bugzilla in order to use it. First you need to create an account. Then Mozilla requires you to check if the bug has already been reported. That's a reasonable request, but most bug reports are hard to read if you don't know Mozilla's and Bugzilla's internal structure. The few times I reported a bug it turned out that the bug was already known, but I couldn't find it because I didn't understand these structures. Microsoft
The Microsoft bug reporting facilities are at https://connect.microsoft.com/feedback/default.aspx?SiteID=136 and they are only for Explorer 7 and later. Please do not report bugs in earlier Explorer versions. Note that you need a Microsoft Connect account in order to gain access. QuirksMode
Finally, I have my own bug-reporting facilities at http://www.quirksmode.org/bugreports/. The reports here are primarily aimed at Web developers, although browser vendors occasionally go through these lists, too. A proper test page is required. If you report your bugs here, other Web developers will be able to find them, and will thank you for saving valuable portions of their time (and hair). Please be patient
You shouldn't expect a bug that you report to a browser vendor to be solved within a few weeks. Usually browser vendors address dozens of bugs simultaneously, and their time is limited. Besides, the bug you report may depend on other bugs, or it may not be important enough for immediate action. Patience is a virtue when dealing with browser bugs. |