A detailed guide to debugging JavaScript code in Chrome Devtools. Stopping when DOM changes


Imagine that you are working on this incredible new web application and your testers have asked you to fix the following bugs:

  • The “Loading...” status bar message does not disappear when the application has finished loading.
  • The default language is Norwegian, even in English versions IE and Firefox.
  • Somewhere in the code a global variable prop was created.
  • For some reason, all elements in the DOM viewer have the “clone” attribute.
  • Running debuggers
    • 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”
    It's time to launch the debuggers. Since some instructions require code changes, you may want to save a test page and load it from disk in your browser. Mistake #1: “Loading...” message If you look at the application you are debugging, you will first see what is shown in Figure 1.


    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:

  • Click in the left margin on the line number to set a breakpoint on the first line
  • Reload the page.
  • Note that the breakpoint must be set on the line of code that will be executed when the function is run. The line that contains clearLoadingMessage() () is not suitable because it is just a function definition. If you set a breakpoint here, the debugger will not stop at it; instead, the breakpoint must be set inside the function.

    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 control point 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:

  • Find the command line:
    * In Firebug, switch to the “Console” tab.
    * In Dragonfly, look below the JavaScript code panel.
    * In IE8, find the "Console" tab on the right.
  • Paste getElements("p", ("class":"statusbar")).innerHTML into the command line.
  • Press Enter.



  • 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:

  • Gets a reference to the statusbar element.
  • Finds firstChild, in other words, the first node in this paragraph.
  • Sets the innerText property.
  • Let's try to run something more than the previous command in the console. For example, you can try to find out what the current value of the innerText property is before assigning a new value to it. To find out, you can type the entire command up to the "=" sign into the command line: getElements("p" , ("class" :"statusbar" )).firstChild.innerText

    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:

  • Click or Run button to finish the script.
  • Don't forget to reset the set checkpoint by clicking on the line number again.
  • Mistake two: language definition problem. You may have noticed the lang;/*language*/ variable at the beginning of the script. One might suspect that the code setting the value of this variable is causing the problem. You can try to find this code using the search function built into debuggers. In Dragonfly the search is located right above the code viewer, in Firebug it is on the right top corner(see Figure 5)

    To find where the localization problem is likely occurring, do the following:

  • Type lang = in the search field.
  • Set a breakpoint on the line where the lang variable is set to a value.
  • Reload the page.
  • WebInspector also has very convenient function search. It allows you to search for anything simultaneously in page markup, CSS, and JavaScript code. The results are shown in a separate panel, where you can double-click on them to go to the desired location, as shown in the screenshot.


    rice. 5: Search in Dragonfly and WebInspector.

    To check what this function does:

  • Click the "step into" button to enter the getLanguage function.
  • Press it again and again, executing the code step by step
  • In the variable viewing window, watch how their values ​​change.
  • When you enter the function, you will see an attempt to read the language from the browser user agent string by analyzing navigator.userAgent.
    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:
  • Add debugging code to the beginning of the very first script.
  • Reload the page.
  • Notice how the script execution is interrupted.
  • IE8 DT has a “Watch” tab, but there is no interruption when a variable is changed. So this example only works in Firefox, Opera and Safari.

    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's not hard to notice 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 clone attribute, which shouldn't exist.

    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:

    var funcSetAttr = Element.prototype.setAttribute; /* save a reference to the system method */
    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:
  • Add the following code to the beginning of the first script on the page.
  • Reload the page.
  • After a reboot, the script starts processing the DOM tree, but immediately stops as soon as a “bad” attribute is set. (Please note that current Firefox versions, the implementation of setAttribute is different for different elements. The code above always works as it should only in Opera; To get the same effect in Firefox, you can replace the word Element with HTMLFormElement to override the more specific HTMLFormElement.prototype.setAttribute method).

    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





    

    2024 gtavrl.ru.