Sommario
< Home
Stampa

Single Page Application

Una Single Page Application è una applicazione Web Javascript composta da una sola pagina html, ma che può comprendere diverse pagine visualizzabili dall’utente. In pratica anzichè utilizzare il sistema tradizionale per cui ad ogni pagina Web corrisponde una pagina HTML con un eventuale Javascript associato, viene caricata una sola pagina, ed è il programma Javascript a mostrare (o nascondere) dinamicamente la singola pagina dell’applicazione attualmente visualizzata.

Il grosso vantaggio di questo tipo di applicazione è che il caricamento della pagina è praticamente istantaneo (nessuna latenza) e questo da all’utente la percezione non di navigare su un sito ma di utilizzare una applicazione locale, dove l’utilizzo della rete è ridotto al solo di scambio di dati col server.

Il meccanismo più semplice per gestire una SPA è attraverso la creazione di “pagine logiche” definite da un tag specifico (ad esempio <div>) all’interno della pagina HTML e poi realizzare un componente JS con il ruolo di “navigatore”, che secondo regole ben definite, mostra in ogni momento una sola di queste pagine.

Nel progetto qui presentato realizzeremo un tipo di pagina di questo tipo, con queste caratteristiche:

  1. La pagina è suddivisa in contenitori <div> che contengono i componenti di una singola pagina logica.
  2. Ogni pagina è associata ad una URL interna (che contiene quindi un suffisso “#nomepagina”) ovvero utilizzando la forma https//miosito/path/pagina.html#nomepagina, dove ad ogni pagina viene associata una URL univoca. Quindi per ogni url interna avremo un contenitore div che contiene una specifica pagina logica. Le singole pagine possono contenere link ad altre pagine logiche, permettendo quindi una navigazione interna.
  3. E’ presente poi un componente navigator che contiene una funzione che analizza la URL corrente, ed in base a questa mostra la pagina corrispondente.
  4. Infine il navigator intercetta l’evento di cambio pagina, esegue la funzione del punto precedente e cambia la pagina visualizzata, in modo del tutto trasparente per l’utente.

Vediamo un esempio di codice funzionante, da personalizzare poi per i propri progetti.

HTML

<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
      type="text/css" />
   <link rel="stylesheet" type="text/css" href="./style.css" />
   <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"></script>
   <title>SPA semplice</title>
</head>

<body>
   <div id="container" class="container">
      <div id="pagina1" class="page">
         <div class="row">Questo è il contenuto di pagina 1</div>
         <div class="row"><a href="#pagina2">Questo è un link a pagina 2</a></div>
         <div class="row"><a href="#pagina3">Questo è un link a pagina 3</a></div>                  
      </div>
      <div id="pagina2" class="page">
         <div class="row"><a href="#pagina1">Questo è un link a pagina 1</a></div>
         <div class="row">Questo è il contenuto di pagina 2</div>         
         <div class="row"><a href="#pagina3">Questo è un link a pagina 3</a></div>                  
      </div>
      <div id="pagina3" class="page">         
         <div class="row"><a href="#pagina1">Questo è un link a pagina 1</a></div>
         <div class="row"><a href="#pagina2">Questo è un link a pagina 2</a></div>                  
         <div class="row">Questo è il contenuto di pagina 3</div>
      </div>
   </div>
   <script type="module" src="./index.js"></script>

</body>

</html>

come si può vedere ogni <div> di pagina è caratterizzato da una classe “page” in modo da essere identificabile dal componente navigator. L’id del div corrisponde alla URL interna del sito, pertanto ad esempio http://miosito/spa.html#pagina2 corrisponde alla visualizzazione del <div> con id=”pagina2″. Osservare poi che le ancore hanno lo stesso nome delle pagine presenti (col prefisso “#”).

CSS

.visible {
   display: block;
}

.hidden {
   display: none;
}

Sono necessarie due classi CSS per gestire la visibilità delle singole pagine, come vedremo qui sotto.

Javascript: Navigator

const hide = (elements) => {
   elements.forEach((element) => {
      element.classList.add("hidden");
      element.classList.remove("visible");
   });
}

const show = (element) => {
   element.classList.add("visible");
   element.classList.remove("hidden");   
}

export const createNavigator = (parentElement) => {
   const pages = Array.from(parentElement.querySelectorAll(".page"));
   
   const render = () => {
      const url = new URL(document.location.href);
      const pageName = url.hash.replace("#", "");
      const selected = pages.filter((page) => page.id === pageName)[0] || pages[0];

      hide(pages);
      show(selected);
   }
   window.addEventListener('popstate', render); 
   render();   
}

La funzione hide ha lo scopo di nascondere tutte le pagine prima di mostrare la nuova pagina. La funzione show invece mostra la sola pagina selezionata.

Veniamo al costruttore del navigator. L’istruzione:

   const pages = Array.from(parentElement.querySelectorAll(".page"));

seleziona tutti i div con classe “page”, ovvero le pagine logiche.

Le istruzioni:

const url = new URL(document.location.href);
const pageName = url.hash.replace("#", "");

servono a estrarre dal suffisso il nome della pagina, che sappiamo corrispondere alla pagina.

A questo punto:

const selected = pages.filter((page) => page.id === pageName)[0] || pages[0];

Seleziona la pagina a partire dal nome (o di default la prima pagina).

hide(pages);
show(selected);

A questo punto si procede prima col nascondere tutte le pagine e poi mostrare quella selezionata.

Infine:

window.addEventListener('popstate', render);

L’evento popstate viene emesso ogni volta che cambia la url (ad esempio dopo aver cliccato su un link). Quindi quel che succede è che non appena l’utente clicca su un link interno questo evento viene intercettato dal navigator, che esegue la funzione render, che analizza la URL del browser, estrae il nome della pagina e la mostra.

Javascript: index

import { createNavigator } from "./navigator.js";

const navigator = createNavigator(document.querySelector("#container"));

Il navigator può essere quindi incluso nella pagina index.js del progetto, analogamente a quanto già visto nella lezione sui componenti.