Questa pagina contiene la traduzione dell'articolo "A tale of two viewports" di Peter-Paul Koch. L'articolo originale si trova nel sito www.quirksmode.org, a questo indirizzo.

Racconto di due viewports - parte prima

In questa miniserie, spiegherò il comportamento dei viewports e delle larghezze di alcuni importanti elementi, come l'elemento <html>, la finestra del browser e lo schermo.

Questa pagina parla di browser desktop, e il suo unico scopo è quello di porre le premesse per un'analoga discussione sui browser per dispositivi mobili. Molti sviluppatori web conoscono già intuitivamente la maggior parte dei concetti riguardanti l'ambiente desktop. In ambiente mobile troveremo gli stessi concetti, ma con qualche complicazione in più; per questo, una discussione preliminare riguardante termini che tutti già conoscono aiuterà la comprensione dei browser mobili.

Concetto: device pixels e CSS pixels

Il primo concetto che è necessario comprendere sono i pixel CSS e la differenza tra di essi e i device pixels.

Se si assegna ad un determinato elemento una larghezza di 128px, su un monitor largo 1024px e con la dimensione del browser massimizzata, l'elemento entra nel monitor otto volte (all'incirca; ignoriamo i dettagli per ora).

Se l'utente ingrandisce attraverso lo zoom, però, la situazione cambia. Al 200%, ad esempio, la larghezza di 128px entra solo quattro volte nel monitor da 1024px.

Lo zoom implementato dai moderni browser consiste in nulla più che "dilatare" i pixel. Ossia, la larghezza dell'elemento non viene modificata da 128 a 256 pixel; viene invece raddoppiata la dimensione dei pixel. Formalmente, l'elemento possiede ancora la larghezza di 128 pixel CSS, anche se ricopre uno spazio di 256 device pixels.

In altre parole, zoomare al 200% fa assumere ad un pixel CSS quattro volte la dimensione di un device pixel (il doppio di larghezza e il doppio di altezza, per un totale di quattro volte la superficie).

Qualche immagine aiuterà a chiarire il concetto. Ecco quattro pixel con un valore di zoom del 100%. Nulla di rilevante fin qui; i pixel CSS sono esattamente sovrapposti ai device pixels.

Ora zoomiamo all'indietro (cioè diminuiamo il valore dello zoom). I pixel CSS iniziano a "restringersi", ad indicare che un device pixel ora ricopre diversi pixel CSS.

Se invece zoomiamo avanti (cioè aumentiamo il valore dello zoom), avviene il contrario. I pixel CSS si ingrandiscono, ed ora ognuno di essi ricopre diversi device pixels.

Il punto è che ciò che ti interessa sono solo i pixel CSS. Sono questi pixel che determinano come viene interpretato il foglio di stile.

I device pixels non hanno quasi alcuna utilità per te, in quanto sviluppatore. E non ne hanno neppure per l'utente; l'utente zoomerà avanti o indietro finché non riuscirà a leggere comodamente la pagina. Quale che sia il livello di zoom, comunque, a te non interessa. Penserà il browser a fare in modo che il layout creato con i CSS sia automaticamente allargato o ristretto.

Zoom al 100%

Ho iniziato l'esempio assumendo un livello di zoom del 100%. È ora di definire meglio questo concetto:

Con un valore di zoom del 100% un pixel CSS è esattamente uguale ad un device pixel.

Il concetto di zoom al 100% è molto utile nella spiegazione che seguirà, ma non occorre che ti preoccupi troppo di questo concetto nel lavoro di tutti i giorni. In ambiente desktop testerai generalmente i tuoi siti con lo zoom impostato al 100%, ma anche se l'utente effettua una zoomata, la magia dei pixel CSS assicurerà che nel layout i rapporti tra le dimensioni degli elementi siano conservati.

Dimensioni dello schermo

Facciamo qualche misura pratica. Inizieremo con la larghezza (screen.width) e l'altezza dello schermo (screen.height). Queste due grandezze rappresentano la larghezza e l'altezza totali dello schermo dell'utente. Sono misurate in device pixels perché non cambiano mai: sono una caratteristica del monitor, non del browser.

Divertente! Ma cosa ce ne facciamo di quest'informazione?

Fondamentalmente, nulla. La dimensione del monitor dell'utente non ha alcuna importanza per noi — be', a meno che non ti interessi raccogliere il dato a fini statistici.

Dimensioni della finestra

Ciò che invece ti interessa conoscere sono le dimensioni interne della finestra del browser. Quest'informazione ti dice esattamente quanto spazio l'utente ha a disposizione per il tuo layout CSS. Puoi ricavare queste dimensioni da window.innerWidth e window.innerHeight.

Com'è ovvio, la larghezza interna della finestra è misurata in pixel CSS. A te serve sapere quanto del tuo layout puoi "infilare" nella finestra del browser, e questa quantità descresce man mano che l'utente aumenta lo zoom. Così, se l'utente ingrandisce con lo zoom, ti resta meno spazio a disposizione nella finestra, e la diminuzione delle quantità window.innerWidth/Height riflette questo fatto.

(Fa eccezione Opera, in cui window.innerWidth e window.innerHeight non decrescono quando l'utente aumenta lo zoom: esse, infatti, sono misurate in device pixels. Questa cosa è piuttosto fastidiosa in ambiente desktop, ma addirittura disastrosa nei dispositivi mobili, come vedremo più avanti.)

Notare che le larghezze e le altezze misurate includono le barre di scorrimento. Anch'esse sono considerate parte dell'interno della finestra (per lo più per ragioni storiche).

Scrolling offset

window.pageXOffset e window.pageYOffset contengono gli offset di scorrimento orizzontale e verticale del documento. In questo modo puoi sapere quanta parte della pagina è stata fatta scorrere dall'utente.

Anche queste proprietà sono misurate in pixel CSS. A te, infatti, interessa sapere quanta parte di documento è stata scorsa, indipendentemente dal livello di zoom.

In teoria, se l'utente scorre la pagina e poi effettua una zoomata, window.pageX/YOffset cambiano valore. In ogni caso, il browser cerca di mantenere una certa consistenza conservando nella parte alta della parte visibile della pagina lo stesso elemento prima e dopo lo zoom. Ciò non funziona sempre perfettamente, ma significa che nella pratica window.pageX/YOffset in realtà non cambiano: il numero di pixel CSS che sono usciti dalla pagina in seguito allo scorrimento rimane (all'incirca) lo stesso.

Concetto: il viewport

Prima di proseguire con altre proprietà JavaScript, dobbiamo introdurre un altro concetto: il viewport.

La funzione del viewport è quella di delimitare l'elemento <html>, che rappresenta il blocco radice della pagina.

Detto così forse è un po' vago, quindi meglio ricorrere ad un esempio pratico. Supponi di avere un layout liquido con una barra laterale del 10% di larghezza (width:10%). Allora tale barra laterale si allargherà e si stringerà a seconda di come ridimensionerai la finestra del browser. Ma come avviene ciò, esattamente?

Ciò che avviene da un punto di vista tecnico è che la barra laterale prende il 10% della larghezza dell'elemento genitore. Supponiamo che sia l'elemento <body> (e che ad esso non sia stata assegnata una larghezza). Allora la domanda diventa: qual è la larghezza del <body>?

Normalmente, gli elementi di tipo block prendono il 100% della larghezza del proprio genitore (ci sono alcune eccezioni, che per ora ignoriamo). Quindi il <body> è largo come il proprio genitore, ossia l'elemento <html>.

Ma quanto è largo l'elemento <html>? Be', è largo quanto la finestra del browser. Ecco perché la tua barra laterale di larghezza 10% (width:10%) occupa il 10% dell'intera finestra del browser. Tutti gli sviluppatori web conoscono intuitivamente ed utilizzano questo fatto.

Ciò che potresti ignorare è come ciò funziona da un punto di vista teorico. In teoria, la larghezza dell'elemento <html> è delimitata dalla larghezza del viewport. L'elemento <html> prende il 100% della larghezza di tale viewport.

Il viewport, a sua volta, coincide esattamente con la finestra del browser: la sua definizione è proprio questa. Il viewport non è un costrutto HTML, quindi non ti è possibile influenzarlo tramite CSS. Ha proprio la larghezza e l'altezza della finestra del browser — in ambiente desktop. Per i dispositivi mobili, la questione è leggermente più complessa.

Conseguenze

Questo stato di cose ha delle conseguenze curiose. Puoi vederne una proprio su questo sito (mi riferisco al sito in cui si trova l'articolo originale, ndt). Scorri fino all'estremità superiore della pagina e zooma avanti di due o tre passi fino a far debordare dalla finestra del browser una parte del contenuto della pagina.

Ora scorri verso destra e vedrai che la barra blu che si trova nella parte alta della pagina non è più allineata corretamente.

Questo comportamento è una conseguenza di come è stato definito il viewport. Alla barra blu in alto è stata data una larghezza width:100%. 100% di cosa? Dell'elemento <html>, che è largo come il viewport, che a sua volta è largo quanto la finestra del browser.

Il punto è questo: mentre ciò ha funzionato bene per uno zoom del 100%, ora che abbiamo zoomato avanti il viewport è divenuto più piccolo della larghezza totale della pagina. Di per sé la cosa non ha grande rilevanza; il contenuto ora esce dall'elemento <html>, ma tale elemento ha overflow:visible, che significa che il contenuto "traboccante" viene mostrato in ogni caso.

Ma la barra blu non "trabocca". Dopo tutto, le è stata assegnata una larghezza width:100%, e il browser obbedisce dandole la larghezza del viewport. Non gli interessa che tale larghezza ora è troppo esigua.

Qual è la larghezza del documento?

Ciò che davvero mi occorrerebbe sapere è quanto è largo l'intero contenuto della pagina, comprese le parti che fuoriescono. Per quanto ne sappia, non è possibile conoscere questo valore (a meno di non voler calcolare le larghezze e i margini di ciascun elemento della pagina, ma questa tecnica è a dir poco soggetta ad errori).

Comincio a pensare che avremmo bisogno di una proprietà JavaScript che fornisca ciò che io chiamo "larghezza del documento" (in pixel CSS, ovviamente).

E se davvero vogliamo fare gli eccentrici, perché non esporre tale valore anche al CSS? Mi piacerebbe impostare width:100% come larghezza della mia barra blu, rendendola dipendente dalla larghezza del documento, e non dalla larghezza dell'elemento <html>. (Ciò può tuttavia risultare complicato, pertanto non mi sorprenderebbe scoprire che è impossibile da implementare.)

Produttori di browser, che ne pensate?

Misurare il viewport

Potresti voler conoscere le dimensioni del viewport. Esse si trovano in document.documentElement.clientWidth e -Height.

Se conosci il DOM, sai che document.documentElement è in realtà l'elemento <html>: l'elemento radice di ogni documento HTML. Il viewport è - per così dire - un livello più su; è l'elemento che contiene l'elemento <html>. Ciò può interessarti se assegni una larghezza all'elemento <html> (raccomando di non farlo, ma comunque è possibile).

In questa situazione document.documentElement.clientWidth e -Height forniscono ancora le dimensioni del viewport, e non dell'elemento <html>. (Questa è una regola speciale che vale solo per questo elemento e per questa coppia di proprietà. In tutti gli altri casi viene utilizzata la larghezza effettiva dell'elemento.)

Insomma, document.documentElement.clientWidth e -Height danno sempre le dimensioni del viewport, indipendentemente dalle dimensioni dell'elemento <html>.

Due coppie di proprietà

Ma le dimensioni del viewport non sono date anche da window.innerWidth/Height? Be', sì e no.

C'è una differenza formale tra le due coppie di proprietà: document.documentElement.clientWidth e -Height non includono la barra di scorrimento, mentre window.innerWidth/Height la comprendono. Si tratta soltanto di un cavillo.

L'esistenza di due coppie di proprietà è un residuo della Guerra dei Browser. A quei tempi Netscape supportava solo window.innerWidth/Height, mentre IE solo document.documentElement.clientWidth e -Height. Da allora tutti gli altri browser iniziarono a supportare clientWidth/Height, ma IE non integrò window.innerWidth/Height.

La disponibilità di due coppie di proprietà è solo una piccola seccatura in ambiente desktop — ma si rivela una benedizione nei dispositivi mobili, come vedremo.

Misurare l'elemento <html>

Quindi clientWidth/Height forniscono le dimensioni del viewport. Ma dove possiamo trovare le dimensioni dell'elemento <html>? Esse sono contenute in document.documentElement.offsetWidth e -Height.

Queste proprietà permettono realmente di accedere all'elemento <html> come elemento di tipo block; se imposti una larghezza, offsetWidth rifletterà tale larghezza.

Coordinate degli eventi

Poi ci sono le coordinate degli eventi. Quando si verifica un evento del mouse, vengono esposte non meno di cinque coppie di proprietà che forniscono informazioni sulla posizione esatta in cui l'evento si è verificato. Ai fini della nostra discussione, tre di esse sono importanti:

  1. pageX/Y forniscono le coordinate relative all'elemento <html> espresse in pixel CSS.
  2. clientX/Y danno le coordinate relative al viewport in pixel CSS.
  3. screenX/Y restituiscono le coordinate relative allo schermo in device pixels.

Utilizzerai pageX/Y il 90% delle volte; di solito ti serve sapere la posizione dell'evento relativamente al documento. Il restante 10% delle volte utilizzerai clientX/Y. Non avrai mai bisogno di conoscere le coordinate di un evento relativamente allo schermo.

Media queries

Per finire, qualche parola sulle media queries. L'idea è molto semplice: puoi definire speciali regole CSS da applicare solo se la larghezza della pagina è maggiore di, uguale a, o minore di un certo valore. Ad esempio:

div.sidebar {
  width: 300px;
}

@media all and (max-width: 400px) {
  // styles assigned when width is smaller than 400px;
  div.sidebar {
    width: 100px;
  }
}

Ora la barra laterale è larga 300px, tranne nel caso in cui la larghezza della pagine è minore di 400px, nel qual caso la barra laterale diventa larga 100px.

La domanda è, ovviamente: quale larghezza stiamo considerando qui?

Esistono due media queries di una certa importanza: width/height e device-width/device-height.

  1. width/height utilizzano gli stessi valori di documentElement.clientWidth/Height (in altre parole, il viewport). Esse lavorano con i pixel CSS.
  2. device-width/device-height utilizzano gli stessi valori di screen.width/height (cioè le dimensioni dello schermo). Esse operano con i device pixels.

Quali dovresti usare? Non c'è dubbio: width, naturalmente. Gli sviluppatori web non sono interessati alla larghezza del dispositivo; ciò che conta è la larghezza della finestra del browser.

Perciò usa width e dimentica device-width — in ambiente desktop. Come vedremo, la situazione è molto più confusa in ambito mobile.

Conclusione

Qui termina la nostra incursione tra i browser desktop. La seconda parte di questa serie trasferisce questi concetti all'ambito mobile evidenziando alcune importanti differenze rispetto all'ambiente desktop.