CSS: come posizionare due elementi uno sopra l'altro, senza specificare un'altezza?


96

Ho due DIV che devo posizionare esattamente uno sopra l'altro. Tuttavia, quando lo faccio, la formattazione viene completamente rovinata perché il DIV contenente si comporta come se non ci fosse altezza. Penso che questo sia il comportamento previsto con, position:absolutema ho bisogno di trovare un modo per posizionare questi due elementi uno sopra l'altro e fare in modo che il contenitore si allunghi mentre il contenuto si estende:

Il bordo superiore sinistro di .layer2deve essere esattamente allineato al bordo superiore sinistro dilayer1

<!-- HTML -->
<div class="container_row">
    <div class="layer1">
        Lorem ipsum...
    </div>
    <div class="layer2">
        More lorem ipsum...
    </div>
</div>
<div class="container_row">
    ...same HTML as above. This one should never overlap the .container_row above.
</div>

/* CSS */
.container_row {}

.layer1 {
    position:absolute;
    z-index: 1;
}

.layer2 {
    position:absolute;
    z-index: 2;
}

1
position: absolutenon è proprio lo stile giusto da usare allora.
BoltClock

sì, position:absolutenon mi sembra giusto. Quale sarebbe lo stile giusto?
Andrew

Gli elementi .layer1e .layer2hanno una dimensione nota?
mu è troppo breve

No, hanno anche una dimensione sconosciuta.
Andrew

Risposte:


81

Prima di tutto, dovresti davvero includere la posizione su elementi assolutamente posizionati o ti imbatterai in comportamenti strani e confusi; probabilmente vuoi aggiungere top: 0; left: 0al CSS per entrambi gli elementi posizionati in modo assoluto. Si potrà anche avere position: relativesu .container_rowse si desidera che gli elementi posizionati in modo assoluto ad essere posizionati rispetto al loro genitore piuttosto che il corpo del documento :

Se l'elemento ha 'position: absolute', il blocco contenitore è stabilito dall'antenato più vicino con una 'position' di 'absolute', 'relative' o 'fixed' ...

Il tuo problema è che position: absoluterimuove elementi dal flusso normale :

Viene completamente rimosso dal flusso normale (non ha alcun impatto sui fratelli successivi). Un box posizionato in modo assoluto stabilisce un nuovo blocco di contenimento per bambini a flusso normale e discendenti in posizione assolutamente (ma non fissa). Tuttavia, il contenuto di un elemento posizionato in modo assoluto non scorre intorno ad altre caselle.

Ciò significa che gli elementi posizionati in modo assoluto non hanno alcun effetto sulla dimensione del loro elemento genitore e il primo <div class="container_row">avrà un'altezza pari a zero.

Quindi non puoi fare quello che stai cercando di fare con elementi posizionati in modo assoluto a meno che tu non sappia quanto saranno alti (o, equivalentemente, puoi specificare la loro altezza). Se puoi specificare le altezze, puoi mettere le stesse altezze su .container_rowe tutto si allineerà; potresti anche mettere una margin-topsul secondo .container_rowper lasciare spazio agli elementi assolutamente posizionati. Per esempio:

http://jsfiddle.net/ambiguous/zVBDc/


Ottima risposta, grazie! Immagino di aver bisogno di capire l'altezza dinamicamente usando javascript. Speravo solo che non sarebbe dovuto arrivare a quello. = [
Andrew

@Andrew: Sì, benvenuto nel sistema di dimensionamento / posizione / allineamento verticale di CSS: se non conosci le dimensioni specifiche delle cose di solito sei sfortunato. Le cose orizzontali sono più facili da gestire ma la maggior parte odora ancora come se fossero state progettate da "stampatori con layout fisso" piuttosto che "persone con layout dinamico".
mu è troppo breve

1
Non è position: absolutenecessario. Controlla: stackoverflow.com/a/51949049/1736186
invece

63

In realtà questo è possibile senza posizione absolutee specificando qualsiasi altezza. Tutto quello che devi fare è usare display: gridsull'elemento genitore e mettere i discendenti, nella stessa riga e colonna.

Controlla l'esempio di seguito, basato sul tuo HTML. Ho aggiunto solo <span>e alcuni colori, così puoi vedere il risultato.

Puoi anche cambiare facilmente z-indexciascuno degli elementi discendenti, per manipolarne la visibilità (quale dovrebbe essere in alto).

.container_row{
  display: grid;
}

.layer1, .layer2{
  grid-column: 1;
  grid-row: 1;
}

.layer1 span{
  color: #fff;
  background: #000cf6;
}

.layer2{
  background: rgba(255, 0, 0, 0.4);
}
<div class="container_row">
    <div class="layer1">
        <span>Lorem ipsum...<br>Test test</span>
    </div>
    <div class="layer2">
        More lorem ipsum...
    </div>
</div>
<div class="container_row">
    ...same HTML as above. This one should never overlap the .container_row above.
</div>


11
Bello. Ma per essere onesti, display: gridnon esisteva sette anni fa :)
mu è troppo breve

C'è un'altra risposta di seguito, scritta da @Cornelius. Usa flex, che ha un supporto browser molto migliore al giorno d'oggi.
Oleg Cherr

20

Ottima risposta, "mu è troppo corto". Cercavo esattamente la stessa cosa e dopo aver letto il tuo post ho trovato una soluzione che si adattava al mio problema.

Avevo due elementi della stessa identica dimensione e volevo impilarli. Poiché ognuno ha la stessa dimensione, quello che potevo fare era farlo

position: absolute;
top: 0px;
left: 0px;

solo sull'ultimo elemento. In questo modo il primo elemento viene inserito correttamente, "spingendo" l'altezza dei genitori, e il secondo elemento viene posizionato sopra.

Spera che questo aiuti altre persone che cercano di impilare 2+ elementi con la stessa altezza (sconosciuta).


1
NB: Con questo approccio, dovrai comunque sapere quale elemento è quello più alto, in modo da poterlo fare con position: aboslutel' altro .
Alec

10

Ecco un'altra soluzione che utilizza display: flex invece di position: absolute o display: grid.

.container_row{
  display: flex;
}

.layer1 {
  width: 100%;
  background-color: rgba(255,0,0,0.5);  // red
}

.layer2{
  width: 100%;
  margin-left: -100%;
  background-color: rgba(0,0,255,0.5);  // blue
}
<div class="container_row">
    <div class="layer1">
        <span>Lorem ipsum...</span>
    </div>
    <div class="layer2">
        More lorem ipsum...
    </div>
</div>
<div class="container_row">
    ...same HTML as above. This one should never overlap the .container_row above.
</div>


Ottima soluzione. Grazie!
Oleg Cherr

5

Ho dovuto impostare

Container_height = Element1_height = Element2_height

.Container {
    position: relative;
}

.ElementOne, .Container ,.ElementTwo{
    width: 283px;
    height: 71px;
}

.ElementOne {
    position:absolute;
}

.ElementTwo{
    position:absolute;
}

L'utente può utilizzare z-index per impostare quale deve essere in primo piano.


4

Ecco alcuni CSS riutilizzabili che preserveranno l'altezza di ogni elemento senza utilizzare position: absolute:

.stack {
    display: grid;
}
.stack > * {
    grid-row: 1;
    grid-column: 1;
}

Il primo elemento nel tuo stackè lo sfondo e il secondo è il primo piano.


2

A causa del posizionamento assoluto che rimuove gli elementi dalla posizione del flusso di documenti: Absolute non è lo strumento giusto per il lavoro. A seconda del layout esatto che desideri creare, avrai successo utilizzando margini negativi, position: relative o forse anche transform: translate. Mostraci un esempio di quello che vuoi fare, possiamo aiutarti meglio.


1

Ovviamente, il problema sta tutto nel recuperare la tua altezza. Ma come puoi farlo se non conosci l'altezza in anticipo? Bene, se sai quale rapporto di aspetto vuoi dare al contenitore (e mantenerlo reattivo), puoi ripristinare la tua altezza aggiungendo il riempimento a un altro figlio del contenitore, espresso in percentuale.

Puoi anche aggiungere un manichino dival contenitore e impostare qualcosa come padding-top: 56.25%dare all'elemento fittizio un'altezza che è una proporzione della larghezza del contenitore. Questo spingerà fuori il contenitore e gli darà un rapporto di aspetto, in questo caso 16: 9 (56,25%).

Il riempimento e il margine usano la percentuale della larghezza, questo è davvero il trucco qui.


0

Dopo molti test, ho verificato che la domanda originale fosse già corretta; mancano solo un paio di impostazioni:

  • il container_rowDEVE avereposition: relative;
  • i bambini (...), DEVONO avere position: absolute; left:0;
  • per assicurarsi che i bambini (...) siano allineati esattamente l'uno sull'altro, container_rowdovrebbero avere uno stile aggiuntivo:
    • height:x; line-height:x; vertical-align:middle;
    • text-align:center; potrebbe anche aiutare.
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.