为什么<fieldset>不能成为flex容器?

238
我尝试使用 display: flexdisplay: inline-flex 来样式化一个 fieldset 元素。

然而,它没有起作用: flex 行为类似于 block,而 inline-flex 行为类似于 inline-block

这在 Firefox 和 Chrome 上都发生了,但奇怪的是在 IE 上可以工作。

这是一个bug吗?我无法找到任何特殊行为应该适用于 fieldset,无论是在 HTML5 还是在 CSS Flexible Box Layout 规范中。

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


2
是的,这是一个bug。简单的解决方法:使用另一个元素。https://code.google.com/p/chromium/issues/detail?id=262679 - Adam Jenkins
7个回答

157

根据Bug 984869 - display: flex在按钮元素中不起作用的说法,

<button> is not implementable (by browsers) in pure CSS, so they are a bit of a black box, from the perspective of CSS. This means that they don't necessarily react in the same way that e.g. a <div> would.

This isn't specific to flexbox -- e.g. we don't render scrollbars if you put overflow:scroll on a button, and we don't render it as a table if you put display:table on it.

Stepping back even further, this isn't specific to <button>. Consider <fieldset> and <table> which also have special rendering behavior:

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

In these cases, Chrome agrees with us and disregards the flex display mode. (as revealed by the fact that "abc" and "def" end up being stacked vertically). The fact that they happen to do what you're expecting on <button style="display:flex"> is likely just due to an implementation detail.

In Gecko's button implementation, we hardcode <button> (and <fieldset>, and <table>) as having a specific frame class (and hence, a specific way of laying out the child elements), regardless of the display property.

If you want to reliably have the children reliably arranged in a particular layout mode in a cross-browser fashion, your best bet is to use a wrapper-div inside the button, just as you would need to inside of a <table> or a <fieldset>.

因此,该错误被标记为“已解决无效”。当前还有一个未经确认的问题是:Bug 1047590 - display: flex; doesn't work in <fieldset>
好消息:Firefox 46+已经为<fieldset>实现了Flexbox。请参见bug 1230207

考虑到flexbox已经处于停滞状态8年了,我认为“非常”有点言过其实。如果说有什么的话,我很惊讶他们在Firefox中如此快速地使其运作起来。也许你想在Google那边帮忙推动事情的发展(嘿,明白吗?)。 - BoltClock
4
“<button>在纯CSS中无法实现”-这是不正确的。浏览器可以自由地以任何方式实现<button>,他们没有“合同义务”使用系统部件库或其他使其难以使用CSS或其他自定义行为进行样式化的方式来呈现它。对于任何其他元素也是如此。 - Armen Michaeli
4
不确定您所说的"limbo"是什么意思。自2015年以来,所有主要浏览器都支持第三个和最终版本的语法;我认为互操作性相当不错。我们甚至现在已经得到了对网格布局的广泛支持! - Šime Vidas
@Šime Vidas:我明白了,看起来它已经CR一年了。很好。我以为它永远不会通过。 - BoltClock
14
2017 年的事情。我该把怒气发泄到哪里?多年的等待才能安全地使用 flexbox,无数的文章、教程、尝试和错误,我已经完成了 98%,然而... fieldsets。这就像请电工给我的房子接线,结果发现一个插座没有接地一样。我听到别人说“不要夸大其词”,但我没有夸张。我明天必须为此作出解释。Flex 是解决几个遗留表格 bug 的答案,但现在又导致了一些新问题。我该把怒气发泄到哪里? - danjah
显示剩余4条评论

29
我发现这可能是Chrome和Firefox上的一个bug,其中legendfieldset被替换为replaced elements
报告的错误: Bug Chrome(自v86版以来已修复)
Bug Firefox(自v46版以来已修复)
一种可能的解决方法:
一个可能的解决方法是在HTML中使用<div role="group">,并在CSS中应用div[role='group']作为选择器。

更新

Chrome 版本 83 中,button 可以与 display: inline-grid/grid/inline-flex/flex 一起使用,您可以查看下面的演示:

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


看起来自Chrome 86版本以来,这个问题已经被修复了(https://bugs.chromium.org/p/chromium/issues/detail?id=375693#c88)。 - Rúnar Berg

21

请给Chrome的bug加星标以提高其修复优先级

这是Chrome的一个bug。请在该问题上添加一个星标,以提高其修复优先级: https://bugs.chromium.org/p/chromium/issues/detail?id=375693

同时,我创建了这三个Code Pen示例,展示如何解决这个问题。这些示例使用CSS Grid构建,但相同的技术也可用于flexbox。

使用aria-labelledby而不是legend

这是处理问题的更合适方式。缺点是您必须处理为每个伪legend元素生成唯一ID的操作。

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>

使用 role="group" 和 aria-labelledby 替代 fieldset 和 legend

如果您需要弹性容器能够拉伸到同级元素的高度,并将该拉伸传递给其子元素,您需要使用 role="group" 而不是 <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>

为了样式目的而创建一个假的重复图例

这是一种更加hacky (狡猾) 的方法。它仍然具有可访问性,但使用此方式时不必处理ID。主要缺点是真实图例元素和假图例元素之间将存在重复内容。

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>

Legend必须是直接后代

当你第一次尝试自己修复这个问题时,你可能会尝试这样做:

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

        ...

    </div>
</fieldset>

你会发现它有效,然后你可能会毫不犹豫地继续前进。

问题在于在fieldset和legend之间放置一个div包装器会破坏两个元素之间的关系。这破坏了fieldset / legend的语义。

所以,这样做,你已经打败了使用fieldset/legend的初衷。

此外,如果您没有给fieldset一个legend,那么使用fieldset就没有多大意义。


9
根据我的经验,我发现无论是<fieldset><button>还是<textarea>,都不能正确使用display:flex或继承属性。
正如其他人已经提到的,已经报告了错误。如果您想使用flexbox来控制排序(例如order:2 ),那么您需要将元素包装在一个div中。如果您想使用flexbox来控制实际布局和尺寸,则可能需要考虑使用div而不是输入控件(这很糟糕,我知道)。

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

可能需要使用role-group,因为火狐、谷歌和我认为Safari在fieldset上存在一个错误。然后CSS中的选择器就很简单了。

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

编辑:以下是其他人也遇到的一些问题。

问题 375693

问题 262679


2
这在Chrome 60中仍然是一个问题。 - evolutionxbox
2
仍然在Chrome 66中。 - Brad
2
Chrome 72 已经登录。 - danemacmillan
2
Chrome 73 登录成功 - Michael Draper
2
Chrome 75 签到成功 - xaddict
显示剩余5条评论

4
你可以在<fieldset>中添加附加的
,使用以下属性:

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

1
我不理解为什么有人要踩。这其实是正确的。但请记得将<legend>元素(如果被使用)放在<fieldset>元素的最顶层,以保持其意图使用(“允许的内容:可选的<legend>元素,后跟流内容”- 请参见 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/fieldset#Technical_summary)。 - Froxx
2
不是我,但我想那个踩的原因可能与没有回答问题有关。这不是关于寻找解决方法,而是关于fieldset本身的行为。 - Manngo
是的,可能不能直接回答这个有点哲学性质的问题,但这是解决这个荒谬 bug 的一个可能方案。 - Eric S. Bullington
因为它鼓励破坏<legend>元素的语义,所以被投了反对票。这违背了首先使用fieldset/legend的目的。请参见我的答案:https://dev59.com/JF4c5IYBdhLWcg3wT4xO#59425373 - Daniel Tonon

4

回答原问题:是的,这是一个错误,但是在提问时它没有被很好地定义。

现在,fieldset的渲染已经更好地定义了:https://html.spec.whatwg.org/multipage/rendering.html#the-fieldset-and-legend-elements

在Firefox和Safari中,fieldset上的flexbox现在有效。但在Chromium中还没有。(请参见https://bugs.chromium.org/p/chromium/issues/detail?id=375693

另请参见https://blog.whatwg.org/the-state-of-fieldset-interoperability,了解2018年规范所作的改进。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接