Documentation Index
Fetch the complete documentation index at: https://mintlify.com/diegoromemora27-creator/HTMLCSSEXPLAIN/llms.txt
Use this file to discover all available pages before exploring further.
What is the DOM?
The DOM (Document Object Model) is the browser’s representation of your HTML as JavaScript objects. Every HTML tag is a “node” that you can manipulate: change text, styles, add events, etc.
<div id="app">
<h1>Hello</h1>
<button id="btn">Click me</button>
</div>
Becomes:
Document
└─ div#app
├─ h1 ("Hello")
└─ button#btn ("Click me")
Selecting Elements
querySelector
Finds one element using CSS selectors:
// By ID
const button = document.querySelector("#my-button");
// By class
const card = document.querySelector(".product-card");
// By tag
const heading = document.querySelector("h1");
// Complex selectors
const firstButton = document.querySelector(".card button");
getElementById
Finds an element by ID (faster than querySelector for IDs):
const button = document.getElementById("my-button");
querySelectorAll
Finds all matching elements:
const allButtons = document.querySelectorAll("button");
// Returns a NodeList (array-like)
The Type Problem
querySelector returns a very generic type:
const element = document.querySelector("#my-input");
// Type: Element | null
// Problem: No access to .value, .checked, etc.
Solution: Generic Helper Function
From our project, we created a reusable helper:
// T extends HTMLElement → T must be HTMLElement or more specific
function getElement<T extends HTMLElement>(selector: string): T {
// document.querySelector<T>(selector) → Search and type as T
const element = document.querySelector<T>(selector);
// If element not found, throw error
// This prevents code from continuing with null
if (!element) {
throw new Error(`Element not found: ${selector}`);
}
// Return the element already typed as T
return element;
}
Using the Generic Helper
// Get a button - has .disabled, .click(), etc.
const btn = getElement<HTMLButtonElement>("#my-btn");
btn.disabled = true; // ✅ TypeScript knows this exists
// Get an input - has .value, .checked, etc.
const input = getElement<HTMLInputElement>("#my-input");
const value = input.value; // ✅ TypeScript knows this exists
// Get a div - general container element
const grid = getElement<HTMLDivElement>("#products-grid");
grid.innerHTML = "<p>Hello</p>"; // ✅ Works
Common HTML Element Types
| Element | TypeScript Type |
|---|
<button> | HTMLButtonElement |
<input> | HTMLInputElement |
<div> | HTMLDivElement |
<a> | HTMLAnchorElement |
<img> | HTMLImageElement |
<form> | HTMLFormElement |
<select> | HTMLSelectElement |
| Any element | HTMLElement |
Generics Explained: <T>
function getElement<T extends HTMLElement>(selector: string): T
──────────────────────────────────────────────────────────────
│ │ │
│ │ │
<T extends HTMLElement> │ │
"T is a type that MUST be │ │
HTMLElement or something │ │
more specific" │ │
(selector: string) │
"Receives a CSS │
selector string" │
: T │
"Returns │
type T" │
Changing Element Content
textContent
Set plain text (safer - escapes HTML):
const heading = getElement<HTMLHeadingElement>("h1");
heading.textContent = "New Title";
// Safe - won't execute scripts
heading.textContent = "<script>alert('xss')</script>";
// Shows literally: <script>alert('xss')</script>
innerHTML
Set HTML content:
const grid = getElement<HTMLDivElement>("#products-grid");
grid.innerHTML = `
<article class="card">
<h2>Product</h2>
</article>
`;
Warning: Only use innerHTML with trusted content! Never with user input.
Changing Element Attributes
Direct Property Access
const button = getElement<HTMLButtonElement>("#btn");
button.disabled = true;
button.textContent = "Loading...";
const link = getElement<HTMLAnchorElement>("#link");
link.href = "https://example.com";
setAttribute
const element = getElement<HTMLElement>("#my-element");
element.setAttribute("data-count", "5");
Data Attributes (dataset)
<article data-product-id="123">
const card = element.closest('.product-card') as HTMLElement;
const productId = card.dataset.productId; // "123"
Hiding/Showing Elements
const loading = getElement<HTMLDivElement>("#products-loading");
const error = getElement<HTMLDivElement>("#products-error");
// Hide elements
loading.hidden = true;
error.hidden = true;
// Show loading
loading.hidden = false;
Event Listeners
Basic Click Event
function setupEventListeners(): void {
const loadBtn = getElement<HTMLButtonElement>("#load-products-btn");
// When clicked, run loadProducts
loadBtn.addEventListener('click', loadProducts);
}
Event with Type
button.addEventListener('click', (event: MouseEvent) => {
console.log('Clicked at:', event.clientX, event.clientY);
});
Common Events
| Event | Trigger |
|---|
click | Element clicked |
submit | Form submitted |
input | Input value changed |
change | Select/checkbox changed |
keydown | Key pressed |
mouseover | Mouse enters element |
focus | Element receives focus |
Event Delegation
Instead of adding listeners to many elements, add ONE to the parent:
function setupAddToCartButtons(): void {
// Get parent container
const grid = getElement<HTMLDivElement>("#products-grid");
// ONE listener on the container
grid.addEventListener('click', (event: MouseEvent) => {
// event.target is the exact element clicked
const target = event.target as HTMLElement;
// .closest() searches up the DOM tree
// Finds element with data-action="add-to-cart"
const button = target.closest('[data-action="add-to-cart"]');
// If we didn't find the button, click was elsewhere; exit
if (!button) return;
// Find parent product card to get ID
const card = button.closest('.product-card') as HTMLElement;
if (!card) return;
// dataset.productId accesses data-product-id from HTML
const productId = card.dataset.productId;
if (productId) {
addToCart(parseInt(productId, 10));
}
});
}
Why Event Delegation?
<div id="grid"> ← ONE listener here
<article>
<button>Add</button> ← Click here
</article>
<article>
<button>Add</button> ← Or here
</article>
<!-- 100 more products... -->
</div>
Advantages:
- One listener instead of 100
- Works with dynamically added elements
- Better performance
Creating Elements Dynamically
function showNotification(message: string, duration: number = 3000): void {
// Create new div
const notification = document.createElement('div');
// Apply styles
notification.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
padding: 16px 24px;
background-color: #333;
color: white;
border-radius: 8px;
`;
// Set content
notification.textContent = message;
// Add to page
document.body.appendChild(notification);
// Remove after duration
setTimeout(() => {
notification.remove();
}, duration);
}
Template Literals for HTML
Create HTML strings with variables:
function createProductCardHTML(product: Product): string {
const formattedPrice = product.price.toLocaleString('es-MX', {
style: 'currency',
currency: 'MXN'
});
const imageUrl = product.images[0] || 'https://placehold.co/400x300?text=No+image';
const cleanImageUrl = imageUrl.replace(/["\[\]]/g, '');
return `
<article class="product-card" data-product-id="${product.id}">
<figure class="product-card__figure">
<img
src="${cleanImageUrl}"
alt="${product.title}"
class="product-card__image"
loading="lazy"
/>
</figure>
<div class="product-card__content">
<span class="product-card__category">${product.category.name}</span>
<h3 class="product-card__title">${product.title}</h3>
<p class="product-card__price">
${formattedPrice}
</p>
<button type="button" class="product-card__btn" data-action="add-to-cart">
Add to Cart
</button>
</div>
</article>
`;
}
Rendering Arrays with map() and join()
function renderProducts(): void {
const grid = getElement<HTMLDivElement>("#products-grid");
if (appState.products.length === 0) {
grid.innerHTML = `<p class="products__empty-state">No products found</p>`;
return;
}
// 1. .map() → Convert each Product to HTML string
// Result: ["<article>...</article>", "<article>...</article>", ...]
// 2. .join('') → Join all strings into one (no separator)
// Result: "<article>...</article><article>...</article>..."
grid.innerHTML = appState.products
.map(product => createProductCardHTML(product))
.join('');
// After inserting HTML, set up event listeners
setupAddToCartButtons();
}
DOM Manipulation Flow
- Select element
- Check if exists (or use our helper that throws)
- Modify content/attributes/styles
- Listen to events if needed
// 1. Select
const button = getElement<HTMLButtonElement>("#btn");
// 2. Modify
button.textContent = "Click me!";
button.disabled = false;
// 3. Listen
button.addEventListener('click', () => {
console.log('Clicked!');
});
Next Steps