Pourquoi <fieldset> ne peut-il pas être des conteneurs flexibles?


201

J'ai essayé de styliser un fieldsetélément avec display: flexet display: inline-flex.

Cependant, cela n'a pas fonctionné: flexs'est comporté comme block, et inline-flexs'est comporté comme inline-block.

Cela se produit à la fois sur Firefox et Chrome, mais étrangement, cela fonctionne sur IE.

Est-ce un bug? Je n'ai pas trouvé que cela fieldsetdevrait avoir un comportement spécial, ni en HTML5 ni dans les spécifications de disposition de boîte flexible CSS .

fieldset, div {
    display: flex;
    border: 1px solid;
}
<fieldset>
    <p>foo</p>
    <p>bar</p>
</fieldset>
<div>
    <p>foo</p>
    <p>bar</p>
</div>


2
Oui, c'est un bug. Solution simple: utilisez un autre élément. code.google.com/p/chromium/issues/detail?id=262679
Adam

Réponses:


145

Selon le bug 984869 - display: flexne fonctionne pas pour les éléments de bouton ,

<button>n'est pas implémentable (par les navigateurs) en CSS pur, ils sont donc un peu une boîte noire, du point de vue du CSS. Cela signifie qu'ils ne réagissent pas nécessairement de la même manière que, par exemple, un <div>.

Ce n'est pas spécifique à Flexbox - par exemple, nous ne rendons pas les barres de défilement si vous placez overflow:scrollun bouton, et nous ne le rendons pas sous forme de tableau si vous le mettez display:table.

Reculer encore plus loin, ce n'est pas spécifique à <button>. Considérer <fieldset> et <table> qui ont également un comportement de rendu spécial:

data:text/html,<fieldset style="display:flex"><div>abc</div><div>def</div>

Dans ces cas, Chrome est d'accord avec nous et ignore le flex mode d'affichage. (comme le révèle le fait que "abc" et "def" finissent par être empilés verticalement). Le fait qu'ils fassent ce que vous attendez <button style="display:flex">est probablement dû à un détail d'implémentation.

Dans l'implémentation des boutons de Gecko, nous codons en dur <button>(et <fieldset>,et <table>) comme ayant une classe de cadre spécifique (et donc une façon spécifique de disposer les éléments enfants), quelle que soit la displaypropriété.

Si vous souhaitez que les enfants soient organisés de manière fiable dans un mode de mise en page particulier de manière multi-navigateur, votre meilleur pari est d'utiliser un wrapper-div à l'intérieur du bouton, tout comme vous en auriez besoin à l'intérieur de un <table>oua <fieldset>.

Par conséquent, ce bogue a été marqué comme "résolu non valide".

Il existe également le bogue 1047590 - display: flex;ne fonctionne pas<fieldset> , actuellement "non confirmé".


Bonne nouvelle : Firefox 46+ implémente Flexbox pour <fieldset>. Voir bug 1230207 .


Étant donné que la flexbox est dans les limbes depuis 8 ans maintenant, je trouve "remarquablement" un peu exagéré. Si quoi que ce soit, je suis surpris qu'ils l'ont fait fonctionner dans Firefox si rapidement. Peut-être que vous aimeriez aider à mettre les choses en marche chez Google (hé, vous comprenez ?).
BoltClock

3
<button> is not implementable (by browsers) in pure CSS- c'est juste faux. Un navigateur est libre d'implémenter <button>ce qu'il veut, il n'est pas "contractuellement obligé" de le rendre en utilisant des bibliothèques de widgets système ou autre, ce qui nuit à la possibilité de les styliser avec CSS ou d'autres comportements personnalisés. Il en va de même pour tout autre élément, vraiment.
amn

4
@BoltClock Vous ne savez pas ce que vous entendez par limbes. Tous les principaux navigateurs prennent en charge la troisième et dernière révision de la syntaxe depuis 2015; l'interopérabilité est plutôt bonne, je pense. Nous avons même obtenu un large soutien pour Grid maintenant! 🎉
Šime Vidas

@ Šime Vidas: Je vois, on dirait que ça fait CR depuis un an maintenant. C'est suffisant. Je pensais que ça n'arriverait jamais.
BoltClock

9
2017 en effet. Où dois-je pointer ma rage? Des années d'attente pour implémenter flexbox en toute sécurité, d'innombrables articles, tutes, essais / erreurs, j'y arrive à 98% et ... ensembles de champs. C'est comme demander à un électricien de câbler ma maison, pour constater qu'une prise d'alimentation n'est pas mise à la terre. "N'exagérez pas" j'entends les gens penser, exagérez je ne le fais pas. Je dois littéralement répondre à cela demain. Flex était la réponse à plusieurs bogues de table hérités que je devais résoudre. Flex est maintenant à l'origine d'une petite poignée de bogues non hérités. Où dois-je pointer ma rage?
danjah

20

Je découvre que cela pourrait être un bug sur Chrome et Firefox où legendet fieldsetsont des éléments remplacés .

Bogues signalés:

Bug Chrome (toujours ouvert)
Bug Firefox (corrigé depuis la v46)

Une solution possible:

Une solution de contournement possible serait d'utiliser <div role="group">en HTML et d'appliquer en CSS div[role='group']comme sélecteur.


METTRE À JOUR

Dans Chrome, la version 83 button peut fonctionner avec display: inline-grid/grid/inline-flex/flex, vous pouvez voir la démo ci-dessous:

button {
  display: inline-flex;
  height: 2rem;
  align-items: flex-end;
  width: 4rem;
  -webkit-appearance: none;
  justify-content: flex-end;
}
<!-- 

The align-items keyword should fail in Chrome 81 or earlier, but work in Chrome 83 or later. To see the error, the button needs styles that make it more of an extrinsic container. In other words, it needs a height or width set. 
 
-->
<button>Hi</button>
<input type="button" value="Hi">


11

Veuillez suivre le bogue Chrome pour augmenter la priorité des bogues

Il s'agit d'un bogue dans Chrome. Veuillez ajouter une étoile à ce problème pour augmenter sa priorité à corriger: https://bugs.chromium.org/p/chromium/issues/detail?id=375693

En attendant, j'ai créé ces trois exemples de stylo code pour montrer comment contourner le problème. Ils sont construits en utilisant CSS Grid pour les exemples mais les mêmes techniques peuvent être utilisées pour flexbox.

Utiliser aria-labelledby au lieu de légende

C'est la manière la plus appropriée de résoudre le problème. L'inconvénient est que vous devez gérer la génération d'ID uniques appliqués à chaque faux élément de légende.

https://codepen.io/daniel-tonon/pen/vaaGzZ

<style>
.flex-container {
    display: flex;
}
</style>

<fieldset aria-labelledby="fake-legend">
    <div class="flex-container">
        <div class="flex-child" id="fake-legend">
            I am as good accessibilty wise as a real legend
        </div>

        ...

    </div>
</fieldset>

Utilisation de role = "group" et aria-labelledby au lieu de fieldset et legend

Si vous avez besoin que le conteneur flexible soit capable de s'étirer à la hauteur d'un élément frère et de passer ensuite cet étirement sur ses enfants, vous devrez utiliser à la role="group"place de<fieldset>

https://codepen.io/daniel-tonon/pen/BayRjGz

<style>
.flex-container {
    display: flex;
}
</style>

<div role="group" class="flex-container" aria-labelledby="fake-legend">
    <div class="flex-child" id="fake-legend">
        I am as good accessibilty wise as a real legend
    </div>

    ...

</div>

Création d'une fausse légende en double à des fins de style

C'est une façon beaucoup plus hacky de le faire. Il est toujours aussi accessible, mais vous n'avez pas à gérer les identifiants lorsque vous le faites de cette façon. Le principal inconvénient est qu'il va y avoir du contenu en double entre le véritable élément de légende et le faux élément de légende.

https://codepen.io/daniel-tonon/pen/zLLqjY

<style>
.screen-reader-only {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.flex-container {
    display: flex;
}
</style>

<fieldset>
    <legend class="screen-reader-only">
        I am a real screen-reader accessible legend element
    </legend>

    <div class="flex-container">
        <div class="flex-child" aria-hidden="true">
            I am a fake legend purely for styling purposes
        </div>

        ...

    </div>
</fieldset>

La légende DOIT être directement décente

Lorsque vous essayez pour la première fois de résoudre ce problème vous-même, vous essayez probablement de le faire:

<!-- DO NOT DO THIS! -->
<fieldset>
    <div class="flex-container">
        <legend class="flex-child">
            Broken semantics legend text
        </legend>

        ...

    </div>
</fieldset>

Vous découvrirez que cela fonctionne, puis vous avancerez probablement sans y réfléchir.

Le problème est que mettre un wrapper div entre le fieldset et la légende rompt la relation entre les deux éléments. Cela rompt la sémantique de fieldset / legend.

Donc, en faisant cela, vous avez déjoué tout le but d'utiliser fieldset / legend en premier lieu.

En outre, il est inutile d'utiliser un jeu de champs si vous ne donnez pas à ce jeu de champs une légende.


9

D'après mon expérience, j'ai constaté que ni <fieldset>, ni <button>, ni <textarea>ne peuvent correctement utiliser display:flexou hériter des propriétés.

Comme d'autres l'ont déjà mentionné, des bugs ont été signalés. Si vous souhaitez utiliser flexbox pour contrôler l'ordre (par exemple order:2), vous devez alors envelopper l'élément dans une div. Si vous souhaitez que Flexbox contrôle la disposition et les dimensions réelles, vous pouvez envisager d'utiliser un div, au lieu du contrôle d'entrée (qui pue, je sais).


3
<div role="group">
    <p>foo</p>
    <p>bar</p>
</div>
<div>
    <p>foo</p>
    <p>bar</p>
</div>

Pourrait avoir besoin d'utiliser le groupe de rôles parce que Firefox, Chrome et je pense que Safari ont apparemment un bug avec les ensembles de champs. Ensuite, le sélecteur dans le CSS serait simplement

div[role='group'], div {
    display: flex;
    border: 1px solid;
}

Edit: Voici quelques problèmes que d'autres personnes rencontrent également.

Numéro 375693

Numéro 262679


C'est toujours un problème dans Chrome 60.
evolutionxbox

Toujours ici dans Chrome 66.
Brad


Enregistrement de Chrome 73
Michael Draper


3

vous pouvez ajouter des div supplémentaires <fieldset>avec les accessoires suivants:

flex-inner-wrapper {
  display: inherit;
  flex-flow: inherit;
  justify-content: inherit;
  align-items: inherit;
}

1
Je ne comprends pas les downvotes. C'est en fait correct. Mais n'oubliez pas de garder l' <legend>élément (s'il est utilisé) au niveau supérieur de l' <fieldset>élément pour conserver son utilisation comme prévu ( Permitted content: An optional <legend> element, followed by flow content.- voir developer.mozilla.org/en-US/docs/Web/HTML/Element/… )
Froxx

2
Ce n'était pas moi, mais j'imagine que le downvote a quelque chose à voir avec le fait de ne pas répondre à la question. Il ne s'agissait pas de trouver une solution de contournement, mais le comportement de fieldsetlui - même.
Manngo

Oui, peut ne pas répondre directement à la question (qui était un peu philosophique), mais c'est une solution possible à ce bug ridicule.
Eric S. Bullington

Down a voté parce qu'il encourage la rupture de la sémantique de l' <legend>élément. Cela va à l'encontre du but d'utiliser fieldset / legend en premier lieu. Voir ma réponse ici: stackoverflow.com/a/59425373/1611058
Daniel Tonon

3

Pour répondre à la question d'origine: oui, c'est un bug, mais il n'était pas bien défini au moment où la question a été posée.

Maintenant, le rendu pour fieldset est mieux défini: https://html.spec.whatwg.org/multipage/rendering.html#the-fieldset-and-legend-elements

Dans Firefox et Safari, flexboxsur fieldsetfonctionne maintenant. Ce n'est pas encore le cas dans Chrome. (Voir https://bugs.chromium.org/p/chromium/issues/detail?id=375693 )

Voir également https://blog.whatwg.org/the-state-of-fieldset-interoperability pour les améliorations apportées à la spécification en 2018.

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.