Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

JavaScript

Why won't my getElementById method work in the global environment?

Hello. The following code gives me the error "Uncaught TypeError: Cannot read property 'getContext' of null. This tells me that my getElementById method before the setUp() function is not working. It works fine when I put that line in the setUp() function and again in the HandleTick() function, but I am trying to avoid repeating myself here. I would also like to put the canvas' context in a variable in the global environment so I don't have to repeat that over and over, but first I have to get this to work. Why am I getting this error?

"use strict";
var enemyYPositions = [];   
var enemyXPositions = []; 
var avatarX = 0;
var avatarY = 0;
var avatarImage; 
var enemyImage;
var ticksSurvived = 0;
var mostTicksSurvived = 0;
var refreshIntervalId;
var gameCanvas = document.getElementById("gameCanvas"); 





function setUp() {

    avatarImage = new Image();
    enemyImage = new Image(); 
    avatarImage.src = "img/avatar.png";  
    enemyImage.src = "img/enemy.png";   

    gameCanvas.getContext('2d').drawImage(avatarImage,Math.random()*100,Math.random()*100); 

    gameCanvas.addEventListener("mousemove", handleMouseMovement);
    refreshIntervalId=window.setInterval(handleTick,25);
}


function handleMouseMovement(mouseEvent) {
      avatarX = mouseEvent.offsetX; 
      avatarY = mouseEvent.offsetY; 
}

 function handleTick() {

    var currentEnemyNumber = 0;
    var numberOfEnemies = enemyXPositions.length;

        if (Math.random()<1/20) { 
        enemyYPositions.push(0);
        enemyXPositions.push(Math.random() * 400);    
        }

        while (currentEnemyNumber<numberOfEnemies) {
            enemyYPositions[currentEnemyNumber]++;   
            currentEnemyNumber++;    
         } 

    gameCanvas.getContext('2d').clearRect(0,0,400,400); 
    gameCanvas.getContext('2d').drawImage(avatarImage,avatarX,avatarY); 

    currentEnemyNumber = 0; 
    while (currentEnemyNumber<numberOfEnemies) {
        gameCanvas.getContext('2d').drawImage(enemyImage,enemyXPositions[currentEnemyNumber],enemyYPositions[currentEnemyNumber]);   
        currentEnemyNumber++;
    }
    gameCanvas.getContext('2d').font = "20px Iceland"; 
    gameCanvas.getContext('2d').textBaseline = "top"; 
    gameCanvas.getContext('2d').fillText("Current score: "+ticksSurvived,0,5);

    currentEnemyNumber = 0;  
    while (currentEnemyNumber<numberOfEnemies)  {
        if (avatarX<(enemyXPositions[currentEnemyNumber]+29) && (avatarX+29)>enemyXPositions[currentEnemyNumber] && avatarY<(enemyYPositions[currentEnemyNumber] + 29) && (avatarY+33)>enemyYPositions[currentEnemyNumber])
        {
            if (ticksSurvived>mostTicksSurvived) { 
                mostTicksSurvived = ticksSurvived; }

            var playAgain = confirm("You died. Your score was: " + ticksSurvived + '. Your high score is: ' + mostTicksSurvived + " Would you like to play again?"); 

            if (playAgain)
                startAgain(); 
            else
                gameCanvas.getContext('2d').clearRect(0,0,400,400);
                clearInterval(refreshIntervalId);

                enemyXPositions = [];
                enemyYPositions = [];
                ticksSurvived = 0;

                gameCanvas.getContext('2d').fillText("High Score: "+mostTicksSurvived,0,5);
                gameCanvas.getContext('2d').fillText("Click to play again! ",0,45);
                gameCanvas.addEventListener("click",handleTick);


        }
            currentEnemyNumber++;  // end of checking for collision
      // increase score by 1    
    }

ticksSurvived++;
}
      // end of loop checking for collision for every enemy




function startAgain() { // clears all enemies for screen by emptying the arrays


    enemyXPositions = []; // empties array
    enemyYPositions = []; // empties other array
    ticksSurvived = 0; // sets score to zero
} 

3 Answers

Dave McFarland
STAFF
Dave McFarland
Treehouse Teacher

Ryan Scharfer

Where are you placing this JavaScript code? If the code goes before the HTML of the page, gameCanvas will be null, because the HTML hasn't loaded and the getElementByID won't find anything.

If you place this (or load via script tag) just before the closing </body> tag, you'll avoid that problem.

Thanks, Dave. You are correct. My script was in the head tag. Isn't there some reason why we should always put our scripts in the head tag, though? I realized if I added the attribute "defer" to the script, that that does the trick.

Dave McFarland
Dave McFarland
Treehouse Teacher

Adding defer to a <script> tag that loads an external JS file makes sure the HTML is parsed before the script is run like this: <script src="app.js" defer></script>

defer does not work reliably in IE 9 or earlier.

Putting JS in the <head> is common, but so is placing the JS just above the closing </body> tag. Some performance experts suggest placing all scripts before the closing </body> tag because JS in the head slows down page rendering (unless you use defer).

Thanks for the help Dave. Could you help me with this one by chance?

https://teamtreehouse.com/forum/i-thought-this-would-be-very-simple-but-i-cant-seem-to-get-js-to-write-onto-my-html-here-is-the-simple-code

The code works when I change the name of the function from "write", but "write" is not a keyword, so I don't know why I wouldn't be able to use it as a name for the function.