You wrote some JavaScript to grab an element and attach a click handler, and the console throws a red error about reading a property of null. Your game will not start, or your button does nothing. This is the most common JavaScript error for anyone working with the DOM, and in the large majority of cases it comes down to a single cause: your script ran before the element existed on the page.
What “null” actually means here
When you call document.getElementById("player") and no element with that id exists at that moment, the method returns null. Then your next line tries to do something with it:
const player = document.getElementById("player"); // returns null
player.addEventListener("click", startGame); // null.addEventListener -> error
You cannot call .addEventListener on null, because null is nothing. That is the entire error. So the real question is always: why was the element not found? There are two answers, and timing is by far the more common one.
Cause 1: the script runs before the HTML loads
Browsers read an HTML page from top to bottom. If your tag sits in the , or anywhere above the element it is looking for, the script runs before that element has been created. At that instant the element genuinely does not exist, so getElementById returns null:
Fix: move the script or use defer
There are two clean fixes, and either works.
Option A: put the script at the end of the body
Move your tag to just before the closing . By the time it runs, every element above it exists:
Option B: keep the script in the head but add defer
If you prefer your script in the head, add the defer attribute. It tells the browser to wait until the HTML is fully parsed before running the script:
You can also wrap your code in a DOMContentLoaded listener, which waits for the same moment:
document.addEventListener("DOMContentLoaded", function () {
// safe: the whole page exists now
document.getElementById("startBtn").addEventListener("click", startGame);
});
Cause 2: a wrong or misspelled id
If timing is correct and you still get null, the id you are searching for does not match the id in your HTML. getElementById is case sensitive and exact. These do not match:
document.getElementById("gameboard"); // lowercase b, returns null
document.getElementById("gameBoard"); // exact match
Also check you are not confusing an id with a class. getElementById only finds ids. If your HTML uses class="gameBoard", you need querySelector(".gameBoard") instead. A quick way to confirm: open the console and type the exact getElementById call. If it returns null there too, the id is the problem, not timing.
Cause 3: the canvas null error in games
If you are building a canvas game, this error usually appears as a null when you try to get the drawing context:
Same root cause: the script grabbed the canvas before it existed, so canvas is null and canvas.getContext("2d") fails. The fix is identical. Make sure your game script runs after the canvas element, using one of the methods above:
document.addEventListener("DOMContentLoaded", function () {
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d"); // canvas exists now
// ... start the game loop ...
});
Want to study a complete, working canvas game where the script timing is already correct? Look at the source of the Melon Man game in JavaScript or the Super Mario game in HTML and JavaScript to see how the canvas and game loop are set up without this error.
A defensive habit that prevents it
Beyond fixing the timing, a good habit is to check that an element exists before using it. This way, if something is ever missing, you get a clear message instead of a cryptic null crash:
const startBtn = document.getElementById("startBtn");
if (startBtn) {
startBtn.addEventListener("click", startGame);
} else {
// tells you exactly what is missing
console.warn("Element #startBtn was not found in the HTML");
}
For larger projects this guard pattern turns a confusing crash into a readable warning that points straight at the missing element.
Frequently asked questions
Why does my code work sometimes but not others?
That points to a timing race. If the script and the element load in slightly different orders depending on cache or connection speed, you get intermittent null errors. Using defer or DOMContentLoaded removes the race entirely by guaranteeing the HTML is ready first.
What is the difference between null and undefined in this error?
A “cannot read properties of null” error means a lookup like getElementById returned null because nothing matched. An “undefined” version usually means a variable was never assigned. For DOM errors you will almost always see null, which points to a missing or not yet loaded element.
Should I use defer or put the script at the bottom?
Both achieve the same safe timing. defer is slightly cleaner because it keeps scripts in the head while still waiting for the HTML, and it preserves script order. Putting scripts at the bottom of the body is the classic approach and is perfectly fine for small projects.
My id is correct and I use defer, but it is still null. What else?
Check that the element is not being created or removed by other JavaScript, and that it is not inside an iframe or a template that has not been inserted yet. Also confirm there are not two elements competing, or a typo in the HTML id attribute itself rather than in your script.
Once your script waits for the page and your ids match exactly, this error disappears. Try the fix on any project from the JavaScript source code library and watch the console stay clean.

