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.