🎯 Lezione 10: Gestione Eventi JavaScript

Scopri come gestire eventi del browser: click, input, submit e molto altro

Cos’è un Evento?

Un evento è un’azione o un’occorrenza che accade nel browser, come un click del mouse, la pressione di un tasto, il caricamento di una pagina. JavaScript può “ascoltare” questi eventi e reagire di conseguenza.

Metodo Moderno: addEventListener

js
const bottone = document.querySelector('button');

// Sintassi: elemento.addEventListener(evento, funzione)
bottone.addEventListener('click', function(evento) {
    console.log('Bottone è stato cliccato!');
    console.log('Elemento cliccato:', evento.target);
    console.log('Coordinata X del click:', evento.clientX);
    console.log('Coordinata Y del click:', evento.clientY);
});

// Con arrow function
bottone.addEventListener('click', (evento) => {
    console.log('Click con arrow function');
});

// Con funzione predefinita
function gestisciClick(evento) {
    console.log('Click gestito da funzione separata');
}
bottone.addEventListener('click', gestisciClick);

Eventi Principali del Browser

js
// Eventi del mouse
bottone.addEventListener('click', () => console.log('Click'));
bottone.addEventListener('dblclick', () => console.log('Doppio click'));
bottone.addEventListener('mouseenter', () => console.log('Mouse entrato'));
bottone.addEventListener('mouseleave', () => console.log('Mouse uscito'));
bottone.addEventListener('mousemove', (e) => console.log(`Mouse: ${e.clientX}, ${e.clientY}`));

// Eventi della tastiera
document.addEventListener('keydown', (e) => {
    console.log('Tasto premuto:', e.key);
    console.log('Codice tasto:', e.code);
});

document.addEventListener('keyup', (e) => {
    console.log('Tasto rilasciato:', e.key);
});

// Eventi del form
const input = document.querySelector('input');
input.addEventListener('focus', () => console.log('Input in focus'));
input.addEventListener('blur', () => console.log('Input perso focus'));
input.addEventListener('input', (e) => console.log('Testo cambiato:', e.target.value));
input.addEventListener('change', (e) => console.log('Valore cambiato:', e.target.value));

// Eventi di caricamento
document.addEventListener('DOMContentLoaded', () => {
    console.log('DOM completamente caricato e pronto');
});

window.addEventListener('load', () => {
    console.log('Pagina completamente caricata (include immagini)');
});

window.addEventListener('beforeunload', (e) => {
    e.preventDefault();
    e.returnValue = ''; // Mostra avviso di conferma
});

Prevenire Comportamenti Predefiniti

js
// Prevenire l'invio di un form
const form = document.querySelector('form');
form.addEventListener('submit', function(evento) {
    evento.preventDefault(); // Impedisce il ricaricamento della pagina
    console.log('Form inviato ma pagina non ricaricata!');
    
    // Qui puoi gestire i dati del form
    const dati = new FormData(this);
    console.log('Dati form:', Object.fromEntries(dati));
});

// Prevenire il comportamento di un link
const link = document.querySelector('a');
link.addEventListener('click', function(evento) {
    evento.preventDefault(); // Impedisce la navigazione
    console.log('Link cliccato ma navigazione bloccata');
    
    // Qui puoi fare qualcos'altro invece di navigare
    alert('Navigazione bloccata!');
});

// Prevenire il menu contestuale (tasto destro)
document.addEventListener('contextmenu', function(evento) {
    evento.preventDefault();
    console.log('Menu contestuale disabilitato');
});

Propagazione degli Eventi: Bubbling e Capturing

js
// HTML: <div id="genitore"><button id="figlio">Cliccami</button></div>

const genitore = document.getElementById('genitore');
const figlio = document.getElementById('figlio');

// BUBBLING (default) - dall'interno verso l'esterno
figlio.addEventListener('click', () => console.log('Figlio cliccato'));
genitore.addEventListener('click', () => console.log('Genitore cliccato'));
// Output quando clicchi il figlio: "Figlio cliccato" → "Genitore cliccato"

// CAPTURING - dall'esterno verso l'interno
genitore.addEventListener('click', () => console.log('Genitore (capturing)'), true);
figlio.addEventListener('click', () => console.log('Figlio (capturing)'), true);
// Output: "Genitore (capturing)" → "Figlio (capturing)"

// Fermare la propagazione
figlio.addEventListener('click', function(evento) {
    evento.stopPropagation(); // Ferma la propagazione
    console.log('Click sul figlio - propagazione fermata');
});

Delegazione degli Eventi

js
// Invece di aggiungere event listener a ogni elemento,
// aggiungine uno al genitore e usa event.target

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

// DELEGAZIONE CORRETTA
lista.addEventListener('click', function(evento) {
    // Controlla se il click è su un elemento LI
    if (evento.target.tagName === 'LI') {
        evento.target.classList.toggle('selezionato');
        console.log('Elemento selezionato:', evento.target.textContent);
    }
    
    // Controlla se il click è su un bottone dentro LI
    if (evento.target.tagName === 'BUTTON') {
        const item = evento.target.parentElement;
        item.remove();
        console.log('Elemento rimosso');
    }
});

// Questo funziona anche per elementi aggiunti dinamicamente!
function aggiungiElemento() {
    const nuovoLi = document.createElement('li');
    nuovoLi.innerHTML = `Nuovo elemento <button>Rimuovi</button>`;
    lista.appendChild(nuovoLi);
    // Non serve aggiungere event listener - la delegazione funziona già!
}

🎯 Esercizio Pratico: Todo List Interattiva

Obiettivo: Crea una todo list completa con tutte le funzionalità

html + js
<!-- HTML -->
<div id="todoApp">
    <h2>📝 La Mia Todo List</h2>
    <div class="input-group">
        <input type="text" id="todoInput" placeholder="Cosa devi fare?">
        <button id="aggiungiBtn">Aggiungi</button>
    </div>
    <ul id="todoList"></ul>
    <div class="controlli">
        <button id="cancellaCompletati">Cancella Completati</button>
        <button id="cancellaTutti">Cancella Tutti</button>
        <span id="contatore">0 task</span>
    </div>
</div>

<script>
class TodoApp {
    constructor() {
        this.todos = JSON.parse(localStorage.getItem('todos')) || [];
        this.init();
    }
    
    init() {
        this.bindEvents();
        this.render();
        this.aggiornaContatore();
    }
    
    bindEvents() {
        // Aggiungi todo con click
        document.getElementById('aggiungiBtn').addEventListener('click', () => this.aggiungiTodo());
        
        // Aggiungi todo con Enter
        document.getElementById('todoInput').addEventListener('keypress', (e) => {
            if (e.key === 'Enter') this.aggiungiTodo();
        });
        
        // Delegazione eventi per la lista
        document.getElementById('todoList').addEventListener('click', (e) => {
            if (e.target.classList.contains('completa')) {
                this.toggleCompleta(parseInt(e.target.dataset.id));
            } else if (e.target.classList.contains('elimina')) {
                this.eliminaTodo(parseInt(e.target.dataset.id));
            }
        });
        
        // Pulsanti controllo
        document.getElementById('cancellaCompletati').addEventListener('click', () => this.cancellaCompletati());
        document.getElementById('cancellaTutti').addEventListener('click', () => this.cancellaTutti());
    }
    
    aggiungiTodo() {
        const input = document.getElementById('todoInput');
        const testo = input.value.trim();
        
        if (testo) {
            this.todos.push({
                id: Date.now(),
                testo: testo,
                completato: false
            });
            input.value = '';
            this.salva();
            this.render();
            this.aggiornaContatore();
        }
    }
    
    toggleCompleta(id) {
        this.todos = this.todos.map(todo => 
            todo.id === id ? {...todo, completato: !todo.completato} : todo
        );
        this.salva();
        this.render();
        this.aggiornaContatore();
    }
    
    eliminaTodo(id) {
        this.todos = this.todos.filter(todo => todo.id !== id);
        this.salva();
        this.render();
        this.aggiornaContatore();
    }
    
    cancellaCompletati() {
        this.todos = this.todos.filter(todo => !todo.completato);
        this.salva();
        this.render();
        this.aggiornaContatore();
    }
    
    cancellaTutti() {
        if (confirm('Sei sicuro di voler cancellare tutti i task?')) {
            this.todos = [];
            this.salva();
            this.render();
            this.aggiornaContatore();
        }
    }
    
    render() {
        const lista = document.getElementById('todoList');
        lista.innerHTML = this.todos.map(todo => `
            <li class="${todo.completato ? 'completato' : ''}">
                <span>${todo.testo}</span>
                <div>
                    <button class="completa" data-id="${todo.id}">
                        ${todo.completato ? '❌' : '✅'}
                    </button>
                    <button class="elimina" data-id="${todo.id}">🗑️</button>
                </div>
            </li>
        `).join('');
    }
    
    aggiornaContatore() {
        const totale = this.todos.length;
        const completati = this.todos.filter(todo => todo.completato).length;
        document.getElementById('contatore').textContent = 
            `${completati}/${totale} completati`;
    }
    
    salva() {
        localStorage.setItem('todos', JSON.stringify(this.todos));
    }
}

// Inizializza l'app quando il DOM è pronto
document.addEventListener('DOMContentLoaded', () => {
    new TodoApp();
});
</script>

<style>
.completato span { text-decoration: line-through; opacity: 0.6; }
.input-group { margin: 20px 0; display: flex; gap: 10px; }
.controlli { margin-top: 20px; display: flex; gap: 10px; align-items: center; }
#todoList { list-style: none; padding: 0; }
#todoList li { 
    display: flex; justify-content: space-between; 
    padding: 10px; margin: 5px 0; background: #f5f5f5; 
    border-radius: 5px; 
}
</style>

Sfida avanzata: Aggiungi queste funzionalità:

  1. Modifica dei task esistenti (doppio click per editare)
  2. Filtri per vedere solo i task completati/non completati
  3. Drag & drop per riordinare i task
  4. Scadenze e promemoria per i task

Torna in alto