Modern JavaScript debugging. Mistake four: a “clone” attribute that shouldn’t be there
It's easy to get lost writing JavaScript code without a debugger.
JavaScript DebuggingIt's difficult to write JavaScript code without a debugger.
Your code may contain syntax errors or logical errors that are difficult to diagnose.
Often, when JavaScript code contains errors, nothing will happen. There are no error messages and you won't be given any instructions on where to look for errors.
Typically, errors will occur every time you try to write something new code JavaScript.
JavaScript DebuggersFinding errors in program code called debugging code.
Debugging is not that easy. But fortunately everything modern browsers have a built-in debugger.
Built-in debuggers can be turned on and off, forcing errors to be reported to the user.
With a debugger, you can also set breakpoints (places where code execution can be stopped) and examine variables while the code is running.
Generally, otherwise follow the instructions at the bottom of this page, you will enable debugging in the browser using the F12 key, and select "Console" from the debugger menu.
If your browser supports debugging, you can use console.log() to display JavaScript values in the debugger window:
exampleMy First Web Page
a = 5;
b = 6;
c = a + b;
console.log(c);
In the debugger window, you can set breakpoints to JavaScript code.
On each control point, JavaScript will stop executing and allow you to explore the JavaScript values.
After examining the value, you can resume executing the code (usually using the play button).
Debugger KeywordDebugger keyword stops JavaScript execution and calls (if any) to the debugging function.
This has the same function by setting a breakpoint in the debugger.
If debugging is not available, the debugger statement has no effect.
With the debugger enabled, this code will stop executing before it executes the third line.
Major browsers "Debug tools"Typically, you will enable debugging in the browser with F12, and select "Console" from the debugger menu.
Otherwise, follow these steps:
Chrome- Open your browser.
- From the menu, select tools.
- Finally, select Console.
- Open your browser.
- Go to web page:
http://www.getfirebug.com - Follow the instructions as:
install Firebug
- Open your browser.
- From the menu, select tools.
- From tools, select developer tools.
- Finally, select Console.
- Open your browser.
- Go to web page:
http://dev.opera.com - Follow the instructions as:
add a developer console button to the toolbar.
- Open your browser.
- Go to web page:
http://extensions.apple.com - Follow the instructions as:
install Firebug Lite.
- Go to Safari, Settings, Advanced in the main menu.
- Check the "Enable Show menu in menu bar Develop" checkbox.
- When the new "Develop" option appears in the menu:
Select "Show Error Console".
Imagine that you are working on this incredible new web application and your testers have asked you to fix the following bugs:
- In Firefox, you must make sure that you have installed Firebug extension. Select “Tools > Firebug > Open Firebug”.
- In Opera 9.5+, select “Tools > Advanced > Development Tools.”
- In IE beta, go to “Tools > Panels > Explorer Bars > IE Developer Toolbar.”
- In Safari or WebKit, first enable the debug menu (1), then select “Develop > Show Web Inspector”
rice. 1: Initial view of our JavaScript application in Dragonfly and Firebug, respectively.
When you look at the source code in the debugger, notice the clearLoadingMessage() function at the very beginning of the code. This is a good place for a checkpoint.
How to install it:
When the page is reloaded, the script will stop running and you will see what is shown in Figure two.
rice. 2: Debuggers stopped at a breakpoint inside clearLoadingMessage.
Let's take a look at the function code. As you can easily see, it updates two DOM elements, and line 31 mentions the word statusbar. It looks like getElements("p", ("class":"statusbar")).innerHTML looks for the statusbar element in the DOM tree. How can we quickly test our assumption?
Paste this statement into the command line to test. Figure three shows three screenshots (Dragonfly, Firebug and IE8) after reading innerHTML or outer HTML element, returned by the command you are researching.
To check, do the following:
* In Firebug, switch to the “Console” tab.
* In Dragonfly, look below the JavaScript code panel.
* In IE8, find the "Console" tab on the right.
rice. 3: Output the command result in Dragonfly, Firebug, and IE8, respectively.
The command line is very useful tool, which allows you to quickly test small pieces of code. The Firebug console integration is very useful - if your command outputs an object, you get a very intelligent view. For example, if it is a DOM object - you will see the marked up result.
You can use the console to do more in-depth research. JavaScript string, which we are studying, does the following three things:
Surprise, at the end... nothing. Thus, the expression getElements("p",("class:"statusbar"")).firstChild points to some object in the DOM that does not contain any text, or does not have an innerText property.
Then, the next question is: what actually comes first? child element at the paragraph? Let's ask this question at the command line. (See fourth picture).
rice. 4: command line StDragonfly debugger, output [Text object].
The Dragonfly's debugger output - [Text object] shows that this is a DOM text node. Thus we found the cause of the first problem. A text node does not have an innerText property, hence setting p.firstChild.innerText to a value does nothing. This error can easily be fixed by replacing innerText with nodeValue, which is a property defined by the W3C standard for text nodes.
Now that we've dealt with the first error:
To find where the localization problem is likely occurring, do the following:
rice. 5: Search in Dragonfly and WebInspector.
To check what this function does:
var str1 = navigator.userAgent.match(/\((.*)\)/);
var ar1 = str1.split(/\s*;\s*/), lang;
for (var i = 0; i< ar1.length; i++){
if (ar1[i].match(/^(.(2))$/))(
lang = ar1[i];
}
}
As you step through the code, you can use the Local Variables Viewer. Figure 6 shows how it looks in Firebug and IE8 DT; we expanded the ar1 array to see its elements.
rice. 6: Pane for viewing local variables of the getLanguage function in Firebug IE8’s
The expression ar1[i].match(/^(.(2))$/) simply searches for a string consisting of two characters, such as “no”, “en”. However, as you can see in the screenshot in Firefox, information about the language is presented in the form “nn-NO” (2). IE does not put language information into the user agent at all.
Thus, we found the second error: the language was determined by searching for a two-letter code in the user agent line, but Firefox has a five-character language designation, and IE does not have it at all. Such code must be rewritten and replaced with language detection either on the server side using the Accept-Language HTTP header, or by retrieving it from navigator.language (navigator.userLanguage for IE). Here is an example of what such a function could be
function getLanguage() (var lang;
if (navigator.language) (
lang = navigator.language;
) else if (navigator.userLanguage) (
lang = navigator.userLanguage;
}
if (lang && lang.length > 2) (
lang = lang.substring(0, 2);
}
return lang;
}
Mistake Three: The Mysterious “prop” Variable
rice. 7: In the Firebug and Dragonfly variable view panel, the global prop variable is visible
In Figure 7 you can clearly see the “prop” variable. In well-written applications, the number of global variables should be kept to a minimum, since they can cause problems when, for example, two parts of the application want to use the same variable. Let's assume that tomorrow another team will add new features to our application and also declare the "prop" variable. We'll end up with two different pieces of application code using the same name for different things. This situation often leads to conflicts and mistakes. You can try to find this variable and declare it local. To do this, you can use the search, as we did in the previous case, but there is a smarter way...
Debuggers for many other programming languages have the concept of a “watch,” which enters debugging mode when a specified variable changes. Neither Firebug nor Dragonfly support "observers" currently, but we can easily emulate similar behavior by adding next line to the beginning of the code under study:
__defineSetter__("prop" , function () ( debugger; ));Do the following:
When you reload the page, code execution will immediately stop where the "prop" variable is defined. The actual stop will occur at the point where you added the above line. One click on the “step out” button will take you to the location where the variable is set.
for (prop in attributes) (if (el.getAttribute(prop) != attributes) includeThisElement = false ;
It is easy to notice the for loop in which the prop variable is declared without keyword var, i.e. global. Fixing this is not difficult, just add var and fix the error. Error four: the “clone” attribute, which should not be there. The fourth error was apparently discovered by an advanced tester using the DOM inspector, since its existence does not appear in any way in user interface applications. If we open the DOM inspector (in Firebug this is the “HTML” tab, in Dragonfly it is called “DOM”), we will see that many elements have a clone attribute, which should not be there.
rice. 8: Dragonfly’s DOM inspector shows problematic code.
Since this does not affect the application's users in any way, this bug may not be considered serious, but do not forget that it can significantly affect performance, since the script sets the attribute on hundreds and thousands of elements.
Most quick way The solution to this problem is to set a breakpoint that fires when an attribute called clone is set on some HTML element. Can debuggers do this?
JavaScript is a very flexible language, and one of its strengths(or weak ones, depending on your point of view) is what you can replace basic functions language with your own. Add this piece of code to the page, it will override system method setAttribute, causing the code to stop when the "clone" property is set:
Element.prototype.setAttribute = function (name, value) (
if (name == "clone" ) (
debugger; /* stop the script */
}
funcSetAttr.call(this ,name,value); /* call a previously saved system method so that normal properties are set correctly */
};
So, we do the following:
When execution stops at a breakpoint, you'll want to know where the setAttribute() call occurred, meaning you'll need to go back up in the function call chain and see what's happening there. You can use a call stack for this.
rice. 9: Call stack in Dragonfly and IE8.
Figure 10 shows the stack in Firebug. In line " setAttribute