Come realizzare un componente Leggi di più (read more) accessibile
Il “read more” è un tipo di elemento molto simile all’accordion e molto usato nei siti web. Anzi, in buona sostanza potremmo dire che è proprio un accordion, e funziona in modo quasi identico.
Tabella dei contenuti
Read more o accordion?
La differenza principale in genere sta nel titolo usato per il singolo elemento: spesso un accordion generico, come nel caso delle FAQ, riporta il titolo del contenuto che viene a seguire, mentre un elemento “read more” riporta solo la dicitura “Leggi di più” o “Scopri” e formule simili. In termini di accessibilità e SEO, non sono dei testi molto adatti, ma per le esigenze grafiche e le richieste di cliente finale, si cerca di fare il possibile.
Altra differenza sostanziale è che il “read more” ha solitamente una prima porzione di testo già visibile, e una seconda porzione nascosta che deve apparire solo quando si aziona il relativo pulsante.
Si crea quindi un caso un po’ particolare che bisogna capire come gestire:
- Il primo testo di anteprima spesso contiene i 3 puntini finali per far capire che la lettura prosegue oltre
- Il secondo testo che appare quindi deve essere una continuazione del primo, oppure deve nascondere il primo testo e poi contenerlo all’interno di sé stesso (forse è più semplice capirlo dal codice HTML)
- Il pulsante deve avere un testo che faccia capire l’azione all’utente
Si tratta quindi di una alternativa piuttosto “custom” del classico accordion.
Esempio pratico con WordPress ed Elementor
Proviamo a vedere meglio il codice di un esempio che ho realizzato per un sito custom fatto con WordPress e Bootstrap Italia.
In questo caso ho usato Elementor sviluppando un Widget Custom che permettesse al cliente di creare un elemento “Read more” direttamente da editor visuale in backend. La soluzione è sicuramente migliorabile, ma a mio avviso rispecchia i requisiti minimi di accessibilità, tentando di mantenere comunque una struttura che sia facilmente gestibile da un utente meno esperto.
Rispetto ad altri Visual Builder che ho usato, come Divi o WP Bakery, Elementor sembra essere quello che permette più facilmente la personalizzazione da parte di noi sviluppatori web, e che allo stesso tempo rispetta in modo più fedele e “developer-friendly” le linee guida sull’accessibilità.
<div class="elementor-element elementor-element-d9aab0c e-grid e-con-boxed e-con e-parent e-lazyloaded" data-id="d9aab0c" data-element_type="container">
<div class="e-con-inner">
<div class="elementor-element elementor-element-fc3befe e-con-full e-flex e-con e-child animated slideInLeft" data-id="fc3befe" data-element_type="container" data-settings="{"animation":"slideInLeft"}">
<div class="elementor-element elementor-element-5ff33aa elementor-widget elementor-widget-image" data-id="5ff33aa" data-element_type="widget" data-settings="{"_animation":"none"}" data-widget_type="image.default">
<div class="elementor-widget-container">
<img fetchpriority="high" decoding="async" width="764" height="700" src="image.jpg" class="attachment-large size-large wp-image-49326" alt="" srcset="image.jpg 764w, image-300x275.jpg 300w, image-273x250.jpg 273w" sizes="(max-width: 764px) 100vw, 764px">
</div>
</div>
</div>
<div class="elementor-element elementor-element-bdbe2a7 e-con-full e-flex e-con e-child animated slideInRight" data-id="bdbe2a7" data-element_type="container" data-settings="{"animation":"slideInRight"}">
<div class="elementor-element elementor-element-be66a43 elementor-widget elementor-widget-readmore-custom" data-id="be66a43" data-element_type="widget" data-settings="{"_animation":"none"}" data-widget_type="readmore-custom.default">
<div class="elementor-widget-container">
<input type="checkbox" id="readmore-handler-66b0b89f9e5e2" class="sr-only visually-hidden" aria-expanded="false" aria-controls="readmore-content-wrapper-66b0b89f9e5e2" tabindex="0" onchange="toggleReadMore('#readmore-content-wrapper-66b0b89f9e5e2')" data-bs-target="readmore-content-wrapper-66b0b89f9e5e2">
<h2><span class="ez-toc-section" id="titolo" ez-toc-data-id="#titolo"></span>Titolo<span class="ez-toc-section-end"></span></h2>
<div id="readmore-content-wrapper-66b0b89f9e5e2" class="readmore-content-wrapper">
<div class="readmore-content-preview">
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat enim quasi ut exercitationem veniam cum, dolorem, iusto, in esse velit praesentium illo vel? Aliquid, tempore aspernatur dolor expedita voluptatibus dolorem!</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat enim quasi ut exercitationem veniam cum, dolorem, iusto, in esse velit praesentium illo vel? Aliquid, tempore aspernatur dolor expedita voluptatibus dolorem!</p>
<p>Questo testo finisce qui...</p>
</div>
<div class="readmore-content-full" style="display: none;">
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat enim quasi ut exercitationem veniam cum, dolorem, iusto, in esse velit praesentium illo vel? Aliquid, tempore aspernatur dolor expedita voluptatibus dolorem!</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat enim quasi ut exercitationem veniam cum, dolorem, iusto, in esse velit praesentium illo vel? Aliquid, tempore aspernatur dolor expedita voluptatibus dolorem!</p>
<p>Seconda parte di testo ...</p>
</div>
</div>
<p class="handler-label m-0"><label for="readmore-handler-66b0b89f9e5e2" tabindex="-1">Leggi di più</label></p>
</div>
</div>
</div>
</div>
</div>
<script>
function toggleReadMore(selector = null)
{
if (!selector) {
selector = jQuery(this).attr('data-bs-target');
}
let previewContent = jQuery(selector).find('.readmore-content-preview');
let fullContent = jQuery(selector).find('.readmore-content-full');
jQuery(previewContent).toggle();
jQuery(fullContent).toggle();
}
</script>
A parte i vari <div> creati di default da Elementor, questa sezione è divisa in due colonne, a sinistra abbiamo un’immagine e a destra il testo. Questo testo viene mostrato solo parzialmente e termina con i tre puntini di sospensione, viene poi sostituito dal secondo testo, inizialmente nascosto, che appare solo all’attivazione del pulsante “Leggi di più”.
Mostrare e nascondere il testo completo
Vi è quindi un piccolo trucchetto, che mira a risolvere il problema di come rendere il secondo testo una continuazione sensata del primo. In pratica, da backend vi sono due campi di testo, il primo che contiene il testo di anteprima, e il secondo che contiene sia il testo di anteprima sia la sua continuazione.
In questo modo, rendiamo più semplice all’utente che gestisce il sito capire come impostare questo componente personalizzato, perché può inserire il testo completo nel secondo campo, copiarne la prima parte, incollarla nel primo campo di testo e, se lo desidera, inserire in fondo i tre puntini di sospensione.
Il risultato finale è che attivando il pulsante, il primo testo viene nascosto, e il secondo testo ne prende il posto, generalmente riuscendo anche a mantenere la stessa altezza di partenza dell’elemento iniziale, che è un altro aspetto da non sottovalutare.
Infine, in questo modo è più semplice “spezzare” il testo: l’utente che inserisce i campi in backend non deve più di tanto preoccuparsi di come dividere tra “prima parte” e “seconda parte”, perché come abbiamo detto può semplicemente copiare e incollare le prime righe del testo completo.
Nel nostro codice HTML abbiamo molta carne al fuoco, ma proviamo a fare un po’ di ordine e capire quali sono gli elementi principali da tenere in considerazione:
- Abbiamo un <input type=”checkbox”> che viene in realtà nascosto tramite l’uso delle classi CSS sr-only (compatibile con Bootstrap 4) e visually-hidden (per Bootstrap 5). Inoltre questo checkbox usa aria-expanded e aria-controls come i button che abbiamo visto in altri esempi, infine è collegato ad una funzione Javascript toggleReadMore() che mostra o nasconde l’anteprima e il testo completo, a seconda che il pulsante sia azionato o meno. Notiamo anche l’uso di tabindex=”-1” sul quale torneremo a breve.
- Subito dopo l’input abbiamo un <h2> che contiene il titolo visibile del nostro elemento e anche l’àncora che viene usata per rimandare l’utente alla sezione specifica di questa pagina
- Allo stesso livello dell’input (questo è molto importante) abbiamo il contenitore del testo, che si divide poi tra testo di anteprima (visibile di default) e testo completo (nascosto di default)
- Infine, anche questo molto importante, abbiamo un <p> che contiene il <label> che viene usato per azionare il checkbox: in pratica, usando l’attributo “for” del <label>, popolato con l’id del checkbox, riusciamo a collegare i due elementi e renderli interagibili. L’utente clicca l’elemento <label>, il quale agisce sul checkbox. È una soluzione simile a quella che abbiamo visto per i menù di navigazione. Sul label il tabindex=”-1” rende l’elemento non raggiungibile alle tecnologie assistive e al tabbing della tastiera, mentre il tabindex=”0” sul checkbox lo rende raggiungibile, evitando così una doppia tabulazione quando l’elemento che deve realmente ricevere il focus è solo il checkbox.
È importante che il contenuto e l’elemento <label> siano a pari livello del checkbox, così da renderli raggiungibili in CSS usando il selettore sibling “~”, ad esempio così:
input[type=”checkbox”]:checked ~ .handler-label label { opacity:1; }
input[type=”checkbox”]:checked ~ .readmore-content-full { display:block; }
input[type=”checkbox”]:checked ~ .readmore-content-preview { display:none; }
Ovviamente, come già accennato anche per gli accordion, la soluzione non è ideale per i crawler dei motori di ricerca, che riusciranno ad indicizzare solo la prima parte visibile del testo (anteprima), tuttavia è una buona via di mezzo per incontrare le richieste del cliente.
Link utili
- Elementor WordPress plugin
- Custom Widgets with Elementor
- Bootstrap 4: sr-only
- Boostrap 5.2: visually-hidden
- MDN: Subsequent-sibling combinator