Why It Happens
// null.property → TypeError
const el = document.getElementById('missing'); // returns null
el.innerHTML = 'Hello'; // ❌ Cannot read properties of null
// undefined.property → TypeError
const user = undefined;
user.name; // ❌ Cannot read properties of undefined
Debugging
// Log before you access to see what you have
const el = document.getElementById('myBtn');
console.log(el); // null? The ID is wrong or element doesn't exist yet
// Common DOM mistake: script runs before DOM is ready
<script>
const btn = document.getElementById('btn'); // null — HTML not parsed yet
</script>
// Fix: use DOMContentLoaded or put script at end of body
document.addEventListener('DOMContentLoaded', () => {
const btn = document.getElementById('btn'); // ✅ DOM is ready
});
Fixes & Defensive Patterns
// Null check before accessing
const el = document.getElementById('myBtn');
if (el) {
el.innerHTML = 'Clicked!';
}
// Default value
const user = getUser() || {};
const name = user.name || 'Guest';
// Nullish coalescing
const name = user?.name ?? 'Guest';
Optional Chaining (?.)
// Without optional chaining — throws if user is null
const city = user.address.city;
// With optional chaining — returns undefined if any part is null/undefined
const city = user?.address?.city;
// Works on method calls too
const len = user?.getName?.().length;
// Works on array access
const first = arr?.[0]?.name;
// Combine with nullish coalescing for defaults
const city = user?.address?.city ?? 'Unknown';
💡 Introduced in ES2020 — optional chaining (?.) is supported in all modern browsers and Node.js 14+. It's the cleanest way to safely access deeply nested properties.