Skip to main content

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:
main.ts
// 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

ElementTypeScript Type
<button>HTMLButtonElement
<input>HTMLInputElement
<div>HTMLDivElement
<a>HTMLAnchorElement
<img>HTMLImageElement
<form>HTMLFormElement
<select>HTMLSelectElement
Any elementHTMLElement

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:
main.ts
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">
main.ts
const card = element.closest('.product-card') as HTMLElement;
const productId = card.dataset.productId; // "123"

Hiding/Showing Elements

main.ts
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

main.ts
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

EventTrigger
clickElement clicked
submitForm submitted
inputInput value changed
changeSelect/checkbox changed
keydownKey pressed
mouseoverMouse enters element
focusElement receives focus

Event Delegation

Instead of adding listeners to many elements, add ONE to the parent:
main.ts
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

main.ts
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:
main.ts
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()

main.ts
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

  1. Select element
  2. Check if exists (or use our helper that throws)
  3. Modify content/attributes/styles
  4. 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

Build docs developers (and LLMs) love