为什么 <fieldset> 不能是弹性容器?

Why can't <fieldset> be flex containers?

我尝试使用 display: flexdisplay: inline-flex 来设置 fieldset 元素的样式。

但是,它不起作用:flex 表现得像 blockinline-flex 表现得像 inline-block

这在 Firefox 和 Chrome 上都会发生,但奇怪的是它在 IE 上也能正常工作。

这是一个错误吗?我找不到 fieldset 应该有任何特殊行为,在 HTML5 nor in 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>

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

可能需要使用角色组,因为 firefox、chrome 和我认为 safari 显然有一个字段集错误。那么 CSS 中的选择器就是

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

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

Issue 375693

Issue 262679

我发现这可能是 Chrome 和 Firefox 上的错误,其中 legendfieldsetreplaced elements

报告的错误:

Bug Chrome(自 v86 起已修复)
Bug Firefox(自 v46 起已修复)

可能的解决方法:

可能的解决方法是在 HTML 中使用 <div role="group">,并在 CSS 中应用 div[role='group'] 作为选择器。


更新

Chrome version 83button中可以使用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">

根据Bug 984869 - display: flex doesn't work for button elements,

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

因此,该错误被标记为 "resolved invalid"。

还有Bug 1047590 - display: flex; doesn't work in <fieldset>,目前"unconfirmed"。


好消息:Firefox 46+ 为 <fieldset> 实现了 Flexbox。参见 bug 1230207

根据我的经验,我发现 <fieldset><button><textarea> 都不能正确使用 display:flex 或继承的属性。

正如其他人已经提到的,已报告错误。如果你想使用 flexbox 来控制顺序(例如 order:2),那么你需要将元素包装在 div 中。如果您希望 flexbox 控制实际布局和尺寸,那么您可能需要考虑使用 div,而不是输入控件(我知道这很糟糕)。

您可以使用以下道具在 <fieldset> 中添加额外的 div:

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

回答原问题:是的,这是一个错误,但在提出问题时并没有明确定义。

现在更好地定义了字段集的呈现: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 年规范中所做的改进。

请为 Chrome 错误添加星号以提高错误优先级

这是 Chrome 中的错误。请为此问题添加星号以增加其修复的优先级: https://bugs.chromium.org/p/chromium/issues/detail?id=375693

与此同时,我创建了这三个 Code Pen 示例来展示如何解决该问题。它们是使用 CSS Grid 作为示例构建的,但相同的技术可用于 flexbox。

使用 aria-labelledby 代替图例

这样处理问题比较妥当。缺点是您必须处理生成应用于每个假图例元素的唯一 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 代替字段集和图例

如果您需要 flex-container 能够拉伸到同级元素的高度,然后将该拉伸传递给它的子元素,则需要使用 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>

传说必须是直系后代

当您第一次尝试自己解决这个问题时,您可能会尝试这样做:

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

        ...

    </div>
</fieldset>

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

问题是在字段集和图例之间放置一个 div 包装器会破坏这两个元素之间的关系。这打破了 fieldset/legend.

的语义

因此,通过这样做,您首先就破坏了使用 fieldset/legend 的全部目的。

此外,如果您不给字段集添加图例,那么使用该字段集也没有多大意义。