Flex/Grid布局在按钮或字段集元素上无效

105

我试图使用flexbox的justify-content: center来居中一个<button>标签内部的元素。但Safari无法将它们居中对齐。我可以将相同的样式应用于其他任何标记,并且可以按预期工作(请参见<p>标记)。只有按钮向左对齐。

尝试Firefox或Chrome,您会看到差异。

是否有任何用户代理样式需要覆盖?还是解决此问题的其他解决方案?

div {
  display: flex;
  flex-direction: column;
  width: 100%;
}
button, p {
  display: flex;
  flex-direction: row;
  justify-content: center;
}
<div>
  <button>
    <span>Test</span>
    <span>Test</span>
  </button>
  <p>
    <span>Test</span>
    <span>Test</span>
  </p>
</div>

这是一个 jsfiddle 示例: http://jsfiddle.net/z3sfwtn2/2/


3个回答

151

问题

在一些浏览器中,<button>元素无法接受超出在blockinline-block之间切换以外的display值更改。这意味着<button>元素既不能是flex或grid容器,也不能是<table>

除了<button>元素,您可能还会发现这个限制适用于<fieldset><legend>元素。

有关详细信息,请参见下面的错误报告。

注意:虽然它们不能是flex容器,但<button>元素可以是flex项。


解决方案

有一个简单易行的跨浏览器解决此问题的方法:

button内容包装在span中,并使成为flex容器。

调整后的HTML(将button内容包装在span中)

<div>
    <button>
        <span><!-- using a div also works but is not valid HTML -->
            <span>Test</span>
            <span>Test</span>
        </span>
    </button>
    <p>
        <span>Test</span>
        <span>Test</span>
    </p>
</div>

调整后的 CSS(针对 span

button > span, p {
    display: flex;
    flex-direction: row;
    justify-content: center;
}

修正后的演示


参考文献/错误报告

<button>元素上使用Flexbox会将内容块化,但无法建立Flex格式上下文

用户(Oriol Brufau):<button>的子项被块化,符合Flexbox规范。然而,<button>似乎建立了一个块格式上下文而不是Flex格式。

用户(Daniel Holbert):这实际上就是HTML规范所要求的。几个HTML容器元素是“特殊的”,除了其是否为行级或块级元素之外,在Gecko中它们实际上忽略其CSS display值。其中之一是<button><fieldset><legend>.

添加支持在<button>元素内使用display:flex/grid和columnset布局

用户(Daniel Holbert):

<button>不能通过纯CSS实现(由浏览器),因此它们从CSS的角度来看有点像黑盒子。这意味着它们不一定会像<div>一样作出相同的反应。

这并非特定于flexbox——例如,如果在按钮上放置overflow:scroll,我们不会渲染滚动条,如果将其放置为display:table,我们也不会将其渲染为表格。

进一步地,这并非特定于<button>。考虑具有特殊渲染行为的<fieldset><table>等元素。

而旧式的HTML元素如<button><table><fieldset>除了用于回答“这个元素是块级还是行内级”,以便流动其他内容周围该元素之外,根本不支持自定义display值。

另请参阅:


3
我明白为什么<button>可能不起作用,但是为什么<fieldset>也不起作用呢?那个元素不应该被视为一个普通的块级元素,适用于弹性容器吗? - fritzmg
3
@fritzmg同意。这个规则可能在CSS3技术(如Flex和Grid)出现之前就存在了,应该进行重新评估。 - Michael Benjamin
你有关于<button>不被设计为flex(或grid)容器的引用吗?看起来浏览器认为无法在这些元素上设置display是一个bug,并且正在积极修复它们。 - mikemaccana
1
@Michael_B 考虑在您进行断言的实际文本中添加引用。无需回复我的评论 - 如果您将其添加到作出断言的文本中,我将给您点赞。 - mikemaccana
2
一个 div 不能作为 button 的子元素,因为如果你按照老式的 HTML 规范来看,块级元素不能作为行内元素的子元素。但是现在,随着 HTML5 的出现,技术上正确的答案是 一个 button 元素只能接受 Phrasing Content。一个 span 表示 Phrasing Content,但是 div 不是。一个 div 表示 Flow Content更多细节 - Michael Benjamin
显示剩余4条评论

12

这是我最简单的黑客技巧。

button::before,
button::after {
    content: '';
    flex: 1 0 auto;
}

对我不起作用。你会如何修复这个:https://codepen.io/nuthinking/pen/d64da8788475c1c9c09a869f7403156d?editors=1100 - Nuthinking

3

从 Chrome 83 开始,`button` 现在可以作为 inline-grid/grid/inline-flex/flex 使用。

以下是适用于 Chrome 83 及以上版本的代码片段:

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


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