CSS: Comment positionner deux éléments l'un sur l'autre, sans spécifier de hauteur?

96

J'ai deux DIV que je dois positionner exactement l'un sur l'autre. Cependant, quand je fais cela, le formatage est complètement foiré parce que le DIV contenant agit comme s'il n'y avait pas de hauteur. Je pense que c'est le comportement attendu avec, position:absolutemais je dois trouver un moyen de positionner ces deux éléments l'un sur l'autre et d'étirer le conteneur au fur et à mesure que le contenu s'étire:

Le bord supérieur gauche de .layer2doit être exactement aligné sur le bord supérieur gauche delayer1

<!-- 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;
}
Andrew
la source
1
position: absoluten'est pas tout à fait le bon style à utiliser alors.
BoltClock
ouais, position:absoluteça ne va pas. Quel serait le bon style?
Andrew
Les éléments .layer1et .layer2ont-ils une taille connue?
mu est trop court le
Non, ils ont également une taille inconnue.
Andrew

Réponses:

81

Tout d'abord, vous devriez vraiment inclure la position sur des éléments absolument positionnés ou vous rencontrerez un comportement étrange et déroutant; vous souhaitez probablement ajouter top: 0; left: 0au CSS pour vos deux éléments absolument positionnés. Vous voudrez également avoir position: relativesur .container_rowsi vous voulez que les éléments positionnés de manière absolue soient positionnés par rapport à leur parent plutôt qu'au corps du document :

Si l'élément a 'position: absolute', le bloc contenant est établi par l'ancêtre le plus proche avec une 'position' de 'absolu', 'relatif' ou 'fixe' ...

Votre problème est que position: absolutesupprime des éléments du flux normal :

Il est entièrement supprimé du flux normal (il n'a aucun impact sur les frères et sœurs ultérieurs). Une boîte positionnée de manière absolue établit un nouveau bloc contenant pour les enfants de flux normal et les descendants positionnés de manière absolue (mais non fixe). Cependant, le contenu d'un élément positionné de manière absolue ne circule dans aucune autre boîte.

Cela signifie que les éléments positionnés de manière absolue n'ont aucun effet sur la taille de leur élément parent et votre premier <div class="container_row">aura une hauteur de zéro.

Vous ne pouvez donc pas faire ce que vous essayez de faire avec des éléments positionnés de manière absolue à moins de savoir quelle sera leur hauteur (ou, de manière équivalente, vous pouvez spécifier leur hauteur). Si vous pouvez spécifier les hauteurs, vous pouvez mettre les mêmes hauteurs sur le .container_rowet tout s'alignera; vous pouvez également mettre un margin-topsur le second .container_rowpour laisser de la place aux éléments absolument positionnés. Par exemple:

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

mu est trop court
la source
Excellente réponse, merci! Je suppose que je devrai peut-être déterminer la hauteur de manière dynamique en utilisant javascript. J'espérais juste qu'il ne faudrait pas en venir là. = [
Andrew
@Andrew: Oui, bienvenue dans le système de dimensionnement / position / alignement vertical de CSS: si vous ne connaissez pas la taille spécifique des choses, vous n'avez généralement pas de chance. Le truc horizontal est plus facile à gérer, mais la plupart sent encore comme s'il avait été conçu par des "gens de l'imprimerie à mise en page fixe" plutôt que par des "gens de la mise en page dynamique".
mu est trop court le
1
Pas position: absolutebesoin. Veuillez vérifier: stackoverflow.com/a/51949049/1736186
au lieu du
63

En fait, cela est possible sans position absoluteet en spécifiant n'importe quelle hauteur. Tout ce que vous devez faire est de l'utiliser display: gridsur l'élément parent et de placer les descendants dans la même ligne et la même colonne.

Veuillez vérifier l'exemple ci-dessous, basé sur votre HTML. J'ai ajouté seulement <span>et quelques couleurs, donc vous pouvez voir le résultat.

Vous pouvez également changer facilement z-indexchacun des éléments descendants, pour manipuler sa visibilité (lequel devrait être en haut).

.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>

au lieu
la source
11
Agréable. Mais pour être honnête, display: gridn'existait pas il y a sept ans :)
mu est trop court
Il y a une autre réponse ci-dessous, écrite par @Cornelius. Il utilise flex, ce qui a un meilleur support de navigateur de nos jours.
Oleg Cherr
20

Excellente réponse, "mu est trop court". Je cherchais exactement la même chose, et après avoir lu votre message, j'ai trouvé une solution qui correspondait à mon problème.

J'avais deux éléments exactement de la même taille et je voulais les empiler. Comme chacun a la même taille, ce que je pourrais faire était de faire

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

sur seulement le dernier élément. De cette façon, le premier élément est inséré correctement, "poussant" la hauteur des parents, et le deuxième élément est placé sur le dessus.

J'espère que cela aidera d'autres personnes qui essaient d'empiler plus de 2 éléments avec la même hauteur (inconnue).

Kraenhansen
la source
1
NB: Avec cette approche, vous aurez toujours besoin de savoir quel élément est le plus grand, afin de pouvoir utiliser position: aboslutel' autre .
Alec
10

Voici une autre solution utilisant display: flex au lieu de position: absolute ou 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>

Cornélius
la source
Excellente solution. Merci!
Oleg Cherr le
5

Je devais régler

Container_height = Element1_height = Élément2_height

.Container {
    position: relative;
}

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

.ElementOne {
    position:absolute;
}

.ElementTwo{
    position:absolute;
}

Utilisez peut utiliser z-index pour définir lequel être en haut.

Arun Prasad ES
la source
4

Voici quelques css réutilisables qui conserveront la hauteur de chaque élément sans utiliser position: absolute:

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

Le premier élément de votre stackest l'arrière-plan et le second est le premier plan.

Ken Mueller
la source
2

En raison du positionnement absolu, supprimant les éléments de la position du flux de documents: absolu n'est pas le bon outil pour le travail. En fonction de la mise en page exacte que vous souhaitez créer, vous réussirez en utilisant des marges négatives, position: relative ou peut-être même transformer: translate. Montrez-nous un échantillon de ce que vous voulez faire, nous pouvons mieux vous aider.

Luiz Sócrate
la source
1

Bien sûr, le problème est de retrouver votre taille. Mais comment pouvez-vous faire cela si vous ne connaissez pas la hauteur à l'avance? Eh bien, si vous savez quel rapport hauteur / largeur vous souhaitez donner au conteneur (et le garder réactif), vous pouvez récupérer votre hauteur en ajoutant un rembourrage à un autre enfant du conteneur, exprimé en pourcentage.

Vous pouvez même ajouter un mannequin divau conteneur et définir quelque chose comme padding-top: 56.25%donner à l'élément factice une hauteur proportionnelle à la largeur du conteneur. Cela poussera le conteneur et lui donnera un rapport hauteur / largeur, dans ce cas 16: 9 (56,25%).

Le remplissage et la marge utilisent le pourcentage de la largeur, c'est vraiment le truc ici.

Luke Puplett
la source
0

Après de nombreux tests, j'ai vérifié que la question initiale était déjà correcte; il manque juste quelques paramètres:

  • le container_rowDOIT avoirposition: relative;
  • les enfants (...), DOIVENT avoir position: absolute; left:0;
  • pour vous assurer que les enfants (...) s'alignent exactement les uns sur les autres, le container_rowdevrait avoir un style supplémentaire:
    • height:x; line-height:x; vertical-align:middle;
    • text-align:center; pourrait, aussi, aider.
justaguest
la source