Eventi JavaScript

Impara a gestire click, input, submit, scroll e tutti gli eventi del browser

🎪 Cos’è un Evento?

Gli eventi sono azioni che accadono nel browser: click, digitazione,
movimento mouse, caricamento pagina, ecc. JavaScript può “ascoltare” questi eventi
e reagire di conseguenza!

💡 Come Funzionano gli Eventi

Pensa agli eventi come a dei “segnali” che il browser invia quando succede qualcosa:

  • 🖱️ L’utente clicca un bottone → evento click
  • ⌨️ L’utente digita in un input → evento input
  • 📜 L’utente scrolla la pagina → evento scroll
  • 📄 La pagina finisce di caricare → evento load

JavaScript può “ascoltare” questi segnali e eseguire codice in risposta!

🎯 In Questa Lezione

  • addEventListener() – Il modo moderno di gestire eventi
  • Event Object – Informazioni sull’evento
  • Tipi di Eventi – Mouse, tastiera, form, window…
  • Event Delegation – Pattern efficiente
  • preventDefault() – Bloccare comportamento default
  • Best Practices – Gestione eventi professionale

👂 addEventListener()

addEventListener() è il metodo moderno (e consigliato)
per “ascoltare” eventi su elementi HTML.

1
Sintassi Base

HTML
<button id="btn">Cliccami!</button>
<p id="output"></p>

addEventListener() Base
// Sintassi: elemento.addEventListener('evento', funzione)

const btn = document.querySelector('#btn');
const output = document.querySelector('#output');

// Metodo 1: Funzione normale
btn.addEventListener('click', function() {
    output.textContent = "Bottone cliccato!";
});

// Metodo 2: Arrow function (CONSIGLIATO 🌟)
btn.addEventListener('click', () => {
    output.textContent = "Bottone cliccato!";
});

// Metodo 3: Funzione esterna (riutilizzabile)
function gestisciClick() {
    output.textContent = "Bottone cliccato!";
}

btn.addEventListener('click', gestisciClick);

// ✅ Puoi aggiungere più listener allo stesso evento!
btn.addEventListener('click', () => {
    console.log("Primo listener");
});

btn.addEventListener('click', () => {
    console.log("Secondo listener");
});
// Entrambi vengono eseguiti!

2
Rimuovere Event Listener

Puoi “smettere di ascoltare” un evento con removeEventListener().

removeEventListener()
const btn = document.querySelector('#btn');

// Definisci funzione PRIMA (serve riferimento!)
function gestisciClick() {
    console.log("Cliccato!");
}

// Aggiungi listener
btn.addEventListener('click', gestisciClick);

// Rimuovi listener
btn.removeEventListener('click', gestisciClick);

// ⚠️ Questo NON funziona (funzioni anonime diverse)
btn.addEventListener('click', () => console.log("A"));
btn.removeEventListener('click', () => console.log("A"));
// Non rimuove nulla! Sono due funzioni diverse

// ✅ Usa riferimento alla funzione
const handler = () => console.log("A");
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler);  // Funziona!

// Esempio: esegui una sola volta
function eseguiUnaVolta() {
    console.log("Eseguito una volta!");
    btn.removeEventListener('click', eseguiUnaVolta);
}

btn.addEventListener('click', eseguiUnaVolta);

// Oppure usa opzione 'once' (moderno)
btn.addEventListener('click', () => {
    console.log("Una volta!");
}, { once: true });

❌ addEventListener vs onclick

const btn = document.querySelector('#btn');

// ❌ VECCHIO MODO - onclick (evita!)
btn.onclick = function() {
    console.log("Click 1");
};

btn.onclick = function() {
    console.log("Click 2");
};
// Solo "Click 2" viene eseguito (sovrascrive!)

// ✅ NUOVO MODO - addEventListener
btn.addEventListener('click', () => {
    console.log("Click 1");
});

btn.addEventListener('click', () => {
    console.log("Click 2");
});
// Entrambi vengono eseguiti!

// Vantaggi addEventListener:
// ✅ Più listener sullo stesso evento
// ✅ Può essere rimosso
// ✅ Opzioni avanzate (once, capture, passive)
// ✅ Standard moderno

📦 Event Object

Quando un evento viene scatenato, JavaScript passa automaticamente un
oggetto evento alla funzione handler con informazioni utili!

1
Accedere all’Event Object

Event Object Base
const btn = document.querySelector('#btn');

// Parametro 'event' (o 'e', 'evt')
btn.addEventListener('click', function(event) {
    console.log(event);  // Oggetto MouseEvent
});

// Arrow function
btn.addEventListener('click', (e) => {
    console.log(e);
});

// Proprietà comuni dell'event object
btn.addEventListener('click', (e) => {
    console.log(e.type);        // "click"
    console.log(e.target);      // Elemento cliccato
    console.log(e.currentTarget); // Elemento con listener (btn)
    console.log(e.timeStamp);   // Timestamp evento
    console.log(e.isTrusted);   // true se evento utente
});

2
target vs currentTarget

Differenza importante quando hai elementi annidati!

HTML
<div id="container">
    <button id="btn">
        <span>Clicca</span>
    </button>
</div>

target vs currentTarget
const container = document.querySelector('#container');

container.addEventListener('click', (e) => {
    console.log('target:', e.target);
    // Elemento EFFETTIVAMENTE cliccato (span, button, o div)
    
    console.log('currentTarget:', e.currentTarget);
    // Elemento con addEventListener (sempre div#container)
});

// Esempio pratico: sempre usa currentTarget per elemento con listener
const btn = document.querySelector('#btn');

btn.addEventListener('click', (e) => {
    // ❌ Può essere button o span (dipende dove clicchi)
    e.target.classList.add('active');
    
    // ✅ Sempre il button
    e.currentTarget.classList.add('active');
    
    // Oppure usa 'this' (non con arrow function!)
    // this.classList.add('active');
});

3
Proprietà Specifiche per Tipo Evento

Eventi Mouse
elemento.addEventListener('click', (e) => {
    // Posizione mouse
    console.log(e.clientX, e.clientY);  // Coordinate viewport
    console.log(e.pageX, e.pageY);      // Coordinate pagina
    console.log(e.screenX, e.screenY);  // Coordinate schermo
    
    // Bottoni mouse
    console.log(e.button);  // 0=sinistro, 1=centrale, 2=destro
    
    // Tasti modificatori
    console.log(e.ctrlKey);   // Ctrl premuto?
    console.log(e.shiftKey);  // Shift premuto?
    console.log(e.altKey);    // Alt premuto?
    console.log(e.metaKey);   // Cmd/Win premuto?
});

Eventi Tastiera
input.addEventListener('keydown', (e) => {
    console.log(e.key);       // Tasto premuto ("a", "Enter", "Escape")
    console.log(e.code);      // Codice fisico ("KeyA", "Enter")
    console.log(e.keyCode);   // Codice numerico (deprecato)
    
    // Tasti modificatori
    console.log(e.ctrlKey, e.shiftKey, e.altKey);
    
    // Esempio: Enter per submit
    if (e.key === 'Enter') {
        console.log("Enter premuto!");
    }
    
    // Esempio: Ctrl+S per salvare
    if (e.ctrlKey && e.key === 's') {
        e.preventDefault();  // Blocca salvataggio browser
        console.log("Salvataggio personalizzato!");
    }
});

🎨 Tipi di Eventi

Ci sono molti tipi di eventi diversi! Vediamo i più comuni e utili.

1
Eventi Mouse

Mouse Events
const elemento = document.querySelector('#elemento');

// click - Click completo (mousedown + mouseup)
elemento.addEventListener('click', (e) => {
    console.log("Cliccato!");
});

// dblclick - Doppio click
elemento.addEventListener('dblclick', (e) => {
    console.log("Doppio click!");
});

// contextmenu - Click destro
elemento.addEventListener('contextmenu', (e) => {
    e.preventDefault();  // Blocca menu contestuale
    console.log("Click destro!");
});

// mousedown - Bottone premuto
// mouseup - Bottone rilasciato
elemento.addEventListener('mousedown', (e) => {
    console.log("Mouse premuto");
});

// mouseenter - Mouse entra (non bubbling)
// mouseleave - Mouse esce (non bubbling)
elemento.addEventListener('mouseenter', (e) => {
    elemento.style.background = 'lightblue';
});

elemento.addEventListener('mouseleave', (e) => {
    elemento.style.background = '';
});

// mouseover - Mouse entra (con bubbling)
// mouseout - Mouse esce (con bubbling)

// mousemove - Mouse si muove
elemento.addEventListener('mousemove', (e) => {
    console.log(`X: ${e.clientX}, Y: ${e.clientY}`);
});

2
Eventi Tastiera

Keyboard Events
const input = document.querySelector('#input');

// keydown - Tasto premuto (ripete se tenuto)
input.addEventListener('keydown', (e) => {
    console.log(`Premuto: ${e.key}`);
});

// keyup - Tasto rilasciato
input.addEventListener('keyup', (e) => {
    console.log(`Rilasciato: ${e.key}`);
});

// keypress - Tasto carattere (DEPRECATO, usa keydown)

// Esempi pratici
input.addEventListener('keydown', (e) => {
    // Escape per cancellare
    if (e.key === 'Escape') {
        input.value = '';
    }
    
    // Enter per submit
    if (e.key === 'Enter') {
        console.log("Submit:", input.value);
    }
    
    // Blocca numeri
    if (e.key >= '0' && e.key <= '9') {
        e.preventDefault();
        console.log("Numeri non ammessi!");
    }
    
    // Shortcuts
    if (e.ctrlKey && e.key === 's') {
        e.preventDefault();
        console.log("Salva");
    }
});

3
Eventi Form

Form Events
const form = document.querySelector('#form');
const input = document.querySelector('#input');
const select = document.querySelector('#select');

// submit - Form inviato
form.addEventListener('submit', (e) => {
    e.preventDefault();  // IMPORTANTE: blocca invio default!
    
    const formData = new FormData(form);
    console.log("Form inviato:", Object.fromEntries(formData));
});

// input - Valore cambia (ogni carattere)
input.addEventListener('input', (e) => {
    console.log("Valore corrente:", e.target.value);
});

// change - Valore cambia e perde focus
input.addEventListener('change', (e) => {
    console.log("Valore finale:", e.target.value);
});

// focus - Elemento riceve focus
input.addEventListener('focus', (e) => {
    console.log("Input in focus");
    input.style.borderColor = 'blue';
});

// blur - Elemento perde focus
input.addEventListener('blur', (e) => {
    console.log("Input perde focus");
    input.style.borderColor = '';
});

// select - Testo selezionato
input.addEventListener('select', (e) => {
    console.log("Testo selezionato");
});

// Esempio validazione real-time
input.addEventListener('input', (e) => {
    const value = e.target.value;
    
    if (value.length < 3) {
        input.style.borderColor = 'red';
    } else {
        input.style.borderColor = 'green';
    }
});

4
Eventi Window e Document

Window/Document Events
// load - Pagina caricata (immagini, CSS, tutto)
window.addEventListener('load', () => {
    console.log("Pagina completamente caricata!");
});

// DOMContentLoaded - DOM caricato (senza aspettare immagini)
document.addEventListener('DOMContentLoaded', () => {
    console.log("DOM pronto!");
    // ✅ Momento migliore per inizializzare JS
});

// resize - Finestra ridimensionata
window.addEventListener('resize', () => {
    console.log(`Dimensioni: ${window.innerWidth}x${window.innerHeight}`);
});

// scroll - Pagina scrollata
window.addEventListener('scroll', () => {
    console.log(`Scroll: ${window.scrollY}px`);
});

// beforeunload - Prima di lasciare pagina
window.addEventListener('beforeunload', (e) => {
    e.preventDefault();
    e.returnValue = '';  // Mostra conferma
    return '';
});

// Esempio: Sticky header on scroll
const header = document.querySelector('header');

window.addEventListener('scroll', () => {
    if (window.scrollY > 100) {
        header.classList.add('sticky');
    } else {
        header.classList.remove('sticky');
    }
});

// Esempio: Responsive behavior
window.addEventListener('resize', () => {
    if (window.innerWidth < 768) {
        console.log("Mobile");
    } else {
        console.log("Desktop");
    }
});

🎯 Event Delegation

Un pattern potente per gestire eventi su molti elementi (anche futuri!)
con un solo listener sul parent.

1
Problema e Soluzione

HTML
<ul id="lista">
    <li>Item 1 <button class="delete">X</button></li>
    <li>Item 2 <button class="delete">X</button></li>
    <li>Item 3 <button class="delete">X</button></li>
</ul>
<button id="add">Aggiungi Item</button>

Senza Event Delegation (❌)
// ❌ PROBLEMA: Listener su ogni bottone
const deleteButtons = document.querySelectorAll('.delete');

deleteButtons.forEach(btn => {
    btn.addEventListener('click', (e) => {
        e.target.closest('li').remove();
    });
});

// Problemi:
// 1. Performance: molti listener
// 2. Nuovi elementi NON hanno listener
document.querySelector('#add').addEventListener('click', () => {
    const li = document.createElement('li');
    li.innerHTML = 'Nuovo <button class="delete">X</button>';
    document.querySelector('#lista').append(li);
    // Questo bottone NON funziona! 😱
});

Con Event Delegation (✅)
// ✅ SOLUZIONE: Un listener sul parent
const lista = document.querySelector('#lista');

lista.addEventListener('click', (e) => {
    // Verifica se cliccato è bottone delete
    if (e.target.classList.contains('delete')) {
        e.target.closest('li').remove();
    }
});

// Oppure con matches()
lista.addEventListener('click', (e) => {
    if (e.target.matches('.delete')) {
        e.target.closest('li').remove();
    }
});

// Ora anche elementi futuri funzionano! ✅
document.querySelector('#add').addEventListener('click', () => {
    const li = document.createElement('li');
    li.innerHTML = 'Nuovo <button class="delete">X</button>';
    lista.append(li);
    // Funziona automaticamente! 🎉
});

// Vantaggi:
// ✅ Un solo listener (performance)
// ✅ Funziona con elementi futuri
// ✅ Meno memoria utilizzata

2
Pattern Avanzato con closest()

Event Delegation Avanzato
const container = document.querySelector('#container');

container.addEventListener('click', (e) => {
    // Trova elemento con data-action
    const action = e.target.closest('[data-action]');
    
    if (!action) return;  // Nessuna action trovata
    
    const actionType = action.dataset.action;
    
    switch(actionType) {
        case 'delete':
            action.closest('.item').remove();
            break;
        
        case 'edit':
            const item = action.closest('.item');
            console.log("Modifica:", item.dataset.id);
            break;
        
        case 'like':
            action.classList.toggle('liked');
            break;
    }
});

// HTML
// <div class="item" data-id="1">
//     <button data-action="edit">Modifica</button>
//     <button data-action="delete">Elimina</button>
//     <button data-action="like">Like</button>
// </div>

🚫 preventDefault() e stopPropagation()

Metodi per controllare il comportamento degli eventi.

1
preventDefault() - Blocca Comportamento Default

preventDefault() Esempi
// Form submit
const form = document.querySelector('#form');

form.addEventListener('submit', (e) => {
    e.preventDefault();  // Blocca invio e reload pagina!
    
    // Gestione custom
    console.log("Form gestito via JavaScript");
});

// Link
const link = document.querySelector('a');

link.addEventListener('click', (e) => {
    e.preventDefault();  // Blocca navigazione
    
    console.log("Link cliccato ma non seguito");
});

// Click destro
document.addEventListener('contextmenu', (e) => {
    e.preventDefault();  // Blocca menu contestuale
    
    // Mostra menu custom
    console.log("Menu custom qui");
});

// Checkbox
const checkbox = document.querySelector('#check');

checkbox.addEventListener('click', (e) => {
    if (someCondition) {
        e.preventDefault();  // Blocca check/uncheck
        console.log("Checkbox non modificabile");
    }
});

// Drag & drop
const dropZone = document.querySelector('#drop-zone');

dropZone.addEventListener('dragover', (e) => {
    e.preventDefault();  // Necessario per drop!
});

dropZone.addEventListener('drop', (e) => {
    e.preventDefault();
    console.log("File droppato");
});

2
stopPropagation() - Ferma Bubbling

Ferma la propagazione dell'evento ai parent.

HTML
<div id="parent">
    <div id="child">
        <button id="btn">Click</button>
    </div>
</div>

Event Bubbling
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
const btn = document.querySelector('#btn');

// Senza stopPropagation
parent.addEventListener('click', () => {
    console.log("Parent cliccato");
});

child.addEventListener('click', () => {
    console.log("Child cliccato");
});

btn.addEventListener('click', () => {
    console.log("Button cliccato");
});

// Click su button stampa:
// "Button cliccato"
// "Child cliccato"    ← bubbling
// "Parent cliccato"   ← bubbling

// Con stopPropagation
btn.addEventListener('click', (e) => {
    e.stopPropagation();  // Ferma bubbling!
    console.log("Button cliccato");
});

// Ora stampa solo:
// "Button cliccato"

// Caso d'uso: Modal/dropdown
const modal = document.querySelector('.modal');
const modalContent = document.querySelector('.modal-content');
const closeBtn = document.querySelector('.close');

// Chiudi cliccando fuori
modal.addEventListener('click', () => {
    modal.style.display = 'none';
});

// NON chiudere cliccando dentro
modalContent.addEventListener('click', (e) => {
    e.stopPropagation();  // Non propaga a modal
});

🚀 Esempio Completo: App Interattiva

HTML
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Interactive App</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: Arial; padding: 20px; background: #f5f5f5; }
        .container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; }
        
        /* Form */
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input, select { width: 100%; padding: 10px; border: 2px solid #ddd; border-radius: 5px; font-size: 16px; }
        input:focus { border-color: #4CAF50; outline: none; }
        input.error { border-color: #f44336; }
        .error-message { color: #f44336; font-size: 14px; margin-top: 5px; display: none; }
        button { padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; }
        .btn-primary { background: #4CAF50; color: white; }
        .btn-primary:hover { background: #45a049; }
        
        /* Cards */
        .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 15px; margin-top: 20px; }
        .card { background: #f9f9f9; padding: 15px; border-radius: 8px; transition: transform 0.3s, box-shadow 0.3s; }
        .card:hover { transform: translateY(-5px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
        .card h3 { margin-bottom: 10px; color: #333; }
        .card p { color: #666; margin-bottom: 10px; }
        .card-actions { display: flex; gap: 10px; }
        .btn-edit { background: #2196F3; color: white; padding: 5px 10px; font-size: 14px; }
        .btn-delete { background: #f44336; color: white; padding: 5px 10px; font-size: 14px; }
        
        /* Modal */
        .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; }
        .modal.active { display: flex; align-items: center; justify-content: center; }
        .modal-content { background: white; padding: 30px; border-radius: 10px; max-width: 500px; width: 90%; }
        .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
        .close { cursor: pointer; font-size: 24px; }
        
        /* Stats */
        .stats { display: flex; gap: 15px; margin-top: 20px; }
        .stat-box { flex: 1; background: #e3f2fd; padding: 15px; border-radius: 8px; text-align: center; }
        .stat-box h4 { color: #1976d2; margin-bottom: 5px; }
        .stat-box p { font-size: 24px; font-weight: bold; color: #1565c0; }
    </style>
</head>
<body>
    <div class="container">
        <h1>📋 Task Manager Interattivo</h1>
        
        <!-- Form -->
        <form id="task-form">
            <div class="form-group">
                <label for="title">Titolo *</label>
                <input type="text" id="title" placeholder="Inserisci titolo..." required>
                <div class="error-message" id="title-error">Titolo obbligatorio (min 3 caratteri)</div>
            </div>
            
            <div class="form-group">
                <label for="description">Descrizione</label>
                <input type="text" id="description" placeholder="Descrizione opzionale...">
            </div>
            
            <div class="form-group">
                <label for="priority">Priorità</label>
                <select id="priority">
                    <option value="low">Bassa</option>
                    <option value="medium" selected>Media</option>
                    <option value="high">Alta</option>
                </select>
            </div>
            
            <button type="submit" class="btn-primary">Aggiungi Task</button>
        </form>
        
        <!-- Stats -->
        <div class="stats" id="stats">
            <div class="stat-box">
                <h4>Totale</h4>
                <p id="stat-total">0</p>
            </div>
            <div class="stat-box">
                <h4>Alta Priorità</h4>
                <p id="stat-high">0</p>
            </div>
            <div class="stat-box">
                <h4>In Corso</h4>
                <p id="stat-active">0</p>
            </div>
        </div>
        
        <!-- Cards -->
        <div class="cards" id="cards-container"></div>
        
        <!-- Modal -->
        <div class="modal" id="modal">
            <div class="modal-content">
                <div class="modal-header">
                    <h2 id="modal-title">Modifica Task</h2>
                    <span class="close" id="modal-close">&times;</span>
                </div>
                <form id="edit-form">
                    <div class="form-group">
                        <label for="edit-title">Titolo</label>
                        <input type="text" id="edit-title">
                    </div>
                    <div class="form-group">
                        <label for="edit-description">Descrizione</label>
                        <input type="text" id="edit-description">
                    </div>
                    <div class="form-group">
                        <label for="edit-priority">Priorità</label>
                        <select id="edit-priority">
                            <option value="low">Bassa</option>
                            <option value="medium">Media</option>
                            <option value="high">Alta</option>
                        </select>
                    </div>
                    <button type="submit" class="btn-primary">Salva Modifiche</button>
                </form>
            </div>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>

JavaScript - script.js (PARTE 1)
// === SELEZIONE ELEMENTI ===
const taskForm = document.querySelector('#task-form');
const titleInput = document.querySelector('#title');
const descriptionInput = document.querySelector('#description');
const prioritySelect = document.querySelector('#priority');
const cardsContainer = document.querySelector('#cards-container');
const modal = document.querySelector('#modal');
const modalClose = document.querySelector('#modal-close');
const editForm = document.querySelector('#edit-form');

// Stats
const statTotal = document.querySelector('#stat-total');
const statHigh = document.querySelector('#stat-high');
const statActive = document.querySelector('#stat-active');

// Error
const titleError = document.querySelector('#title-error');

// === DATI ===
let tasks = [];
let nextId = 1;
let editingTaskId = null;

// === VALIDAZIONE ===
function validateTitle(value) {
    return value.trim().length >= 3;
}

// Validazione real-time
titleInput.addEventListener('input', (e) => {
    const value = e.target.value;
    
    if (value.length === 0) {
        titleInput.classList.remove('error');
        titleError.style.display = 'none';
    } else if (!validateTitle(value)) {
        titleInput.classList.add('error');
        titleError.style.display = 'block';
    } else {
        titleInput.classList.remove('error');
        titleError.style.display = 'none';
    }
});

// === FUNZIONI ===
function createTaskCard(task) {
    const card = document.createElement('div');
    card.className = 'card';
    card.dataset.id = task.id;
    card.dataset.priority = task.priority;
    
    const priorityEmoji = {
        low: '🟢',
        medium: '🟡',
        high: '🔴'
    };
    
    card.innerHTML = `
        <h3>${priorityEmoji[task.priority]} ${task.title}</h3>
        <p>${task.description || 'Nessuna descrizione'}</p>
        <div class="card-actions">
            <button class="btn-edit" data-action="edit">Modifica</button>
            <button class="btn-delete" data-action="delete">Elimina</button>
        </div>
    `;
    
    return card;
}

function renderTasks() {
    cardsContainer.innerHTML = '';
    
    tasks.forEach(task => {
        const card = createTaskCard(task);
        cardsContainer.append(card);
    });
    
    updateStats();
}

function updateStats() {
    statTotal.textContent = tasks.length;
    statHigh.textContent = tasks.filter(t => t.priority === 'high').length;
    statActive.textContent = tasks.length;
}

function addTask(taskData) {
    const newTask = {
        id: nextId++,
        ...taskData
    };
    
    tasks.push(newTask);
    renderTasks();
}

function deleteTask(id) {
    tasks = tasks.filter(t => t.id !== id);
    renderTasks();
}

function openEditModal(id) {
    const task = tasks.find(t => t.id === id);
    if (!task) return;
    
    editingTaskId = id;
    
    document.querySelector('#edit-title').value = task.title;
    document.querySelector('#edit-description').value = task.description;
    document.querySelector('#edit-priority').value = task.priority;
    
    modal.classList.add('active');
}

function closeModal() {
    modal.classList.remove('active');
    editingTaskId = null;
}

function updateTask(id, taskData) {
    const task = tasks.find(t => t.id === id);
    if (!task) return;
    
    Object.assign(task, taskData);
    renderTasks();
}

// Continua nella parte 2...

JavaScript - script.js (PARTE 2)
// === EVENT LISTENERS ===

// 1. Submit form principale
taskForm.addEventListener('submit', (e) => {
    e.preventDefault();
    
    const title = titleInput.value.trim();
    
    if (!validateTitle(title)) {
        titleInput.classList.add('error');
        titleError.style.display = 'block';
        titleInput.focus();
        return;
    }
    
    const taskData = {
        title: title,
        description: descriptionInput.value.trim(),
        priority: prioritySelect.value
    };
    
    addTask(taskData);
    
    // Reset form
    taskForm.reset();
    titleInput.focus();
});

// 2. Event delegation su cards
cardsContainer.addEventListener('click', (e) => {
    const action = e.target.dataset.action;
    const card = e.target.closest('.card');
    
    if (!card) return;
    
    const taskId = parseInt(card.dataset.id);
    
    switch(action) {
        case 'edit':
            openEditModal(taskId);
            break;
        
        case 'delete':
            if (confirm('Sicuro di voler eliminare questa task?')) {
                deleteTask(taskId);
            }
            break;
    }
});

// 3. Modal close
modalClose.addEventListener('click', closeModal);

// Click fuori modal per chiudere
modal.addEventListener('click', (e) => {
    if (e.target === modal) {
        closeModal();
    }
});

// 4. Edit form submit
editForm.addEventListener('submit', (e) => {
    e.preventDefault();
    
    const taskData = {
        title: document.querySelector('#edit-title').value.trim(),
        description: document.querySelector('#edit-description').value.trim(),
        priority: document.querySelector('#edit-priority').value
    };
    
    updateTask(editingTaskId, taskData);
    closeModal();
});

// 5. Keyboard shortcuts
document.addEventListener('keydown', (e) => {
    // ESC per chiudere modal
    if (e.key === 'Escape' && modal.classList.contains('active')) {
        closeModal();
    }
    
    // Ctrl/Cmd + K per focus su input
    if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
        e.preventDefault();
        titleInput.focus();
    }
});

// 6. Prevent context menu su cards
cardsContainer.addEventListener('contextmenu', (e) => {
    const card = e.target.closest('.card');
    
    if (card) {
        e.preventDefault();
        console.log('Menu personalizzato qui');
    }
});

// === INIZIALIZZAZIONE ===
console.log('🚀 App inizializzata!');
titleInput.focus();

// Task di esempio
addTask({
    title: 'Studiare Eventi JavaScript',
    description: 'Completare lezione 14',
    priority: 'high'
});

addTask({
    title: 'Fare la spesa',
    description: 'Latte, pane, frutta',
    priority: 'medium'
});

addTask({
    title: 'Chiamare il dentista',
    description: 'Prenotare visita',
    priority: 'low'
});

💡 Eventi Usati nell'Esempio
  • submit - Form principale e edit
  • input - Validazione real-time
  • click - Bottoni con event delegation
  • keydown - Keyboard shortcuts (ESC, Ctrl+K)
  • contextmenu - Click destro personalizzato
  • preventDefault() - Blocca submit e shortcuts
  • stopPropagation() - Modal click fuori
  • Event delegation - Cards container
  • Data attributes - Actions

✅ Best Practices Eventi

🌟 Regole d'Oro

1
Usa addEventListener()

// ✅ MODERNO
elemento.addEventListener('click', handler);

// ❌ EVITA
elemento.onclick = handler;  // Legacy

2
Event Delegation per Liste

// ✅ Un listener sul parent
container.addEventListener('click', (e) => {
    if (e.target.matches('.delete-btn')) {
        // Gestisci
    }
});

// ❌ Listener su ogni elemento
items.forEach(item => {
    item.addEventListener('click', handler);
});

3
preventDefault() su Form

// ✅ SEMPRE su submit
form.addEventListener('submit', (e) => {
    e.preventDefault();  // Non dimenticare!
    // Gestione custom
});

4
Debounce Eventi Frequenti

Per eventi come scroll, resize, input che scattano molto spesso.

// Debounce function
function debounce(func, wait) {
    let timeout;
    return function(...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => func(...args), wait);
    };
}

// ✅ Con debounce (performance)
window.addEventListener('scroll', debounce(() => {
    console.log('Scroll!');
}, 200));

// ❌ Senza debounce (troppi trigger!)
window.addEventListener('scroll', () => {
    console.log('Scroll!');  // Eseguito centinaia di volte!
});

🎯 Riepilogo Lezione

Concetto Sintassi Quando Usare
addEventListener el.addEventListener('click', fn) ⭐ Sempre (standard moderno)
Event Object (e) => { e.target } Accedere info evento
preventDefault e.preventDefault() Form, link, keyboard
stopPropagation e.stopPropagation() Fermare bubbling
Event Delegation Listener su parent Liste dinamiche
Debounce Delay esecuzione scroll, resize, input

Lezione 14 del Corso JavaScript Completo

Torna in alto