Enfant avec hauteur max: 100% parent déborde


139

J'essaie de comprendre ce qui me semble être un comportement inattendu:

J'ai un élément avec une hauteur maximale de 100% à l'intérieur d'un conteneur qui utilise également une hauteur maximale mais, de manière inattendue, l'enfant déborde le parent:

Cas de test: http://jsfiddle.net/bq4Wu/16/

.container {  
    background: blue; 
    padding: 10px; 
    max-height: 200px; 
    max-width: 200px; 
}

img { 
    display: block;
    max-height: 100%; 
    max-width: 100%; 
}

Ceci est corrigé, cependant, si le parent reçoit une hauteur explicite:

Cas de test: http://jsfiddle.net/bq4Wu/17/

.container {  
    height: 200px;
}

Est-ce que quelqu'un sait pourquoi l'enfant n'honorerait pas la hauteur maximale de son parent dans le premier exemple? Pourquoi une hauteur explicite est-elle requise?

Réponses:


209

Lorsque vous spécifiez un pourcentage pour max-heightun enfant, il s'agit d'un pourcentage de la taille réelle du parent, et non de celle du parent max-height, assez curieusement . La même chose s'applique à max-width.

Ainsi, lorsque vous ne spécifiez pas de hauteur explicite sur le parent, il n'y a pas de hauteur de base à partir de laquelle l'enfant max-heightdoit être calculé, donc max-heightcalcule à none, permettant à l'enfant d'être aussi grand que possible. La seule autre contrainte agissant sur l'enfant maintenant est celle max-widthde son parent, et comme l'image elle-même est plus haute que large, elle déborde la hauteur du conteneur vers le bas, afin de maintenir son rapport hauteur / largeur tout en étant globalement aussi grand que possible.

Lorsque vous faites spécifier une hauteur explicite pour le parent, l'enfant sait qu'il doit être au plus 100% de cette hauteur explicite. Cela lui permet d'être contraint à la hauteur du parent (tout en conservant son rapport hauteur / largeur).


4
Ceci est une explication utile, en particulier mélangée avec l'entrée de mon ami: "max-height: 100% (enfant) de max-height: 100% (parent) = 0 - c'est pourquoi votre enfant n'honore pas sa valeur parent".
iamkeir

8
Je ne sais pas quelle réponse accepter - celle-ci répond au «pourquoi».
iamkeir

J'ai seulement répondu au «pourquoi» parce que je ne suis pas sûr de votre objectif car il n'est pas explicitement mentionné. Peut-être que si vous développez là-dessus, je peux y répondre.
BoltClock

Accepté cela car il répond à ma question réelle, merci. Veuillez noter que la réponse de @ Daniel ci-dessous offre un correctif.
iamkeir

Dans mon cas, cela a display: flex; flex-direction: columnrésolu le problème et ajouté la barre de défilement que je voulais.
xdevs23

73

J'ai joué un peu. Sur une image plus grande dans Firefox, j'ai obtenu un bon résultat en utilisant la valeur de la propriété inherit. Cela vous aidera-t-il?

.container {
    background: blue;
    padding: 10px;
    max-height: 100px;
    max-width: 100px;
    text-align:center;
}

img {
    max-height: inherit;
    max-width: inherit;
}

Je ne sais pas quelle réponse accepter - celle-ci répond au «correctif».
iamkeir

Accepté @BoltClock pour avoir répondu au pourquoi, mais merci d'avoir proposé une solution.
iamkeir

1
En plus de @BoltClock, ma réponse est exactement ce qu'il dit. CSS indique simplement à l'ordinateur comment gérer un élément, mais après les calculs, il aura délimité toutes les valeurs jusqu'aux valeurs de base nécessaires pour le rendu, comme la hauteur, la largeur, la couleur, les coordonnées, ... La propriété inherit indique à l'img de prenez la valeur "calculée" du parent. Ainsi, vous obtenez les valeurs calculées pour la hauteur et la largeur dans vos propriétés css max-height et max-width.
Daniel

2
Cela ne fonctionne pas quand max:height: 100%au lieu d'une hauteur fixe: jsfiddle.net/umbreak/n6czk9xt/1
Didac Montero

2
Je ne peux pas déclarer la largeur et la hauteur comme fixes car ma mise en page est réactive dispaly: flex;. J'ai ouvert un nouveau fil: j'ai ouvert un nouveau fil: stackoverflow.com/questions/29732391/... Cependant, dans le pire des cas, je pourrais utiliser Jquery pour récupérer la largeur et la hauteur réelles de l'img et les définir dans le div de l'image parente.
Didac Montero

7

J'ai trouvé une solution ici: http://www.sitepoint.com/maintain-image-aspect-ratios-responsive-web-design/

L'astuce est possible car il existe une relation entre WIDTH et PADDING-BOTTOM d'un élément. Alors:

parent:

container {
  height: 0;
  padding-bottom: 66%; /* for a 4:3 container size */
  }

enfant ( supprimez tous les css liés à la largeur , c'est-à-dire largeur: 100%):

img {
  max-height: 100%;
  max-width: 100%;
  position: absolute;     
  display:block;
  margin:0 auto; /* center */
  left:0;        /* center */
  right:0;       /* center */
  }

5

Vous pouvez utiliser la propriété object-fit

.cover {
    object-fit: cover;
    width: 150px;
    height: 100px;
}

Comme suggéré ici

Une explication complète de cette propriété par Chris Mills dans Dev.Opera

Et un encore meilleur dans CSS-Tricks

Il est pris en charge dans

  • Chrome 31+
  • Safari 7.1+
  • Firefox 36+
  • Opera 26+
  • Android 4.4.4 ou version ultérieure
  • iOS 8+

Je viens de vérifier que le vivaldi et le chrome le supportent également (pas de surprise ici)

Il n'est actuellement pas pris en charge sur IE, mais ... qui s'en soucie ? En outre, iOS prend en charge l'ajustement d'objet, mais pas la position d'objet, mais ce sera bientôt le cas.


Belle alternative! J'imagine que flex-box pourrait également aider, si une dégradation gracieuse dans IE est acceptable.
iamkeir

3

Voici une solution pour une question récemment ouverte marquée comme un double de cette question. La <img>balise dépassait la hauteur maximale du parent <div>.

Cassé: violon

Travail: violon

Dans ce cas, ajouter display:flexaux 2 <div>balises parents était la réponse


3

Au lieu d'aller avec max-height: 100% / 100%, une approche alternative consistant à remplir tout l'espace serait d'utiliser position: absoluteavec haut / bas / gauche / droite réglé sur 0.

En d'autres termes, le HTML ressemblerait à ceci:

<div class="flex-content">
  <div class="scrollable-content-wrapper">
    <div class="scrollable-content">
      1, 2, 3
    </div>
   </div>
</div>
.flex-content {
  flex-grow: 1;
  position: relative;
  width: 100%;
  height: 100%;
}

.scrollable-content-wrapper {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  overflow: auto;
}

.scrollable-content {
}

Vous pouvez voir un exemple en direct sur Codesandbox - Overflow dans CSS Flex / Grid


2

Peut-être que quelqu'un d'autre peut expliquer les raisons de votre problème, mais vous pouvez le résoudre en spécifiant la hauteur du conteneur, puis en définissant la hauteur de l'image sur 100%. Il est important que le widthde l'image apparaisse avant le height.

<html>
    <head>
        <style>
            .container {  
                background: blue; 
                padding: 10px;
                height: 100%;
                max-height: 200px; 
                max-width: 300px; 
            }

            .container img {
                width: 100%;
                height: 100%
            }
        </style>
    </head>
    <body>
        <div class="container">
            <img src="http://placekitten.com/400/500" />
        </div>
    </body>
</html>

S'ils veulent que le conteneur devienne plus petit si l'image est plus petite que 200x200, alors oui, cela doit être fait avec min-height / max-height.
cimmanon

2

Le plus proche que je puisse obtenir est cet exemple:

http://jsfiddle.net/YRFJQ/1/

ou

.container {  
  background: blue; 
  border: 10px solid blue; 
  max-height: 200px; 
  max-width: 200px; 
  overflow:hidden;
  box-sizing:border-box;
}

img { 
  display: block;
  max-height: 100%; 
  max-width: 100%; 
}

Le problème principal est que la hauteur prend le pourcentage de la hauteur du conteneur, il recherche donc une hauteur explicitement définie dans le conteneur parent, et non sa hauteur maximale.

Le seul moyen de contourner cela dans une certaine mesure est le violon ci-dessus où vous pouvez masquer le débordement, mais le rembourrage agit toujours comme un espace visible dans lequel l'image coulera, et le remplacement par une bordure solide fonctionne à la place (et ensuite ajouter une zone de bordure pour en faire 200px si c'est la largeur dont vous avez besoin)

Je ne sais pas si cela correspondrait à ce pour quoi vous en avez besoin, mais le mieux que je puisse sembler arriver.


Malheureusement, recadrer l'image n'est pas idéal. Merci tout de même pour ta contribution.
iamkeir

1

Une bonne solution est de ne pas utiliser la hauteur sur le parent et de l'utiliser uniquement sur l'enfant avec View Port :

Exemple de violon: https://jsfiddle.net/voan3v13/1/

body, html {
  width: 100%;
  height: 100%;
}
.parent {
  width: 400px;
  background: green;
}

.child {
  max-height: 40vh;
  background: blue;
  overflow-y: scroll;
}

1

Les conteneurs emballent déjà bien leur contenu. Cela ne fonctionne souvent pas aussi bien dans l'autre sens: les enfants ne remplissent pas bien leurs ancêtres. Donc, définissez vos valeurs de largeur / hauteur sur l'élément le plus à l'intérieur plutôt que sur l'élément le plus à l'extérieur, et laissez les éléments extérieurs envelopper leur contenu.

.container {
    background: blue;
    padding: 10px;
}

img {
    display: block;
    max-height: 200px;
    max-width: 200px;
}

Belle prise, j'aime ça.
iamkeir

0

Votre conteneur n'a pas de hauteur.

Ajouter une hauteur: 200px;

aux conteneurs css et le minou restera à l'intérieur.


Merci pour votre contribution, mais je l'ai identifié dans l'OP.
iamkeir
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.