CSS: définition de la largeur / hauteur en pourcentage moins pixels

479

J'essaie de créer des classes CSS réutilisables pour plus de cohérence et moins d'encombrement sur mon site, et je suis coincé à essayer de normaliser une chose que j'utilise fréquemment.

J'ai un conteneur pour <div>lequel je ne veux pas définir la hauteur (car il variera en fonction de l'endroit où il se trouve sur le site), et à l'intérieur, il y a un en-tête <div>, puis une liste non ordonnée d'éléments, le tout avec CSS appliqué à leur.

Cela ressemble beaucoup à ceci:

Widget

Je veux que la liste non ordonnée occupe l'espace restant dans le conteneur <div>, sachant que l'en-tête <div>est 18pxgrand. Je ne sais tout simplement pas comment spécifier la hauteur de la liste comme "le résultat de 100%moins 18px".

J'ai vu cette question posée dans quelques autres contextes sur SO, mais j'ai pensé qu'il valait la peine de se poser à nouveau pour mon cas particulier. Quelqu'un at-il des conseils dans cette situation?

MegaMatt
la source
6
Définir une marge? __
kennytm
@KennyTM, je suppose que vous proposez de mettre une marge sur ma liste non ordonnée de, disons, 17px. Mais cela ne fait que pousser toute la liste vers le bas; cela ne le fait pas rétrécir pour rester dans le récipient. Essentiellement, sa hauteur actuelle est maintenue, mais elle est juste abaissée de 17 pixels. Cela ne résout pas mon problème, mais je pense que c'est un pas dans la bonne direction car j'ai vu d'autres approches en ligne qui utilisaient cette technique.
MegaMatt
Je viens de résoudre ce problème dans ma question que j'ai postée ici. stackoverflow.com/a/10420387/340947
Steven Lu

Réponses:

895

Je me rends compte que c'est un ancien article, mais étant donné qu'il n'a pas été suggéré, il convient de mentionner que si vous écrivez pour des navigateurs compatibles CSS3, vous pouvez utiliser calc:

height: calc(100% - 18px);

Il vaut la peine de noter que tous les navigateurs ne prennent pas actuellement en charge la fonction standard CSS3 calc (), donc l'implémentation des versions spécifiques au navigateur de la fonction peut être requise comme suit:

/* Firefox */
height: -moz-calc(100% - 18px);
/* WebKit */
height: -webkit-calc(100% - 18px);
/* Opera */
height: -o-calc(100% - 18px);
/* Standard */
height: calc(100% - 18px);
Levi Botelho
la source
5
Oui, c'est normal pour le moment.
Levi Botelho
1
Fonctionne pour moi dans IE10 et Chrome 27. Vous monsieur, êtes mon héros flippant!
BrainSlugs83
3
@LeviBotelho Mon mauvais. N'avait pas d'espace autour de l'opérateur.
utilisateur
20
De plus, si vous utilisez Less, vous feriez mieux de compiler un fichier avec lessc --strict-math=onmoins afin de ne pas évaluer l'expression à l'intérieur de calc- juste un problème auquel j'ai été confronté et j'ai passé beaucoup de temps (à partir de Less 2.0.0)
Dmitry Wojciechowski
34
Notez que les espaces autour du signe moins ne sont pas en option: 100%-18px, 100%- 18pxet 100% -18pxne fonctionne pas dans les navigateurs que j'ai testé avec.
nisetama
71

Pour un peu d'une approche différente, vous pouvez utiliser quelque chose comme ça sur la liste:

position: absolute;
top: 18px;
bottom: 0px;
width: 100%;

Cela fonctionne tant que le conteneur parent a position: relative;

Nicolas Galler
la source
1
Merci mec. le conteneur parent étant position: relative;me faisait trébucher.
gthmb
Pour ceux qui peuvent se demander comme moi: la position doit être absolutedans le conteneur pour enfants, elle ne peut pas l'être relative.
Greg
Suite (désolé): Cependant, le parent a juste besoin d'être positionné (cela peut l'être absoluteaussi), et il doit également être réglé sur 100% de hauteur.
Greg
1
Malheureusement, cela ne fonctionne pas sur Safari iOS 8. (Testé OK sur le navigateur de stock Android 4.1 et le standard FF 34) :-(
Greg
Cela peut fonctionner dans certains cas, mais peut également entraîner des problèmes, car les éléments positionnés de manière absolue sont supprimés du flux normal, voir stackoverflow.com/a/12821537/4173303 .
Christophe Weis
26

J'utilise Jquery pour cela

function setSizes() {
   var containerHeight = $("#listContainer").height();
   $("#myList").height(containerHeight - 18);
}

puis je lie le redimensionnement de la fenêtre pour le recalculer chaque fois que la fenêtre du navigateur est redimensionnée (si la taille du conteneur a changé avec le redimensionnement de la fenêtre)

$(window).resize(function() { setSizes(); });
puffpio
la source
12
@puffpio, Certainement JS est un moyen d'accomplir cela au cas par cas. Mais comme je l'ai dit, j'essaie de rendre le CSS générique, à appliquer sur plusieurs pages (qui héritent d'une page maître, soit dit en passant). Je préfère donc ne pas utiliser JS. Mais merci pour la réponse.
MegaMatt
7
Ne mettez pas votre style dans JavaScript.
Greg
8
@greg, ça aiderait si CSS avait des fonctionnalités plus utiles. Certaines choses pour lesquelles vous devez faire des hacks sont ridicules.
Jonathan.
1
@puffpio Belle solution en effet. Mais lier des événements sur une fenêtre n'est pas une bonne solution pour moi. Si ma page contient 3-4 ou plus de ces éléments, je dois mettre à jour la hauteur et la largeur de chaque élément dans le gestionnaire de redimensionnement de la fenêtre. Ce serait un peu plus lent que la solution CSS.
sachinjain024
1
"Ne mettez pas votre xyz dans JavaScript", c'est comme dire "ne supporte pas 80% du marché des navigateurs".
Beejor
16

Ne définissez pas la hauteur en pourcentage, définissez simplement le top=0et bottom=0, comme ceci:

#div {
   top: 0; bottom: 0;
   position: absolute;
   width: 100%;
}
kaleazy
la source
Pourriez-vous expliquer pourquoi ne pas définir la hauteur à un pourcentage?
Shrikant Sharat
@ShrikantSharat Cela fait partie de la spécification de mise en forme visuelle pour les éléments absolument positionnés. Cette solution tire parti de la possibilité 5. "," hauteur "est" auto "," haut "et" bas "ne sont pas" auto ", puis les valeurs" auto "pour" margin-top "et" margin-bottom "sont définies à 0 et résoudre pour 'hauteur' ". Le navigateur peut calculer la hauteur de l'élément car il sait à quelle distance du haut et du bas de son parent il doit être.
Ross Allen
Cela ne semble pas fonctionner, du moins dans Firefox. Même avec tous les éléments parents définis sur height:100%et display:block. Sinon, ce serait une solution très élégante. Fait intéressant, margin:autone parvient pas non plus à centrer verticalement un objet à largeur fixe, même s'il fonctionne correctement horizontalement.
Beejor
8

Présumer la hauteur de l'en-tête 17px

Liste css:

height: 100%;
padding-top: 17px;

En-tête CSS:

height: 17px;
float: left;
width: 100%;
ghoppe
la source
Oui, c'est ce que je voulais dire.
dclowd9901
1
Cela n'a pas fonctionné aussi bien que je l'espérais. Ma liste non ordonnée commence sous ce div d'en-tête. Le div d'en-tête prend de l'espace dans le conteneur, donc mettre le padding-top de 17px sur la liste va juste le pousser vers le bas à 17px de l'endroit où il se trouve déjà dans l'image ci-dessus, pas à 17px du haut du div du conteneur. Voici une photo de ce que j'obtiens avec le CSS donné dans la réponse ci-dessus: i41.tinypic.com/mcuk1x.jpg . J'apprécie l'effort, cependant, et s'il y a quelque chose que vous pensez que je manque, faites-le moi savoir. Btw, j'ai débordement: auto sur la liste non ordonnée aussi.
MegaMatt
2
Il serait beaucoup plus facile de résoudre ce problème si vous publiez des CSS réels afin que nous puissions voir exactement quelles interactions se déroulent. Une autre chose que vous pourriez essayer est une marge supérieure négative sur votre ul. ul { margin-top: -17px; }
ghoppe
7
  1. Utilisez des marges négatives sur l'élément sur lequel vous souhaitez réduire les pixels. (élément souhaité)
  2. Faire overflow:hidden;sur l'élément contenant
  3. Activez overflow:auto;l'élément souhaité.

Ça a marché pour moi!

desbester
la source
3
Cela a fonctionné comme un charme pour moi (je n'ai même pas eu besoin de changer le débordement). Excellente solution, merci!
Aaj
1
Absolument génial! Apprécié la solution populaire "CSS calc", mais c'est beaucoup plus simple et prend en charge un navigateur plus large. Marges négatives en parfait!
Simon Steinberger
Il s'agit d'une solution très élégante et compatible. L'inconvénient principal est que l'élément doit avoir une hauteur fixe. Sinon, vous devez utiliser JS ou calc()le centrer lors de l'exécution. Mais pour de nombreux cas, cela ne devrait pas être un problème. Une autre chose à garder à l'esprit est que l'utilisation de nombreuses valeurs négatives pour les marges, le remplissage et les décalages peut entraîner une confusion lors du débogage de code plus tard, alors essayez de garder les choses simples.
Beejor
5

Essayez le dimensionnement des boîtes. Pour la liste:

height: 100%;
/* Presuming 10px header height */
padding-top: 10px;
/* Firefox */
-moz-box-sizing: border-box;
/* WebKit */
-webkit-box-sizing: border-box;
/* Standard */
box-sizing: border-box;

Pour l'en-tête:

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

Bien sûr, le conteneur parent devrait avoir quelque chose comme:

position: relative;
Lumière
la source
3

Une autre façon d'atteindre le même objectif: les boîtes flexibles . Faites du conteneur une boîte flexible de colonne, puis vous avez toute liberté pour autoriser certains éléments à avoir une taille fixe (comportement par défaut) ou à remplir / réduire l'espace du conteneur (avec flex-grow: 1 et flex- rétrécir: 1).

#wrap {        
  display:flex;
  flex-direction:column;
}
.extendOrShrink {
  flex-shrink:1;
  flex-grow:1;
  overflow:auto;
}

Voir https://jsfiddle.net/2Lmodwxk/ (essayez d'agrandir ou de réduire la fenêtre pour remarquer l'effet)

Remarque: vous pouvez également utiliser la propriété de raccourci:

   flex:1 1 auto;
tarulen
la source
Je pense que cela peut être la meilleure réponse aujourd'hui, car cela signifie ne pas avoir à spécifier une hauteur définie pour la div d'en-tête (de 18px dans ma question d'origine), et cela signifie ne pas avoir à soustraire des choses comme le rembourrage et la marge. Pas de pixels définis, et tout prend soin de lui-même.
MegaMatt
2

J'ai essayé certaines des autres réponses, et aucune n'a fonctionné comme je le souhaitais. Notre situation était très similaire où nous avions un en-tête de fenêtre et la fenêtre était redimensionnable avec des images dans le corps de la fenêtre. Nous voulions verrouiller le rapport hauteur / largeur du redimensionnement sans avoir besoin d'ajouter des calculs pour tenir compte de la taille fixe de l'en-tête tout en laissant l'image remplir le corps de la fenêtre.

Ci-dessous, j'ai créé un extrait très simple qui montre ce que nous avons fini par faire qui semble bien fonctionner dans notre situation et devrait être compatible avec la plupart des navigateurs.

Sur notre élément de fenêtre, nous avons ajouté une marge de 20 pixels qui contribue au positionnement par rapport aux autres éléments de l'écran, mais ne contribue pas à la "taille" de la fenêtre. L'en-tête de fenêtre est ensuite positionné de manière absolue (ce qui le supprime du flux d'autres éléments, de sorte qu'il ne provoquera pas le déplacement d'autres éléments comme la liste non ordonnée) et son sommet est positionné à -20px, ce qui place l'en-tête à l'intérieur de la marge de la fenêtre. Enfin, notre élément ul est ajouté à la fenêtre, et la hauteur peut être réglée à 100% ce qui la fera remplir le corps de la fenêtre (hors marge).

*,*:before,*:after
{
  box-sizing: border-box;
}

.window
{
  position: relative;
  top: 20px;
  left: 50px;
  margin-top: 20px;
  width: 150px;
  height: 150px;
}

.window-header
{
  position: absolute;
  top: -20px;
  height: 20px;
  border: 2px solid black;
  width: 100%;
}

ul
{
  border: 5px dashed gray;
  height: 100%;
}
<div class="window">
  <div class="window-header">Hey this is a header</div>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li>Item 5</li>
  </ul>
</div>

TJ Rockefeller
la source
1

Merci, j'ai résolu le mien avec votre aide, en l'ajustant un peu car je veux un div 100% de largeur 100% de hauteur (moins de hauteur d'une barre inférieure) et pas de défilement sur le corps (sans pirater / cacher les barres de défilement).

Pour CSS:

 html{
  width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }
 body{
  position:relative;width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }
 div.adjusted{
  position:absolute;width:auto;height:auto;left:0px;right:0px;top:0px;bottom:36px;margin:0px;border:0px;padding:0px;
 }
 div.the_bottom_bar{
  width:100%;height:31px;margin:0px;border:0px;padding:0px;
}

Pour HTML:

<body>
<div class="adjusted">
 // My elements that go on dynamic size area
 <div class="the_bottom_bar">
  // My elements that goes on bottom bar (fixed heigh of 31 pixels)
 </div>  
</div>  

Cela a fait l'affaire, oh oui, j'ai mis une valeur un peu plus grande sur div.justée pour le bas que pour la hauteur de la barre inférieure, sinon la barre de défilement verticale apparaît, j'ai ajusté pour être la valeur la plus proche.

Cette différence est parce que l'un des éléments de la zone dynamique ajoute un trou inférieur supplémentaire dont je ne sais pas comment me débarrasser ... c'est un tag vidéo (HTML5), veuillez noter que j'ai mis ce tag vidéo avec ce css ( il n'y a donc aucune raison pour qu'il fasse un trou au fond, mais il le fait):

 video{
  width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }

L'objectif: avoir une vidéo qui prend 100% du navigateur (et se redimensionne dynamiquement lorsque le navigateur est redimensionné, mais sans modifier le rapport d'aspect) moins un espace inférieur que j'utilise pour une div avec quelques textes, boutons, etc. (et validateurs w3c & css bien sûr).

EDIT: J'ai trouvé la raison, le tag vidéo est comme du texte, pas un élément de bloc, donc je l'ai corrigé avec ce CSS:

 video{
  display:block;width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }

Notez la display:block;balise on video.

Claudio
la source
0

Je ne sais pas si cela fonctionne dans votre situation particulière, mais j'ai trouvé que le remplissage sur la div intérieure poussera le contenu à l'intérieur d'une div si la div contenant est de taille fixe. Vous devrez soit flotter soit positionner absolument votre élément d'en-tête, mais sinon, je n'ai pas essayé cela pour les divs de taille variable.

dclowd9901
la source