<fieldset> redimensionne mal; semble avoir un "min-width: min-content" inamovible

221

Problème

J'ai un <select>où l'une de ses <option>valeurs de texte est très longue. Je veux que le <select>redimensionne pour qu'il ne soit jamais plus large que son parent, même s'il doit couper son texte affiché. max-width: 100%devrait le faire.

Avant de redimensionner:

la page avant de redimensionner, montrant l'ensemble <code> sélectionnez </code>

Ce que je veux après le redimensionnement:

ma page souhaitée après le redimensionnement, avec le <code> sélectionner </code> coupant son contenu

Mais si vous chargez cet exemple jsFiddle et redimensionnez la largeur du panneau Résultat pour qu'elle soit plus petite que celle du <select>, vous pouvez voir que la sélection à l'intérieur du <fieldset>ne parvient pas à réduire sa largeur.

Ce que je vois réellement après le redimensionnement:

ma page actuelle après redimensionnement, avec une barre de défilement

Cependant, la page équivalente avec un <div>au lieu d'un a<fieldset> évolue correctement. Vous pouvez le voir et tester vos modifications plus facilement si vous avez un <fieldset>et un <div>côte à côte sur une seule page . Et si vous supprimez les <fieldset>balises environnantes , le redimensionnement fonctionne. La <fieldset>balise provoque en quelque sorte la rupture du redimensionnement horizontal.

Les <fieldset>actes sont comme s'il y avait une règle CSS fieldset { min-width: min-content; }. ( min-contentsignifie, en gros, la plus petite largeur qui ne fait pas déborder un enfant.) Si je remplace le <fieldset>par un <div>avecmin-width: min-content , il a exactement la même apparence. Pourtant, il n'y a pas de règle avec min-contentdans mes styles, dans la feuille de style par défaut du navigateur, ou visible dans l'inspecteur CSS de Firebug. J'ai essayé de remplacer tous les styles visibles <fieldset>dans l'inspecteur CSS de Firebug et dans la feuille de style par défaut de Firefox, forms.css , mais cela n'a pas aidé. Spécifiquement prioritaire min-widthet widthn'a rien fait non plus.

Code

HTML de l'ensemble de champs:

<fieldset>
    <div class="wrapper">
        <select id="section" name="section">
            <option value="-1"></option>
            <option value="1501" selected="selected">Sphinx of black quartz, judge my vow. The quick brown fox jumps over the lazy dog.</option>
            <option value="1480">Subcontractor</option>
            <option value="3181">Valley</option>
            <option value="3180">Ventura</option>
            <option value="3220">Very Newest Section</option>
            <option value="1481">Visitor</option>
            <option value="3200">N/A</option>
        </select>
    </div>
</fieldset>

Mon CSS devrait fonctionner mais ne l'est pas:

fieldset {
    /* hide fieldset-specific visual features: */
    margin: 0;
    padding: 0;
    border: none;
}

select {
    max-width: 100%;
}

La réinitialisation des widthpropriétés aux valeurs par défaut ne fait rien:

fieldset {
    width: auto;
    min-width: 0;
    max-width: none;
}

CSS supplémentaire dans lequel j'essaie et ne parviens pas à résoudre le problème :

/* try lots of things to fix the width, with no success: */
fieldset {
    display: block;
    min-width: 0;
    max-width: 100%;
    width: 100%;
    text-overflow: clip;
}

div.wrapper {
    width: 100%;
}

select {
    overflow: hidden;
}

Plus de détails

Le problème se produit également dans cet exemple jsFiddle plus complet et plus compliqué , qui est plus similaire à la page Web que j'essaie de corriger. Vous pouvez en voir que ce <select>n'est pas le problème - un bloc en ligne divne parvient pas non plus à redimensionner. Bien que cet exemple soit plus compliqué, je suppose que le correctif pour le cas simple ci-dessus corrigera également ce cas plus compliqué.

[Modifier: voir les détails de prise en charge du navigateur ci-dessous.]

Une chose curieuse à propos de ce problème est que si vous définissezdiv.wrapper { width: 50%; } , les <fieldset>arrêts se redimensionnent au point alors la taille réelle <select>aurait atteint le bord de la fenêtre. Le redimensionnement se produit comme si le <select>has width: 100%, même si le <select>ressemblait width: 50%.

après redimensionnement avec div wrapper 50% de largeur

Si vous donnez le <select>lui - mêmewidth: 50% , ce comportement ne se produit pas; la largeur est simplement correctement réglée.

après redimensionnement avec sélection de 50% de largeur

Je ne comprends pas la raison de cette différence. Mais cela peut ne pas être pertinent.

J'ai également trouvé que le champ de questions HTML très similaire permet aux enfants de se développer indéfiniment . Le demandeur n'a pas pu trouver de solution et suppose qu'il n'y a pas d' autre solution que de supprimer le <fieldset>. Mais je me demande, s'il est vraiment impossible de bien faire l' <fieldset>affichage, pourquoi est-ce? Quelle <fieldset>est la spécification ou le CSS par défautpartir de cette question ) à l'origine de ce comportement? Ce comportement spécial est probablement documenté quelque part, car plusieurs navigateurs fonctionnent comme ceci.

Objectif et exigences de base

La raison pour laquelle j'essaie de le faire est dans le cadre de l'écriture de styles mobiles pour une page existante avec un grand formulaire. Le formulaire comporte plusieurs sections, dont une partie est enveloppée dans un <fieldset>. Sur un smartphone (ou si vous réduisez la taille de la fenêtre de votre navigateur), la partie de la page avec le <fieldset>est beaucoup plus large que le reste du formulaire. La plupart du formulaire limite très bien sa largeur, mais pas la section avec le <fieldset>, forçant l'utilisateur à effectuer un zoom arrière ou à faire défiler vers la droite pour voir toute cette section.

Je me méfie simplement de supprimer le <fieldset>, car il est généré sur de nombreuses pages dans une grande application, et je ne suis pas sûr de savoir quels sélecteurs en CSS ou JavaScript pourraient en dépendre.

Je peux utiliser JavaScript si nécessaire, et une solution JavaScript vaut mieux que rien. Mais si JavaScript est le seul moyen de le faire, je serais curieux d'entendre pourquoi il n'est pas possible d'utiliser uniquement CSS et HTML.


Modifier: prise en charge du navigateur

Sur le site, je dois prendre en charge Internet Explorer 8 et versions ultérieures (nous venons de supprimer la prise en charge d'IE7), le dernier Firefox et le dernier Chrome. Cette page particulière devrait également fonctionner sur les smartphones iOS et Android. Un comportement légèrement dégradé mais toujours utilisable est acceptable pour Internet Explorer 8.

J'ai retesté mon fieldsetexemple cassé sur différents navigateurs. En fait, cela fonctionne déjà dans ces navigateurs:

  • Internet Explorer 8, 9 et 10
  • Chrome
  • Chrome pour Android

Il casse dans ces navigateurs:

  • Firefox
  • Firefox pour Android
  • Internet Explorer 7

Ainsi, le seul navigateur auquel je tiens à ce que le code actuel entre en jeu est Firefox (à la fois sur ordinateur et sur mobile). Si le code était corrigé pour qu'il fonctionne dans Firefox sans le casser dans d'autres navigateurs, cela résoudrait mon problème.

Le modèle HTML du site utilise des commentaires conditionnels Internet Explorer pour ajouter des classes telles .ie8et .oldieà l' <html>élément. Vous pouvez utiliser ces classes dans votre CSS si vous avez besoin de contourner les différences de style dans IE. Les classes ajoutées sont les mêmes que dans cette ancienne version de HTML5 Boilerplate .

Rory O'Kane
la source
41
félicitations pour cet excellent résumé
Alp
Votre exemple simple: donnez <fieldset>(ou div.wrapper) la largeur dont il a besoin, et select { width:100% }. max-widthsemble donner à l'élément quelque% de la largeur d' origine de son parent , puis ne se redimensionne pas, alors widthqu'il se redimensionne avec son parent. Je pense que cela fera ce que vous voulez (mais j'aurais pu mal compris). Dans votre violon plus complexe, essayez de donner aux champs de la colonne de gauche une largeur de% et une largeur de pixel minimale. Donnez ensuite aux champs de droite 100 - (champs de gauche -% - largeur)% largeur.
Trojan
Je viens d'avoir une autre pensée: que se passe-t-il si vous définissez la largeur min du fieldset à 0? Cela ne résoudra probablement pas le problème en soi, mais cela vaut peut-être la peine d'essayer. Je travaille sur un chemin différent maintenant, mais si cela ne fonctionne pas, je vais essayer ça aussi
Trojan
Avez-vous besoin de l'étiquette et du champ pour rester sur la même ligne? Pour les petites fenêtres, elles se chevauchent. Pourquoi ne pas le rendre quelque peu réactif et passer à une nouvelle ligne si nécessaire?
Trojan

Réponses:

346

Mise à jour (25 septembre 2017)

Le bogue Firefox décrit ci-dessous est corrigé à partir de Firefox 53 et le lien vers cette réponse a finalement été supprimé de la documentation de Bootstrap .

Aussi, mes sincères excuses aux contributeurs de Mozilla qui ont dû bloquer la suppression du support en -moz-documentpartie à cause de cette réponse.

Le correctif

Dans WebKit et Firefox 53+, vous venez de définir min-width: 0;sur le fieldset pour remplacer la valeur par défaut de min-content

Pourtant, Firefox est un peu… étrange en ce qui concerne les ensembles de champs. Pour que cela fonctionne dans les versions antérieures, vous devez remplacer la displaypropriété de l'ensemble de champs par l'une des valeurs suivantes:

  • table-cell (conseillé)
  • table-column
  • table-column-group
  • table-footer-group
  • table-header-group
  • table-row
  • table-row-group

Parmi ceux-ci, je recommandetable-cell . Les deux table-rowet table-row-groupvous empêcher de changer la largeur, tout table-columnet table-column-groupvous empêcher de changer la hauteur.

Cela interrompra (assez raisonnablement) le rendu dans IE. Étant donné que seul Gecko en a besoin, vous pouvez à juste titre utiliser @-moz-document- l'une des extensions CSS propriétaires de Mozilla - pour la cacher aux autres navigateurs:

@-moz-document url-prefix() {
    fieldset {
        display: table-cell;
    }
}

(Voici une démo jsFiddle .)


Cela règle les choses, mais si vous êtes comme moi, votre réaction a été quelque chose comme…

Quoi.

Il y a une raison, mais ce n'est pas joli.

La présentation par défaut de l'élément fieldset est absurde et essentiellement impossible à spécifier en CSS. Pensez-y: la bordure du fieldset disparaît là où elle est chevauchée par un élément de légende, mais l'arrière-plan reste visible! Il n'y a aucun moyen de reproduire cela avec une autre combinaison d'éléments.

Pour couronner le tout, les implémentations sont pleines de concessions au comportement hérité. L'une d'entre elles est que la largeur minimale d'un jeu de champs n'est jamais inférieure à la largeur intrinsèque de son contenu. WebKit vous donne un moyen de remplacer ce comportement en le spécifiant dans la feuille de style par défaut, mais Gecko² va plus loin et l' impose dans le moteur de rendu .

Cependant, les éléments de table internes constituent un type de cadre spécial dans Gecko. Les contraintes dimensionnelles pour les éléments avec ces displayvaleurs définies sont calculées dans un chemin de code séparé , contournant entièrement la largeur minimale imposée imposée aux ensembles de champs.

Encore une fois - le bogue pour cela a été corrigé à partir de Firefox 53, donc vous n'avez pas besoin de ce hack si vous ne ciblez que les versions plus récentes.

L'usage est-il @-moz-documentsûr?

Pour ce seul problème, oui. @-moz-documentfonctionne comme prévu dans toutes les versions de Firefox jusqu'à 53, où ce bug est corrigé.

Ce n'est pas un hasard. En raison de cette réponse, le bogue à limiter @-moz-documentaux feuilles de style utilisateur / UA a été rendu dépendant du bogue fieldset sous-jacent qui a été corrigé en premier.

Au-delà, n'utilisez pas @-moz-documentpour cibler Firefox dans votre CSS , malgré les autres ressources.³


¹ La valeur peut être préfixée. Selon un lecteur, cela n'a aucun effet dans Android 4.1.2 Stock Browser et éventuellement d'autres anciennes versions; Je n'ai pas eu le temps de vérifier cela.

² Tous les liens vers la source Gecko dans cette réponse se réfèrent à l' ensemble de modifications 5065fdc12408 , validé le 29 juillet 2013; vous pouvez comparer les notes avec la révision la plus récente de Mozilla Central .

³ Voir par exemple SO # 953491: Cibler uniquement Firefox avec CSS et astuces CSS: hacks CSS ciblant Firefox pour des articles largement référencés sur des sites de haut niveau.

Jordan Grey
la source
5
Je venais de remarquer que le correctif s'est cassé dans IE, pour venir ici et découvrir que vous avez déjà modifié la solution à cela. Merci! La solution de piratage Gecko fonctionne bien pour moi dans chaque navigateur.
Rory O'Kane
J'arrachait mes cheveux à ce problème, mais votre solution est parfaite - sauf qu'elle ne semble fonctionner que dans Firefox. Des suggestions pour d'autres navigateurs, en particulier les navigateurs mobiles? Mon plus gros problème est le redimensionnement en raison du changement d'orientation sur les appareils mobiles ...
Adriaan Nel
30
C'est une réponse absolument superbe - approfondie, recherchée et concise. Merci.
iamkeir
16
Bien sûr, c'est une réponse absolument superbe! La documentation officielle de Bootstrap pointe ici
Ranhiru Jude Cooray
1
Eh bien, je n'ai jamais su ça des champs. J'ai appris quelque chose ici aujourd'hui.
superluminaire
11

Problème Safari sur iOS avec réponse sélectionnée

J'ai trouvé la réponse de Jordan Gray particulièrement utile. Cependant, cela n'a pas semblé résoudre ce problème sur Safari iOS pour moi.

Le problème pour moi est simplement que le jeu de champs ne peut pas avoir une largeur automatique si l'élément à l'intérieur a une largeur maximale en% de largeur.

Correction d'un problème

Le simple fait de définir le fieldset pour avoir une largeur de 100% de son conteneur semble contourner ce problème.

Exemple

fieldset {
    min-width: 0; 
    width: 100%; 
}

Veuillez vous référer aux exemples ci-dessous - si vous supprimez le% width du champ ou le remplacez par auto, il ne continuera pas de fonctionner.

JSFiddle | Codepen

sangliers
la source
1
C'est un excellent ajout au problème général, je n'ai pas pensé à le tester dans iOS. :) Je pense presque que cela vaut peut-être la peine de vous poser et de répondre par vous-même comme nouvelle question. J'essaierai de faire des tests supplémentaires cette semaine et de créer un lien vers cette réponse si cela fonctionne.
Jordan Grey
3

J'ai eu du mal pendant de nombreuses heures avec cela, et fondamentalement, le navigateur applique un style calculé que vous devez remplacer dans votre CSS. J'oublie la propriété exacte qui est définie sur les fieldsetéléments par rapport à divs (peut min-width- être ?).

Mon meilleur conseil serait de changer votre élément en a div, de copier les styles calculés depuis votre inspecteur, puis de changer votre élément en arrière fieldsetet de comparer les styles calculés pour trouver le coupable.

J'espère que cela pourra aider.

Mise à jour: l' ajout d' display: table-cellaides dans les navigateurs non Chrome.

phdj
la source
J'ai comparé chaque propriété CSS de fieldsetet divsur cette page dans Firebug. Ça n'a pas aidé. Dans Firebug, les seules propriétés avec des valeurs différentes étaient widthet perspective-origin. Le widthétait numériquement différent, mais les fieldset« s widthétait auto, comme les div» s. Et le perspective-originest juste une fonction de width- 50% de celui-ci par défaut.
Rory O'Kane
Cependant, la comparaison a aidé d'une certaine manière dans Chrome Web Inspector. J'ai découvert juste après avoir ouvert mon exemple qu'il fonctionne dans Chrome! J'avais ajouté le min-width: 0;à mon exemple après avoir testé dans Chrome, mais il semble que cela ait résolu le problème dans Chrome. L'Inspecteur Web me dit que Chrome a le style min-width: -webkit-min-content;, comme je l'ai supposé. Alors maintenant, je n'ai plus qu'à résoudre le problème dans Firefox et peut-être d'autres navigateurs dans lesquels je n'ai pas encore testé.
Rory O'Kane
Heureux que cela ait aidé un peu. J'ai pu résoudre mon problème dans d'autres navigateurs avec "display: table-cell". Je ne pense pas que ce ne soit pas la meilleure solution, mais j'espère que cela fonctionne pour vous.
phdj
2

.fake-select { white-space:nowrap; }a amené le fieldset à interpréter l' .fake-selectélément par sa largeur d'origine, plutôt que par sa largeur forcée (même lorsque le débordement est masqué).

Retirer cette règle, et le changement .fake-select« est max-width:100%à seulement width:100%et tout se. La mise en garde est que vous voyez tout le contenu de la fausse sélection, mais je ne pense pas que ce soit si mauvais, et il s'intègre horizontalement maintenant.

Mise à jour: avec les règles actuelles dans le violon suivant (qui ne contient que des sélections réelles), les enfants de l'ensemble de champs sont contraints de corriger les largeurs. Autre que la suppression des règles .fake-selectet la correction des commentaires (de // commentà /* comment */, j'ai noté des changements dans le CSS du violon.

Je comprends mieux votre problème maintenant, et le violon reflète certains progrès. J'ai défini des règles par défaut pour tous les <select>s, et réserve .xxlargepour celles dont vous savez qu'elles seront plus larges que 480px (et cela ne fonctionne que parce que vous connaissez la largeur de #viewport, et pouvez ajouter manuellement la classe à celles trop larges. Nécessite juste un peu de test )

Preuve

troyen
la source
Quelle que soit la correction que vous utilisez, elle doit fonctionner sur un réel <select>. Le site actuel n'a que l' <select>art. Le but fake-selectétait d'avoir les règles de mise en page d'un <select>sans être réellement un <select>, juste pour montrer que ce comportement n'est pas un bug de navigateur qui ne se produit qu'avec des <select>éléments. Donc, changer les styles .fake-selectpour être moins comme un <select>défait le but.
Rory O'Kane
J'ai remplacé la .fake-selectboîte par une <select>(identique à celle déjà présente) pour le démontrer. Les éléments sont tous limités à la largeur du parent (sauf lorsque vous cliquez dessus, en déposant les options, qui sont chacune affichées sur une seule ligne - mais je ne pense pas que vous puissiez contrôler cela). J'ai également supprimé toutes les règles pour .fake-select. Le violon actuel fait-il ce dont vous avez besoin?
Trojan
Ce ne est pas. width: 100%fonctionne avec les menus longs de la page, mais ne fonctionne pas correctement avec les menus courts. Il étend les menus courts sur toute la largeur de la page, mais je souhaite que les menus courts conservent leur largeur courte. Les menus doivent être leur largeur naturelle s'il y a de la place, mais une largeur de 100% s'ils sont trop grands pour leur parent. C'est ce qui max-widthest censé faire, mais à défaut dans ce cas.
Rory O'Kane
J'ai apporté quelques modifications supplémentaires, en séparant "tous les <select>s" des "longs <select>" et en travaillant maintenant sur la largeur de l'arrière-plan.
Trojan
#viewportn'a pas non plus de largeur fixe. C'est pourquoi j'ai mis le bouton Redimensionner la fenêtre d'affichage sur la page - afin que vous puissiez tester que tout fonctionne quelle que soit la largeur de la fenêtre d'affichage. Tout comme .fake-selectun substitut pour un réel <select>à tester, #viewportest un substitut pour une vraie fenêtre. (Une «fenêtre» est essentiellement une fenêtre de navigateur). J'ai inclus #viewportsur la page afin que vous puissiez facilement voir les éléments qui dépassent de la fenêtre d'affichage, plutôt que d'avoir à faire défiler pour les voir.
Rory O'Kane