Polar Code 🎭

Command Palette

Search for a command to run...

11
Pièce N°11

Module 11 -- Le DOM : La Carte de la Ville

Le DOM est la carte. Chaque élément est un bâtiment. Chaque attribut est une adresse.

11.1 Sélection d'Éléments

Méthodes de Base

// Par ID (unique)
const titre = document.getElementById('titre-principal');

// Par classe (collection live)
const cameras = document.getElementsByClassName('camera');

// Par balise
const boutons = document.getElementsByTagName('button');

Méthodes Modernes (CSS Selectors)

// Premier élément correspondant
const camera = document.querySelector('.camera.active');

// Tous les éléments correspondants
const toutesCameras = document.querySelectorAll('.camera');

// Sélections complexes
const camActives = document.querySelectorAll('.camera.active:not(.offline)');
const premierLi = document.querySelector('ul > li:first-child');

11.2 Manipulation d'Éléments

Création

// Créer un élément
const nouvelleDiv = document.createElement('div');
nouvelleDiv.className = 'camera';
nouvelleDiv.textContent = 'Camera #1';

// Créer avec dataset
nouvelleDiv.dataset.id = 'cam-001';
nouvelleDiv.dataset.status = 'active';

Ajout au DOM

const container = document.getElementById('container');

// À la fin
container.appendChild(nouvelleDiv);

// Avant un élément
const reference = document.querySelector('.reference');
container.insertBefore(nouvelleDiv, reference);

// Positions précises
reference.insertAdjacentElement('beforebegin', nouvelleDiv); // Avant
reference.insertAdjacentElement('afterend', nouvelleDiv);   // Après

Suppression

// Méthode moderne
element.remove();

// Ancienne méthode
parent.removeChild(element);

// Remplacer
parent.replaceChild(nouveau, ancien);

Clonage

const clone = element.cloneNode(true); // true = avec enfants

11.3 Modification de Contenu

Texte vs HTML

// Sécurisé (échappe le HTML)
element.textContent = '<script>alert("xss")</script>';
// Affiche: <script>alert("xss")</script>

// Dangereux (exécute le HTML)
element.innerHTML = '<strong>Nouveau</strong> contenu';

Classes

const el = document.querySelector('.camera');

el.classList.add('active', 'enregistrement');
el.classList.remove('offline');
el.classList.toggle('cache');
el.classList.contains('active'); // true/false
el.classList.replace('vieux', 'nouveau');

Attributs

// Standard
el.setAttribute('src', 'image.jpg');
const src = el.getAttribute('src');
el.removeAttribute('alt');

// Data attributes (recommandé)
el.dataset.cameraId = '123';
el.dataset.status = 'active';
console.log(el.dataset.cameraId); // '123'

Style (à éviter - utiliser des classes)

el.style.color = 'red';
el.style.backgroundColor = '#000';
el.style.display = 'none';

11.4 Parcours du DOM

Relations

const enfant = document.querySelector('.enfant');

// Parents
enfant.parentElement;
enfant.closest('.parent-selector'); // Le plus proche correspondant

// Enfants
parent.children;      // Éléments seulement
parent.childNodes;    // Tous les nœuds (texte, commentaires)
parent.firstElementChild;
parent.lastElementChild;

// Frères
enfant.previousElementSibling;
enfant.nextElementSibling;

11.5 Événements

Écouteur d'Événements

const bouton = document.querySelector('button');

function handleClick(event) {
  console.log('Clic!', event.target);
  event.preventDefault(); // Empêche comportement par défaut
}

bouton.addEventListener('click', handleClick);
bouton.removeEventListener('click', handleClick); // Même fonction requise

Délégation d'Événements (pour éléments dynamiques)

// Écouter sur un parent stable
document.addEventListener('click', (event) => {
  if (event.target.matches('.btn-dynamique')) {
    console.log('Bouton dynamique cliqué');
  }
  
  // Si le bouton a des enfants
  const bouton = event.target.closest('.btn-dynamique');
  if (bouton) {
    console.log('Bouton trouvé via closest');
  }
});

Événements Courants

// Souris
'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove'

// Clavier
'keydown', 'keyup'

// Formulaire
'submit', 'change', 'input', 'focus', 'blur'

// Document
'DOMContentLoaded', 'load', 'resize', 'scroll'

Événements Personnalisés

// Créer
const event = new CustomEvent('alerte', {
  detail: { message: 'Intrusion!' },
  bubbles: true
});

// Déclencher
element.dispatchEvent(event);

// Écouter
element.addEventListener('alerte', (e) => {
  console.log(e.detail.message);
});

11.6 Exemple Complet : Système de Surveillance

<div class="surveillance">
  <div id="cameras"></div>
  <button id="ajouter">Ajouter Caméra</button>
  <div id="logs"></div>
</div>
class SurveillanceSystem {
  constructor() {
    this.cameras = [];
    this.init();
  }
  
  init() {
    this.container = document.getElementById('cameras');
    this.btnAjouter = document.getElementById('ajouter');
    this.logs = document.getElementById('logs');
    
    this.btnAjouter.addEventListener('click', () => this.ajouterCamera());
    
    // Délégation pour les boutons dynamiques
    this.container.addEventListener('click', (e) => {
      if (e.target.classList.contains('btn-remove')) {
        this.supprimerCamera(e.target.closest('.camera'));
      }
    });
  }
  
  ajouterCamera() {
    const id = this.cameras.length + 1;
    
    // Créer l'élément
    const camera = document.createElement('div');
    camera.className = 'camera active';
    camera.dataset.id = id;
    
    // Contenu
    camera.innerHTML = `
      <h3>Camera ${id}</h3>
      <p>Status: <span class="status">active</span></p>
      <button class="btn-toggle">Toggle</button>
      <button class="btn-remove">×</button>
    `;
    
    // Ajouter au DOM
    this.container.appendChild(camera);
    this.cameras.push(camera);
    
    // Log
    this.log(`Camera ${id} ajoutée`);
  }
  
  supprimerCamera(cameraEl) {
    cameraEl.remove();
    this.log(`Camera ${cameraEl.dataset.id} supprimée`);
  }
  
  log(message) {
    const entry = document.createElement('div');
    entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
    this.logs.prepend(entry);
  }
}

// Initialisation
document.addEventListener('DOMContentLoaded', () => {
  new SurveillanceSystem();
});

Points Clés

  1. Sélection : querySelector / querySelectorAll (moderne)
  2. Création : createElement + appendChild / insertAdjacentElement
  3. Modification : textContent (sécurisé), classList (classes), dataset (data-attrs)
  4. Événements : addEventListener + délégation pour éléments dynamiques
  5. Parcours : parentElement, children, closest(), previous/nextElementSibling

Règle d'or : Pour les éléments dynamiques, utiliser la délégation d'événements.