W3docs

Elementi Interattivi e Widget nello Sviluppo Web

Crea widget personalizzati accessibili in JavaScript — slider, modal, tab e accordion — più la Canvas API per grafica dinamica, con esempi pratici e navigabili da tastiera.

Creare widget personalizzati e sfruttare le API di HTML può migliorare significativamente l'interattività e l'esperienza utente delle tue applicazioni web. Questa guida fornisce istruzioni passo passo per costruire elementi interattivi personalizzati come slider, modal e tab, e ti introduce alle API HTML5 come la Canvas API per la creazione di grafica dinamica.

Introduzione

Un widget è un componente UI interattivo autonomo — uno slider, un dialogo modal, un pannello a schede, un accordion — che l'utente può azionare per modificare ciò che vede o per inserire dati. I browser offrono nativamente alcuni widget (<input type="range">, <dialog>, <details>), ma spesso dovrai costruire i tuoi quando hai bisogno di comportamenti personalizzati, stili o layout che gli elementi nativi non forniscono.

Questa guida mostra come costruire i tre widget più utilizzati — uno slider, un modal e un insieme di tab — e poi introduce la Canvas API, una funzionalità HTML5 per disegnare grafica dinamica. Ogni esempio collega il comportamento con il DOM e la gestione degli eventi, quindi è utile avere familiarità con la selezione degli elementi e gli eventi del browser prima di procedere.

Widget nativo o widget personalizzato?

Preferisci un elemento nativo prima di scrivere JavaScript. I widget nativi sono accessibili, utilizzabili da tastiera e compatibili con i form fin da subito, e continuano a funzionare anche quando il tuo script non riesce a caricarsi.

NecessitàElemento nativoCostruisci personalizzato quando…
Selezionare un valore in un intervallo<input type="range">hai bisogno di un controllo a due cursori o non lineare
Mostrare un dialogo bloccante<dialog> + showModal()hai bisogno di un layout o animazione completamente personalizzati
Sezione comprimibile<details> / <summary>hai bisogno di un gruppo accordion animato
Tab(nessun elemento nativo)sempre personalizzato — segui il pattern ARIA tabs

L'esempio di slider qui sotto in realtà racchiude il nativo <input type="range">, che è l'approccio consigliato. Il modal e le tab sono costruiti da zero così puoi vedere le parti in gioco, ma in produzione preferisci <dialog> per i modal.

Buone Pratiche

  1. Usa HTML Semantico: Assicurati che la struttura HTML sia significativa e accessibile.
  2. Separazione delle Responsabilità: Mantieni HTML, CSS e JavaScript separati per avere codice pulito e gestibile.
  3. Accessibilità: Assicurati che gli elementi interattivi siano accessibili a tutti gli utenti, compresi quelli che usano screen reader.
  4. Ottimizzazione delle Prestazioni: Riduci al minimo la manipolazione del DOM e ottimizza JavaScript per garantire interazioni fluide.
  5. Design Responsivo: Assicurati che gli elementi interattivi funzionino bene su diverse dimensioni di schermo e dispositivi.

Creare Widget Personalizzati

Attenzione

Per le applicazioni in produzione, considera prima gli elementi HTML nativi: <dialog> per i modal e <details> / <summary> per i contenuti comprimibili. Ti forniscono gratuitamente la gestione del focus, il tasto Escape e il supporto per la tastiera.

Slider Personalizzato

Uno slider personalizzato consente agli utenti di selezionare un valore da un intervallo. Ecco come crearne uno usando HTML, CSS e JavaScript.

Esempio

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Custom Slider</title>
    <style>
        .slider-container {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        #slider {
            width: 200px;
        }
    </style>
</head>
<body>
    <div class="slider-container">
        <input type="range" id="slider" min="0" max="100" value="50" aria-label="Value slider" aria-describedby="slider-value" />
        <span id="slider-value">50</span>
    </div>
    <script>
        document.getElementById('slider').addEventListener('input', function() {
            document.getElementById('slider-value').textContent = this.value;
        });
    </script>
</body>
</html>

Questo esempio crea un semplice slider con un input range e uno span per visualizzare il valore corrente. JavaScript aggiorna il contenuto testuale dello span mentre lo slider si sposta.

I modal vengono usati per visualizzare contenuti in un overlay. Ecco come creare un modal personalizzato.

Esempio

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Custom Modal</title>
    <style>
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            justify-content: center;
            align-items: center;
        }

        .modal-content {
            background-color: #fff;
            padding: 20px;
            border-radius: 5px;
            text-align: center;
        }

        .close {
            position: absolute;
            top: 10px;
            right: 10px;
            font-size: 20px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <button id="open-modal">Open Modal</button>
    <div id="modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title" tabindex="-1">
        <div class="modal-content">
            <span id="close-modal" class="close" aria-label="Close modal">&times;</span>
            <h2 id="modal-title">Custom Modal</h2>
            <p>This is a custom modal!</p>
        </div>
    </div>
    <script>
        const modal = document.getElementById('modal');
        const openBtn = document.getElementById('open-modal');
        const closeBtn = document.getElementById('close-modal');

        openBtn.addEventListener('click', () => {
            modal.style.display = 'flex';
            closeBtn.focus();
        });

        closeBtn.addEventListener('click', () => {
            modal.style.display = 'none';
            openBtn.focus();
        });

        window.addEventListener('keydown', (e) => {
            if (modal.style.display === 'flex' && e.key === 'Escape') {
                modal.style.display = 'none';
                openBtn.focus();
            }
        });

        modal.addEventListener('click', (event) => {
            if (event.target === modal) {
                modal.style.display = 'none';
                openBtn.focus();
            }
        });
    </script>
</body>
</html>

Questo esempio mostra come creare un modal che può essere aperto e chiuso usando JavaScript. Il modal visualizza un overlay e una finestra di contenuto, che può essere chiusa facendo clic su un pulsante, premendo Escape o facendo clic sull'overlay. Il focus viene gestito per garantire che gli utenti che usano la tastiera possano navigare nel dialogo.

Tab Personalizzate

Le tab consentono agli utenti di passare tra diverse sezioni di contenuto. Ecco come creare tab personalizzate.

Esempio

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Custom Tabs</title>
    <style>
        .tabs {
            display: flex;
            gap: 10px;
        }

        .tab-button {
            padding: 10px 20px;
            cursor: pointer;
            background-color: #f0f0f0;
            border: 1px solid #ccc;
            border-radius: 5px;
        }

        .tab-button.active {
            background-color: #fff;
            border-bottom: 2px solid #000;
        }

        .tab-content {
            display: none;
            margin-top: 20px;
        }

        .tab-content.active {
            display: block;
        }
    </style>
</head>
<body>
    <div class="tabs" role="tablist">
        <button class="tab-button active" role="tab" aria-selected="true" data-tab="tab1">Tab 1</button>
        <button class="tab-button" role="tab" aria-selected="false" data-tab="tab2">Tab 2</button>
        <button class="tab-button" role="tab" aria-selected="false" data-tab="tab3">Tab 3</button>
    </div>
    <div class="tab-content active" role="tabpanel" aria-labelledby="tab1">
        <p>Content for Tab 1</p>
    </div>
    <div class="tab-content" role="tabpanel" aria-labelledby="tab2">
        <p>Content for Tab 2</p>
    </div>
    <div class="tab-content" role="tabpanel" aria-labelledby="tab3">
        <p>Content for Tab 3</p>
    </div>
    <script>
        // Array.from is important: querySelectorAll returns a NodeList,
        // which has no .indexOf method. We need a real array for the
        // arrow-key navigation below.
        const buttons = Array.from(document.querySelectorAll('.tab-button'));
        const contents = document.querySelectorAll('.tab-content');

        function activateTab(clickedBtn) {
            buttons.forEach(btn => {
                btn.classList.remove('active');
                btn.setAttribute('aria-selected', 'false');
            });
            contents.forEach(content => content.classList.remove('active'));

            clickedBtn.classList.add('active');
            clickedBtn.setAttribute('aria-selected', 'true');
            document.getElementById(clickedBtn.dataset.tab).classList.add('active');
        }

        buttons.forEach(button => {
            button.addEventListener('click', () => activateTab(button));
            button.addEventListener('keydown', (e) => {
                let nextIndex;
                if (e.key === 'ArrowRight') nextIndex = (buttons.indexOf(button) + 1) % buttons.length;
                else if (e.key === 'ArrowLeft') nextIndex = (buttons.indexOf(button) - 1 + buttons.length) % buttons.length;
                else return;
                e.preventDefault();
                buttons[nextIndex].focus();
                activateTab(buttons[nextIndex]);
            });
        });
    </script>
</body>
</html>

Questo esempio crea un'interfaccia a schede. Facendo clic su un pulsante di tab o usando i tasti freccia sinistra/destra verrà visualizzato il contenuto corrispondente e gli altri verranno nascosti. La gestione dei tasti freccia segue il pattern WAI-ARIA per le tab, che consente agli utenti da tastiera di spostarsi tra le tab senza un mouse — una parte fondamentale dell'accessibilità DOM.

Accordion con <details> nativo

Non ogni widget comprimibile ha bisogno di JavaScript. L'elemento nativo <details> ti fornisce un toggle apri/chiudi, supporto per la tastiera e la semantica corretta senza nessun script. Usalo per FAQ e pannelli di informazioni.

Esempio

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Accordion</title>
</head>
<body>
    <details>
        <summary>What is a widget?</summary>
        <p>A self-contained interactive UI component.</p>
    </details>
    <details>
        <summary>Do I always need JavaScript?</summary>
        <p>No — native elements like this one work without any script.</p>
    </details>
    <script>
        // Optional: react when a panel opens (e.g. lazy-load content).
        document.querySelectorAll('details').forEach((d) => {
            d.addEventListener('toggle', () => {
                console.log(d.open ? 'opened' : 'closed');
            });
        });
    </script>
</body>
</html>

L'evento toggle si attiva ogni volta che l'utente espande o comprime un pannello, quindi puoi caricare contenuti in modo lazy o tracciare analitiche. Ricorri a un accordion con script solo quando hai bisogno di animazioni o del comportamento "solo un pannello aperto alla volta".

Usare le API HTML5

Introduzione alle API HTML5

Le API HTML5 forniscono funzionalità potenti che migliorano le applicazioni web. Una delle API HTML5 più versatili è la Canvas API, che consente la creazione di grafica dinamica.

Usare la Canvas API

La Canvas API ti consente di disegnare grafica direttamente su una pagina web. Ecco un esempio di base su come usare la Canvas API.

Esempio

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Canvas API Example</title>
</head>
<body>
    <canvas id="myCanvas" width="400" height="400" style="border:1px solid #000;"></canvas>
    <script>
        const canvas = document.getElementById('myCanvas');
        const ctx = canvas.getContext('2d');

        // Draw a rectangle
        ctx.fillStyle = '#FF0000';
        ctx.fillRect(50, 50, 150, 100);

        // Draw a circle
        ctx.beginPath();
        ctx.arc(200, 200, 40, 0, 2 * Math.PI);
        ctx.fillStyle = '#00FF00';
        ctx.fill();

        // Draw text
        ctx.font = '20px Arial';
        ctx.fillStyle = '#0000FF';
        ctx.fillText('Hello Canvas', 100, 300);
    </script>
</body>
</html>

Questo esempio mostra le funzioni di disegno di base della Canvas API:

  • fillRect(x, y, width, height) disegna un rettangolo pieno il cui angolo superiore sinistro si trova in (x, y).
  • arc(x, y, radius, startAngle, endAngle) aggiunge un percorso circolare centrato in (x, y); gli angoli sono in radianti, quindi un cerchio completo è da 0 a 2 * Math.PI. Devi chiamare beginPath() prima e fill() (o stroke()) dopo.
  • fillText(text, x, y) disegna del testo, dove (x, y) è il punto di inizio della linea base, non l'angolo superiore sinistro.

Un errore comune: imposta fillStyle prima della chiamata di disegno che deve influenzare — il canvas mantiene l'ultimo colore impostato, quindi riutilizzare un colore tra forme diverse è facile da fare per errore. Per le animazioni, cancella il frame precedente con ctx.clearRect(0, 0, canvas.width, canvas.height) e ridisegna all'interno di requestAnimationFrame. Impostare la dimensione del canvas in CSS invece degli attributi width/height allunga il disegno — dimensionalo sempre con gli attributi.

Informazione

Assicurati sempre che i tuoi elementi interattivi siano accessibili. Usa ruoli e proprietà ARIA, HTML semantico e garantisci la navigabilità da tastiera per offrire un'esperienza utente inclusiva a tutti gli utenti. Questo non solo migliora l'accessibilità, ma aumenta anche l'usabilità generale e la SEO.

Conclusione

I widget personalizzati — slider, modal, tab, accordion — e le API HTML5 come Canvas ti consentono di costruire le superfici interattive su cui si basano le moderne applicazioni web. La lezione ricorrente in tutti questi casi è la stessa: parti da un elemento nativo quando ne esiste uno, poi aggiungi JavaScript solo per il comportamento che il browser non ti fornisce, e non trascurare mai il supporto per tastiera e ARIA.

Per approfondire, consulta le tecniche di manipolazione del DOM per i blocchi costitutivi che questi widget utilizzano, la gestione degli eventi nel DOM per come viene collegato l'input dell'utente, e l'ottimizzazione delle prestazioni del DOM per mantenere le interazioni fluide.

Esercitazione

Pratica
Quali delle seguenti affermazioni sugli elementi interattivi e sui widget sono vere?
Quali delle seguenti affermazioni sugli elementi interattivi e sui widget sono vere?
Was this page helpful?