Whats wrong with my function

I don't know why this is not working. The function call at the bottom of displayWorld(); if you commit that line out and then console.log in the console it works. But if I call the function in the code it breaks.

sorry here error

Uncaught TypeError: Cannot set property 'innerHTML' of null at displayWorld (index.html:40) at index.html:43

The HTML

<!DOCTYPE html>
<html>
<head>
    <title>Pacman Demo</title>
    <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="style.css"></style>
  <script type="text/javascript" src='http://code.jquery.com/jquery-1.10.2.min.js'></script> 
  <script type="text/javascript">

  var world = [
              [2,2,2,2,2,2,2,2,2,2],
              [2,1,1,2,1,1,1,1,1,2],
              [2,1,1,2,1,2,2,2,1,2],
              [2,1,1,1,1,1,2,1,1,2],
              [2,1,1,1,1,1,2,1,1,2],
              [2,1,1,1,1,1,2,1,1,2],
              [2,1,1,1,1,1,2,1,1,2],
              [2,1,1,1,1,1,1,1,1,2],
              [2,2,2,2,2,2,2,2,2,2],

            ];


            function displayWorld(){
                var output = '';

                for(var i=0; i<world.length; i++){
                    output += "\n<div class='row'>";
                    for(var j=0; j<world[i].length; j++){
                        if(world[i][j] == 2)
                            output += "\n\t<div class='brick'></div>";
                        else if(world[i][j] == 1)
                            output += "\n\t<div class='coin'></div>";
                        if(world[i][j] == 0)
                            output += "<\n\tdiv class='empty'></div>";
                    }
                    output += "\n</div>";
                }
                console.log(output);
                document.getElementById('world').innerHTML = output;
            }

     displayWorld();      <----- this piece here
  </script>


    </style>
</head>
<body>

    <div id='world'></div>


</body>
</html>

The CSS

/*CSS reset settings here*/
*{
 margin: 0px;
 padding: 0px;
}

body{
    background-color: black ;
}
div.row div{
    width: 20px;
    height: 20px;
    display: inline-block;
}

div.brick {
    background-color: blue;

}

div.coin{
    background: url(coin.gif);
    background-repeat: no-repeat;
    background-position: center;

}

div.packman{
    background: url(mrpacman.gif);
    background-repeat: no-repeat;
    background-position: center;

}

div.empty{


}

Answers 1

  • To fully understand why this happens you should understand how HTML + CSS + JS model works. I described the full page loading and execution logic as it might help you understand similar issues. If you're only interested in the current error, skip to point 3.

    1. No JavaScript is executed before CSSOM is built (to prevent FOUC).
    2. CSSOM is built after <head> is read and parsed. This is why you need to place all your styleseets and <style> tags in <head>. If you place them in <body>, FOUC becomes possible on the contents preceeding the <style> in body, because it is first rendered without those rules.
    3. <script> tags are read and executed in order of appearance as soon as they are met. The ones already loaded from <head> are executed before the <body> tag is added to DOM, in the order they were encountered. The <script> tags inside <body> are executed when they are met, as DOM is being built, but before the rest of the page (following content) has been added to DOM.

    This means your <script> tag is executed before <div id="world"></div> has been created in DOM and your script, trying to add content to it with this line...

    document.getElementById('world').innerHTML = output;
    

    ... results in an error, as document.getElementById('world') returns null at that point and null doesn't have an innerHTML property.

    To fix it, you should place your script in <body>, after the #world div:

    *{
     margin: 0px;
     padding: 0px;
    }
    
    body{
        background-color: black ;
    }
    div.row div{
        width: 20px;
        height: 20px;
        display: inline-block;
    }
    
    div.brick {
        background-color: blue;
    
    }
    
    div.coin{
        background: url(coin.gif);
        background-repeat: no-repeat;
        background-position: center;
    
    }
    
    div.packman{
        background: url(mrpacman.gif);
        background-repeat: no-repeat;
        background-position: center;
    
    }
    
    div.empty{
    
    }
    <div id="world"></div>
    <script type="text/javascript">
      var world = [
        [2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
        [2, 1, 1, 2, 1, 1, 1, 1, 1, 2],
        [2, 1, 1, 2, 1, 2, 2, 2, 1, 2],
        [2, 1, 1, 1, 1, 1, 2, 1, 1, 2],
        [2, 1, 1, 1, 1, 1, 2, 1, 1, 2],
        [2, 1, 1, 1, 1, 1, 2, 1, 1, 2],
        [2, 1, 1, 1, 1, 1, 2, 1, 1, 2],
        [2, 1, 1, 1, 1, 1, 1, 1, 1, 2],
        [2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
    
    ];
    
    function displayWorld() {
        var output = '';
    
        for (var i = 0; i < world.length; i++) {
            output += "\n<div class='row'>";
            for (var j = 0; j < world[i].length; j++) {
                if (world[i][j] == 2)
                    output += "\n\t<div class='brick'></div>";
                else if (world[i][j] == 1)
                    output += "\n\t<div class='coin'></div>";
                if (world[i][j] == 0)
                    output += "<\n\tdiv class='empty'></div>";
            }
            output += "\n</div>";
        }
        document.getElementById('world').innerHTML = output;
    }
    
    displayWorld();
      </script>

    As an alternative, you could delay the execution of your script by wrapping it inside

    window.onload = function() {
      // your script here...
    }
    

    This will delay the execution of your script until load event is fired for window, which happens as soon as </html> is encountered and DOM is complete.

    Note: your script does not include any jQuery code and, since your example doesn't need it, I removed it.


Related Articles