Layout in muratura solo CSS


134

Devo implementare un layout in muratura abbastanza scarso. Tuttavia, per una serie di motivi non voglio usare JavaScript per farlo.

Una griglia di più colonne di rettangoli di altezza variabile.

parametri:

  • Tutti gli elementi hanno la stessa larghezza
  • Gli elementi hanno un'altezza che non può essere calcolata sul lato server (un'immagine più varie quantità di testo)
  • Posso vivere con un numero fisso di colonne se devo

c'è una soluzione banale a questo che funziona nei browser moderni, la column-countproprietà.

Il problema con quella soluzione è che gli elementi sono ordinati in colonne:

A partire dalla casella più in alto a sinistra, sono numerate da 1 a 4 in basso, la casella più in alto nella colonna successiva è 5 e così via.

Mentre ho bisogno che gli elementi siano ordinati in file, almeno approssimativamente:

A partire dalla casella in alto a sinistra, sono numerate da 1 a 6 diritte, ma poiché la casella 5 è la più corta la casella sottostante è 7 in quanto ha l'apparenza di essere su una riga più in alto della casella successiva all'estrema sinistra.

Approcci che ho provato che non funzionano:

Ora potrei cambiare il rendering lato server e riordinare gli elementi che dividono il numero di elementi per il numero di colonne, ma è complicato, soggetto a errori (basato su come i browser decidono di dividere l'elenco degli elementi in colonne), quindi mi piacerebbe per evitarlo se possibile.

C'è qualche magia flexbox nuova che lo rende possibile?


7
Non riesco a pensare a un modo che non dipende da altezze predefinite. Se riconsidera JS, dai un'occhiata a stackoverflow.com/questions/13518653/… dove implemento una soluzione del genere che è abbastanza semplice.
Gabriele Petrioli,

1
@Gaby implementa la tua soluzione adesso, grazie!
Pekka,

4
Mi rendo conto che hai detto solo CSS. Voglio solo menzionare che Masonry non richiede più jQuery - la libreria minimizzata è sotto 8kb - e può essere inizializzata solo con HTML. Solo per riferimento jsfiddle.net/wp7kuk1t
I haz kode

1
Se riesci a determinare l'altezza degli elementi in anticipo, conoscendo l'altezza della linea, la dimensione del carattere (dovresti servire un carattere specifico e fare alcuni calcoli intelligenti), l'altezza dell'immagine, il margine verticale e il riempimento, puoi Fai questo. Altrimenti, non puoi farlo usando solo CSS. Potresti anche usare qualcosa come PhantomJS per eseguire il pre-rendering di ogni elemento e ottenere l'altezza di quell'elemento, ma ci sarebbe un significativo sovraccarico / latenza aggiunta.
Timothy Zorn,

Risposte:


157

Flexbox

Un layout dinamico in muratura non è possibile con flexbox, almeno non in modo pulito ed efficiente.

Flexbox è un sistema di layout monodimensionale. Ciò significa che può allineare gli oggetti lungo linee orizzontali o verticali. Un oggetto flessibile è limitato alla sua riga o colonna.

Un vero sistema a griglia è bidimensionale, il che significa che può allineare gli oggetti lungo linee orizzontali E verticali. Gli elementi di contenuto possono estendersi su più righe e colonne contemporaneamente, cosa che gli elementi flessibili non possono fare.

Questo è il motivo per cui flexbox ha una capacità limitata per la costruzione di reti. È anche un motivo per cui il W3C ha sviluppato un'altra tecnologia CSS3, Grid Layout .


row wrap

In un contenitore flessibile con flex-flow: row wrap, gli articoli flessibili devono avvolgere in nuove file .

Ciò significa che un oggetto flessibile non può avvolgere sotto un altro oggetto nella stessa riga .

Nota sopra come div # 3 si avvolge sotto div # 1 , creando una nuova riga. Non può avvolgere al di sotto del div # 2 .

Di conseguenza, quando gli oggetti non sono i più alti della riga, rimane spazio bianco, creando vuoti sgradevoli.


column wrap

Se si passa a flex-flow: column wrap, un layout simile a una griglia è più raggiungibile. Tuttavia, un contenitore in direzione di colonna presenta quattro potenziali problemi fin dall'inizio:

  1. Gli oggetti flessibili scorrono verticalmente, non orizzontalmente (come è necessario in questo caso).
  2. Il contenitore si espande in orizzontale, non in verticale (come il layout Pinterest).
  3. Richiede che il contenitore abbia un'altezza fissa, quindi gli articoli sanno dove avvolgere.
  4. Al momento della stesura di questo documento, presenta una carenza in tutti i principali browser in cui il contenitore non si espande per contenere colonne aggiuntive .

Di conseguenza, un contenitore con direzione colonna non è un'opzione in questo caso e in molti altri casi.


Griglia CSS con dimensioni oggetto non definite

Il layout della griglia sarebbe una soluzione perfetta al tuo problema se le varie altezze degli elementi di contenuto potessero essere predeterminate . Tutti gli altri requisiti rientrano nella capacità di Grid.

La larghezza e l'altezza degli elementi della griglia devono essere noti per colmare le lacune con gli elementi circostanti.

Quindi Grid, che è il miglior CSS che ha da offrire per costruire un layout in muratura a scorrimento orizzontale, in questo caso non è all'altezza.

In effetti, fino a quando una tecnologia CSS arriva con la capacità di colmare automaticamente le lacune, i CSS in generale non hanno soluzione. Qualcosa del genere richiederebbe probabilmente di ridisegnare il documento, quindi non sono sicuro di quanto sia utile o efficiente.

Avrai bisogno di una sceneggiatura.

Le soluzioni JavaScript tendono a utilizzare il posizionamento assoluto, che rimuove gli elementi di contenuto dal flusso di documenti al fine di riorganizzarli senza lacune. Ecco due esempi:


Griglia CSS con dimensioni dell'elemento definite

Per i layout in cui sono noti la larghezza e l'altezza degli elementi di contenuto, ecco un layout in muratura a scorrimento orizzontale in puro CSS:

grid-container {
  display: grid;                                                /* 1 */
  grid-auto-rows: 50px;                                         /* 2 */
  grid-gap: 10px;                                               /* 3 */
  grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));   /* 4 */
}

[short] {
  grid-row: span 1;                                             /* 5 */
  background-color: green;
}

[tall] {
  grid-row: span 2;
  background-color: crimson;
}

[taller] {
  grid-row: span 3;
  background-color: blue;
}

[tallest] {
  grid-row: span 4;
  background-color: gray;
}

grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3em;
  font-weight: bold;
  color: white;
}
<grid-container>
  <grid-item short>01</grid-item>
  <grid-item short>02</grid-item>
  <grid-item tall>03</grid-item>
  <grid-item tall>04</grid-item>
  <grid-item short>05</grid-item>
  <grid-item taller>06</grid-item>
  <grid-item short>07</grid-item>
  <grid-item tallest>08</grid-item>
  <grid-item tall>09</grid-item>
  <grid-item short>10</grid-item>
  <grid-item tallest>etc.</grid-item>
  <grid-item tall></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
</grid-container>

Demo jsFiddle


Come funziona

  1. Stabilire un contenitore della griglia a livello di blocco. ( inline-gridsarebbe l'altra opzione)
  2. La grid-auto-rowsproprietà imposta l'altezza delle righe generate automaticamente. In questa griglia ogni riga è alta 50 px.
  3. La grid-gapproprietà è una scorciatoia per grid-column-gape grid-row-gap. Questa regola imposta un divario di 10px tra gli elementi della griglia. (Non si applica all'area tra gli oggetti e il contenitore.)
  4. La grid-template-columnsproprietà imposta la larghezza delle colonne definite in modo esplicito.

    La repeatnotazione definisce un modello di colonne (o righe) ripetute.

    La auto-fillfunzione dice alla griglia di allineare quante più colonne (o righe) possibile senza traboccare il contenitore. (Questo può creare un comportamento simile a quello del layout flessibile flex-wrap: wrap.)

    La minmax()funzione imposta un intervallo di dimensioni minimo e massimo per ogni colonna (o riga). Nel codice sopra, la larghezza di ogni colonna sarà un minimo del 30% del contenitore e il massimo di tutto lo spazio libero disponibile.

    L' frunità rappresenta una frazione dello spazio libero nel contenitore della griglia. È paragonabile alla flex-growproprietà di flexbox .

  5. Con grid-rowe spanstiamo dicendo agli elementi della griglia quante righe devono attraversare.


Supporto browser per griglia CSS

  • Chrome: supporto completo a partire dall'8 marzo 2017 (versione 57)
  • Firefox: supporto completo a partire dal 6 marzo 2017 (versione 52)
  • Safari: supporto completo a partire dal 26 marzo 2017 (versione 10.1)
  • Edge: supporto completo a partire dal 16 ottobre 2017 (versione 16)
  • IE11 - nessun supporto per le specifiche attuali; supporta la versione obsoleta

Ecco il quadro completo: http://caniuse.com/#search=grid


Fantastica funzione di sovrapposizione della griglia in Firefox

Negli strumenti di sviluppo di Firefox, quando si ispeziona il contenitore della griglia, nella dichiarazione CSS è presente una piccola icona a griglia. Al clic, mostra un contorno della griglia sulla pagina.

Maggiori dettagli qui: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts


5
Risposta fantastica! Con la soluzione "Griglia CSS con dimensioni oggetto definite", è possibile esprimere l'altezza di una cella come percentuale della larghezza della cella? Ciò sarebbe utile per la visualizzazione di immagini, in cui è noto il rapporto di formato. Vogliamo mantenere le proporzioni in ogni momento.
Oliver Joseph Ash

Grazie. Per quanto riguarda il problema delle proporzioni per le immagini, il metodo della "percentuale di larghezza della cella" (il trucco della percentuale di riempimento?), Non è affidabile in Grid, o Flexbox, del resto. Vedi qui e qui . @OliverJosephAsh
Michael Benjamin,

Sì, mi riferisco al trucco del riempimento percentuale. È possibile utilizzare una delle soluzioni alternative in combinazione con la soluzione di layout in muratura? Per il contesto, stiamo cercando di implementare un layout in muratura solo CSS per le immagini su unsplash.com .
Oliver Joseph Ash

1
Sfortunatamente usare JS per questo non è un'opzione. Vogliamo abilitare il rendering lato server per prestazioni e motivi SEO. Ciò significa che il layout deve essere reso prima che JavaScript sul lato client sia stato scaricato, analizzato ed eseguito. Dato che questo sembra impossibile, immagino che dovremo fare uno scambio da qualche parte lungo la linea! Grazie per l'aiuto :-)
Oliver Joseph Ash

1
Aggiornamento delle specifiche: in flexbox, i margini percentuali e le imbottiture sono ora impostati per risolvere nuovamente le dimensioni in linea del contenitore. drafts.csswg.org/css-flexbox/#item-margins @OliverJosephAsh
Michael Benjamin

13

Questa è una tecnica recentemente scoperta che coinvolge flexbox: https://tobiasahlin.com/blog/masonry-with-css/ .

L'articolo ha senso per me, ma non ho provato ad usarlo, quindi non so se ci sono avvertimenti, a parte quello menzionato nella risposta di Michael.

Ecco un esempio dell'articolo, facendo uso della orderproprietà, combinato con :nth-child.

Stack snippet

.container {
  display: flex;
  flex-flow: column wrap;
  align-content: space-between;
  /* Your container needs a fixed height, and it 
   * needs to be taller than your tallest column. */
  height: 960px;
  
  /* Optional */
  background-color: #f7f7f7;
  border-radius: 3px;
  padding: 20px;
  width: 60%;
  margin: 40px auto;
  counter-reset: items;
}

.item {
  width: 24%;
  /* Optional */
  position: relative;
  margin-bottom: 2%;
  border-radius: 3px;
  background-color: #a1cbfa;
  border: 1px solid #4290e2;
  box-shadow: 0 2px 2px rgba(0,90,250,0.05),
    0 4px 4px rgba(0,90,250,0.05),
    0 8px 8px rgba(0,90,250,0.05),
    0 16px 16px rgba(0,90,250,0.05);
  color: #fff;
  padding: 15px;
  box-sizing: border-box;
}

 /* Just to print out numbers */
div.item::before {
  counter-increment: items;
  content: counter(items);
}

/* Re-order items into 3 rows */
.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n)   { order: 4; }

/* Force new columns */
.break {
  flex-basis: 100%;
  width: 0;
  border: 1px solid #ddd;
  margin: 0;
  content: "";
  padding: 0;
}

body { font-family: sans-serif; }
h3 { text-align: center; }
<div class="container">
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  
  <span class="item break"></span>
  <span class="item break"></span>
  <span class="item break"></span>
</div>


Prima di tutto, solo le risposte al link sono cattive, come quando muore tale link, così fa la risposta. In secondo luogo, se leggi la risposta fornita più attentamente, scoprirai che cosa è menzionato nel tuo link è coperto. In poche parole, ci sono troppe limitazioni usando Flexbox da solo, a meno che ciò che è menzionato sopra non sia un problema.
Ason,

3
Ho letto molto attentamente la risposta accettata, vedrai anche alcuni commenti che ho aggiunto in fondo. L'approccio flexbox a cui ho collegato è unico e non coperto da quella risposta. Non voglio ripetere l'articolo, perché è molto complicato e l'articolo fa un ottimo lavoro nel coprirlo.
Oliver Joseph Ash,

L'unica cosa che il link fa diversamente è usare la orderproprietà, facendola apparire come se gli oggetti scorressero da sinistra a destra. Tuttavia, il suo trucco principale è flex-flow: column wrap, che è menzionato nella risposta sopra. Inoltre, non può far fluire gli oggetti come fa una vera muratura, ad esempio se il 3o e il 4o oggetto si adattassero alla 3a colonna, non lo farebbero quelli collegati. Ma, come ho già detto, tutto dipende da quali sono i requisiti e, in questo caso (questa domanda), non funzionerà come richiesto.
Ason,

Inoltre, non si tratta di "non voler ripetere l'articolo" , una risposta dovrebbe contenere la parte essenziale del codice, quindi sarà comunque valida quando la risorsa collegata muore.
Ason,

3
L'ho aggiornato, ho aggiunto un campione dall'articolo + un voto.
Ason,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.