Fieldset 图例 CSS 渲染行为在最近几个月发生了变化……发生了什么事?

Fieldset legend CSS rendering behavior has changed in recent months... what happened?

我有一个包含各种嵌套 <fieldset> 元素的表单,每个元素都有一个 <legend>,今天我发现在我检查过的所有浏览器上我的网页都存在错误(Chrome、Firefox、Opera),之前(6-12 个月前)不存在。

有一个 <span class="undo"> 元素,它使用 background-image 显示为绿色卷曲箭头。目的是当周围的 <fieldset>class="modified" 时显示它,否则隐藏 --- 这是通过更改跨度的 width 来实现的。

旧行为(仍然出现在我的 Chromebook 运行 Chrome OS 76.0.3809.136 上)与我的意图一致呈现:

新行为(在我的 Windows 10 PC 上的 Chrome 87.0.4280.88 上)出于某种原因将“Foo 配置器”换行成两行。

是否最近浏览器渲染发生了某些变化导致了这种情况?如果是,怎么办?我该如何解决?

HTML:

<!DOCTYPE html>
<head><style type="text/css">
  form {
    max-width: 500px;
  }
  body {
    font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif;
  }
  fieldset {
    padding: 0;
  }
  fieldset > legend {
    font-size: 90%;
    margin-left: 0.5em;
  }  
  span.undo {
    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAIAAABvFaqvAAAABnRSTlMA/wD/AP83WBt9AAAACXBIWXMAAAsTAAALEwEAmpwYAAADEklEQVQ4jY2UXSxbYRjHn/ecnm5lLFTbxdIlbENTq0x8JD62RBHRm/lKCBIXJGw3jbhtuCERVy6wyDJEYhLbMuKiDYmwIRITESQNq1LZaFg5ynrannN2ceR1VrT7X53n/f/PL8/7CXxQzdnnGicbg2cESeAW8cD3f+83mo3qSDUPPAJ0W1IQ4nn++ijDMs2W5r7lPp7npaS0VFOqCFNoFBp9nD5BnvC/oAP3QeXHytnd2etpAhHZ6uzWl6158XkBPQaCln8uV4xV2E/swWaBUP3z+u6ibhkluxk0YZ2o/lzt9rqDULD0cfrxqvFwKlwoyba2Nuyp76s1MRqaoffoPY7n8LiMkoVRYQzLiEE7Jzs7rp0STQlCKBBEkZRWqa3V1VY/q5bL5I5Th8vjAgCdSrf5ZjNLnWVz2fbpfZxfd65rFVqtUhs4tQD5Of+sfXZgdWDGPmM32imC8rE+o9nYu9yLM4nyxPXX6xJCEgyE5fK4IqWRJEECAMuxhg8Gy7YFu5YaS+HjQkIofKxvwbGw4dzg4QZu1N0ogQIAJEF26jsJRGDXvG0GAAIAPH5P/nB+9vts3VudacYUssGUBylJMUm4XPm1cgmy/LDM7c4BAMdzXfNdp57TkKxEeSL+Pjw/vASJ//SyXueFMyRIvLLCEScAIDYiVhzacG6EoAC/ebSJS+F3AgCSlckS4uoZGF4bDg5a2l/a+r2Fy7TYtEuQ6p4q42EGNsat41O2qdsoDMu0TLWIp1b8tPgShAA1pjVig+XYqk9V83vz1ykXvou6L3ViS6fS5TzKAXyy/Zw/812msJGCpKS0IbWhPrU+KSaJIqjjP8fmbXPHtw7rkRVnEEKTVZNCR1cne+1wLXcgl2bogC4i7kRQBEUztJ/zB1hNaU09hh5h1/65IuZtc/lY+bn3/LYFEqtMUzZSNiIlpUJJiL2iJ0WjZaOKMEVwBEKoRlcz+GoQUwI7EnTgPmj/2j60OnTmPbuOSI9NN70wGRIMIZ5aLJfHNW2bXnQsOmiHj/VFy6K1Sm1BfEGyMll8Y7H+AvtLjkoh3mWsAAAAAElFTkSuQmCC');
    display: inline-block;
    height: 13px;
    width: 1em;
    font-size: 13px;
    background-repeat: no-repeat;
    background-size: 13px;
    margin-top: 2px;
    margin-right: -2px;
  }
  legend > span.undo {
    width: 0em;
  }
  fieldset.modified > legend > span.undo {
    width: 1em;
  }
  div.description {
    padding-left: 4px;
    font-size: 90%;
  }
</style></head>
<body>
<form>
  <fieldset class="field">
    <legend title="">Foo configurator<span class="undo" title="Restore default"></span></legend>
    <label title="">
    <input name="foo_type" type="radio" value="123">Foo #1</label> <div class="description">First foo</div>
    <span class="field-content">
      <label><input name="foo_enable" type="checkbox" value="value">Enable</label>
      <span class="undo" title="Restore default"></span>
      <div class="description">Zoppity</div>
  </span>
  </fieldset>
  <fieldset class="field modified">
    <legend title="">Bar configurator<span class="undo" title="Restore default"></span></legend>
    <label title="">
    <input name="bar_type" type="radio" value="123">Bar #1</label> <div class="description">First bar</div>
    <span class="field-content">
      <label><input name="bar_enable" type="checkbox" value="value">Enable</label>
      <span class="undo" title="Restore default"></span>
      <div class="description">Zoppity</div>
  </span>
  </fieldset>
</form>
</body>
</html>

至于行为改变的原因是什么,我也说不清楚。

同时,您可以通过添加

来修复此行为
white-space: nowrap;

到您的图例元素,或

legend > span.undo { display: none; }
fieldset.modified > legend > span.undo { display: inline-block; }

似乎也能完成这项工作,以及删除负边距

span.undo { margin-right: 0; }

如果您在 Chrome 76 上可以正确看到它,则可能是通过在 Chrome 77 中打开 LayoutNG 引入了此更改,该版本已于 2019 年发布,但它的某些部分后来出现了。例如,我知道我在文本区域中的布局有些不一致,直到大约去年才不得不修改。

最好用Chrome 77 测试一下,看是否真的是这个东西。

无论如何,这看起来像是 margin-right: -2pxwidth: 0em 之间的相互作用,它以某种方式将布局计算为溢出并添加新行。

通过使用 BrowserStack,我发现它在 Chrome 86 中发生了变化,恰好是 Chrome 实现了 flex and grid for fieldsets 的版本。他们可能也需要细微的改变才能做到这一点。

负边距将 legend 的右边框向左拉 2px,从而将图例的宽度减少 2px。当您在 span.undo 上设置 width:0em 时,负边距仍然起作用。但是现在 legend 的宽度短了 2px 以在同一行上呈现标题文本,因此它会换行。

我们可以模拟这种行为。见代码注释。

* {
  font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif;
  font-size: 16px;
}

/* legend is behaving like a block element with width fit-content */
div {
  border: 1px solid;
  width: fit-content;
}

/* undo icon is inline-block */
span {
  display: inline-block;
}

/* title text */
span:first-child {
  color: green;
}

/* undo elements with negative margins */
.negative-margin1 {
  margin-right: -20px;
}

.negative-margin2 {
  margin-right: -40px;
}

.negative-margin3 {
  margin-right: -49px;
}
<div class="one"><span>Foo efg </span> <span>undo</span></div>
<div class="two"><span>Foo efg </span> <span class="negative-margin1">undo</span></div>
<div class="two"><span>Foo efg </span> <span class="negative-margin2">undo</span></div>
<div class="two"><span>Foo efg </span> <span class="negative-margin3">undo</span></div>
随着负边距减小宽度,内联文本开始换行。
根据您的字体大小,输出可能会略有不同,因此这是它的预期外观:


我们可以通过对 padding 做一些调整来避免负边距:

form {
  max-width: 500px;
}

body {
  font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif;
}

fieldset {
  padding: 0;
}

fieldset>legend {
  font-size: 90%;
  margin-left: 0.5em;
  
  /* set padding right to zero */
  padding-right: 0;
}

/* but we want 2px margin right for the title text */
span.ttl {
  margin-right: 2px;
}

span.undo {
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAIAAABvFaqvAAAABnRSTlMA/wD/AP83WBt9AAAACXBIWXMAAAsTAAALEwEAmpwYAAADEklEQVQ4jY2UXSxbYRjHn/ecnm5lLFTbxdIlbENTq0x8JD62RBHRm/lKCBIXJGw3jbhtuCERVy6wyDJEYhLbMuKiDYmwIRITESQNq1LZaFg5ynrannN2ceR1VrT7X53n/f/PL8/7CXxQzdnnGicbg2cESeAW8cD3f+83mo3qSDUPPAJ0W1IQ4nn++ijDMs2W5r7lPp7npaS0VFOqCFNoFBp9nD5BnvC/oAP3QeXHytnd2etpAhHZ6uzWl6158XkBPQaCln8uV4xV2E/swWaBUP3z+u6ibhkluxk0YZ2o/lzt9rqDULD0cfrxqvFwKlwoyba2Nuyp76s1MRqaoffoPY7n8LiMkoVRYQzLiEE7Jzs7rp0STQlCKBBEkZRWqa3V1VY/q5bL5I5Th8vjAgCdSrf5ZjNLnWVz2fbpfZxfd65rFVqtUhs4tQD5Of+sfXZgdWDGPmM32imC8rE+o9nYu9yLM4nyxPXX6xJCEgyE5fK4IqWRJEECAMuxhg8Gy7YFu5YaS+HjQkIofKxvwbGw4dzg4QZu1N0ogQIAJEF26jsJRGDXvG0GAAIAPH5P/nB+9vts3VudacYUssGUBylJMUm4XPm1cgmy/LDM7c4BAMdzXfNdp57TkKxEeSL+Pjw/vASJ//SyXueFMyRIvLLCEScAIDYiVhzacG6EoAC/ebSJS+F3AgCSlckS4uoZGF4bDg5a2l/a+r2Fy7TYtEuQ6p4q42EGNsat41O2qdsoDMu0TLWIp1b8tPgShAA1pjVig+XYqk9V83vz1ykXvou6L3ViS6fS5TzKAXyy/Zw/812msJGCpKS0IbWhPrU+KSaJIqjjP8fmbXPHtw7rkRVnEEKTVZNCR1cne+1wLXcgl2bogC4i7kRQBEUztJ/zB1hNaU09hh5h1/65IuZtc/lY+bn3/LYFEqtMUzZSNiIlpUJJiL2iJ0WjZaOKMEVwBEKoRlcz+GoQUwI7EnTgPmj/2j60OnTmPbuOSI9NN70wGRIMIZ5aLJfHNW2bXnQsOmiHj/VFy6K1Sm1BfEGyMll8Y7H+AvtLjkoh3mWsAAAAAElFTkSuQmCC');
  display: inline-block;
  height: 13px;
  width: 1em;
  font-size: 13px;
  background-repeat: no-repeat;
  background-size: 13px;
  margin-top: 2px;
  
  /*margin-right: -2px;  we don't need this now*/ 
}

legend>span.undo {
  width: 0em;
}

fieldset.modified>legend>span.undo {
  width: 1em;
}

div.description {
  padding-left: 4px;
  font-size: 90%;
}
<form>
  <fieldset class="field">
    <legend title=""><span class='ttl'>Foo configurator</span><span class="undo" title="Restore default"></span></legend>
    <label title="">
    <input name="foo_type" type="radio" value="123">Foo #1</label>
    <div class="description">First foo</div>
    <span class="field-content">
      <label><input name="foo_enable" type="checkbox" value="value">Enable</label>
      <span class="undo" title="Restore default"></span>
    <div class="description">Zoppity</div>
    </span>
  </fieldset>
  <fieldset class="field modified">
    <legend title=""><span class='ttl'>Bar configurator</span><span class="undo" title="Restore default"></span></legend>
    <label title="">
    <input name="bar_type" type="radio" value="123">Bar #1</label>
    <div class="description">First bar</div>
    <span class="field-content">
      <label><input name="bar_enable" type="checkbox" value="value">Enable</label>
      <span class="undo" title="Restore default"></span>
    <div class="description">Zoppity</div>
    </span>
  </fieldset>
</form>

因此,我们可以在 parent 元素上设置 0 填充,而不是在 child 上设置负边距。


在当前版本(100.0.4896.88)中,Chrome默认将以下CSS分配给图例元素:

legend {
  display: block;
  padding-inline-start: 2px;
  padding-inline-end: 2px;
  border-width: initial;
  border-style: none;
  border-color: initial;
  border-image: initial;
}

我们需要检查它在早期版本中是否不同。