Lesson Wednesday

Note: The purple "pause on exceptions" button seen at 1:40 in the video is from an older version of the console. This button now turns blue, but still functions exactly as described.

Note: The purple "pause on exceptions" button seen at 1:40 in the video is from an older version of the console. This button now turns blue, but still functions exactly as described.

As you start writing more and more complex JavaScript, you'll run into increasingly difficult bugs and problems. Here are some approaches to debugging when something goes wrong.

As an example, let's use our Madlibs page, but with some errors I've introduced for us to debug. Here's the code:

madlibs.html
<!DOCTYPE html>
<html>
  <head>
    <link href="css/bootstrap.css" rel="stylesheet" type="text/css">
    <link href="css/styles.css" rel="stylesheet" type="text/css">
    <script src="js/jquery-1.10.2.js"></script>
    <script src="js/scipts.js"></script>
    <title>A fantastical adventure</title>
  </head>
  <body>
    <div class="container">
      <h1>Fill in the blanks to write your story!</h1>
      <div id="blanks">
        <form>
          <div class="form-group">
            <label for="person1">A name</label>
            <input id="person1" class="form-control" type="text">
          </div>
          <div class="form-group">
            <label for="person2">Another name</label>
            <input id="person2" class="form-control" type="text">
          </div>
          <div class="form-group">
            <label for="animal">An animal</label>
            <input id="animal" class="form-control" type="text">
          </div>
          <div class="form-group">
            <label for="exclamation">An exclamation</label>
            <input id="exclamation" class="form-control" type="text">
          </div>
          <div class="form-group">
            <label for="verb">A past tense verb</label>
            <input id="verb" class="form-control" type="text">
          </div>
          <div class="form-group">
            <label for="noun">A noun</label>
            <input id="noun" class="form-control" type="text">
          </div>

          <button type="submit" class="btn">Show me the story!</button>
        </form>
      </div>

      <div id="story">
        <h1>A fantastical adventure</h1>
        <p>One day, <span class="person1"></span> and <span class="person2"></span> were walking through the woods, when suddenly a giant <span class="animal"></span> appeared. "<span class="exclamation"></span>", <span class="person1"></span> cried. The two of them <span class="verb"></span> as quickly possible, and when they were safe, <span class="person1"></span> and <span class="person2"></span> gave each other a giant <span class="noun"></span>.</p>
      </div>
    </div>
  </body>
</html>
scripts.js
$(document).ready(function() {
  $("#blanks form").submit(function() {
    var blanks = ["person1", "person2", "animal", "exclamation", "verb", "noun"];

    blanks.forEach(function(blank) {
      var userInput = $("input." + blank).val();
      $("." + blank).text(userInput).val();      
    });

    $("#story").sho();
  });
});

When I load up this page, fill out the form, and click the submit button, nothing happens. Time to debug!

The first step of debugging is to look for error messages. If we open up the JavaScript console, sure enough, there's an error: GET file:///Users/michael/epicodus/debug/js/scipts.js index.html:7. This error tells us that the browser tried to get the file scipts.js but couldn't find it. And hey, that's our problem - we left the r out of scripts.js. I can't tell you how many times I've seen students spend a long time debugging, just to find out they mis-spelled a filename. Let's fix that typo and reload the page.

Great, no more errors in the JavaScript console. But when we submit our form, again, we get nothing. Let's use another tactic: pausing on exceptions. If we open our JavaScript console and switch to the tab on the top that says Sources, there's a button on the top right that looks like an octagon with a pause button. If we click it twice, it turns purple. This will cause JavaScript to stop running whenever there's an error. If we submit our form, sure enough, it pauses on and highlights the offending line. I've mis-typed the method name: $("#story").sho(); should be $("#story").show();.

Let's fix this and move forward. Now, we don't have any errors, but we still aren't seeing the story. Let's try another tactic: checking to see what code is executed. I do that by adding an alert() to a couple points in my code:

scripts.js
$(document).ready(function() {
  $("#blanks form").submit(function() {
    alert('Got to beginning of form submit!');
    var blanks = ["person1", "person2", "animal", "exclamation", "verb", "noun"];

    blanks.forEach(function(blank) {
      var userInput = $("input." + blank).val();
      $("." + blank).text(userInput).val();      
    });

    $("#story").show();
    alert('Got to end of form submit!');
  });
});

Now, when I submit my form, I can see if all my code is run, or if the form breaks at some point in the middle. In this case, both dialog boxes open up. So there's nothing actually breaking or not getting run in my code. That should be a clue to step back and review the documentation for the tools I'm using to make sure I haven't left anything out. If I look back at the lesson on forms with jQuery, I can see that I forgot to include event.preventDefault(). Let's add that:

scripts.js
$(document).ready(function() {
  $("#blanks form").submit(function(event) {
    var blanks = ["person1", "person2", "animal", "exclamation", "verb", "noun"];

    blanks.forEach(function(blank) {
      var userInput = $("input." + blank).val();
      $("." + blank).text(userInput).val();      
    });

    $("#story").show();

    event.preventDefault();
  });
});

And now our paragraph with the story finally shows!

But, there's still a problem: none of what we type actually gets put into the story. Something is going wrong, but it's hard to tell what by just looking at our code. Wouldn't it be nice if we could drop in and run just one line of our code at a time, to see what's going wrong? Chrome has a handy tool called the debugger keyword to do just that. Here's how to use it:

scripts.js
$(document).ready(function() {
  $("#blanks form").submit(function(event) {
    var blanks = ["person1", "person2", "animal", "exclamation", "verb", "noun"];

    blanks.forEach(function(blank) {
      debugger;
      var userInput = $("input." + blank).val();
      $("." + blank).text(userInput).val();      
    });

    $("#story").show();

    event.preventDefault();
  });
});

Now, whenever Chrome JavaScript engine hits the debugger keyword, it will pause execution and let us run whatever code we please. This only happens when the JavaScript console window is open, so let's make sure that it is. Now, when we submit the form, we get the same kind of screen we got when we paused on the exception. Down in the bottom left, there's a little arrow and some lines. Clicking that will open up a JavaScript console below our code. Here, we can run JavaScript as if we were at the exact point where the debugger keyword is.

Let's copy and paste the next line of code: var userInput = $("input." + blank).val();. Hm, it returns undefined. I would have expected it to return the first person's name. Let's type blank so that we can see what the value of that variable is. Sure enough, it's person1. Let's just run $("input." + blank); to make sure that we're selecting the correct element. Oh, this is the problem - we're getting back an empty array. That's not what we wanted. If we look back at our HTML, we can see that our inputs have IDs, not classes, so we should be using #s, not .s in our jQuery selector. Let's try changing that in the debugger console: sure enough, $("input#" + blank); returns the proper element. If we go back to our code, remove the debugger and update our selector, our page now works.

One last helpful JavaScript debugging tool I'll tell you about is console.log(). Let's go back to before we used debugger, and try console.log() instead:

scripts.js
$(document).ready(function() {
  $("#blanks form").submit(function(event) {
    var blanks = ["person1", "person2", "animal", "exclamation", "verb", "noun"];

    blanks.forEach(function(blank) {
      var userInput = $("input." + blank).val();
      console.log(userInput);
      $("." + blank).text(userInput).val();      
    });

    $("#story").show();

    event.preventDefault();
  });
});

When we submit our form, the JavaScript console now says 6 undefined scripts.js:7. In other words, undefined was logged 6 times from line 7 of scripts.js. If we fix our code but leave the console.log() in, the proper values get logged to the console.

Generally, I use console.log() when there's a little piece of information I need to know that will help me debug, and debugger when I need to explore my code to know what's going wrong.

Just as a reminder, a useful debugging technique we explored before is changing the background color of an element to make sure you correctly selected what you were trying to, or checking in the Chrome developer tools under Event Listeners.

And those are the most important tricks of the trade for debugging.

Recommended Troubleshooting Steps


  • Check for errors in the console.

  • Read all errors carefully.

  • Break on errors.

  • Stick in an alert() or two and see if you ever trigger it. If not, you know that block of code isn't even running.

  • console.log() any relevant information.

  • Add debugger and explore your live code in the console.

  • Read any relevant documentation.

  • Add green background to make sure right thing is selected.

  • Check Event Listeners in the Chrome DevTools.