首页 > 解决方案 > 在 CSS 中创建具有右对齐标签和左对齐值的两列布局

问题描述

全部,

在 HTML 和 CSS 中创建如下所示的简单布局的最简单方法是什么:

在此处输入图像描述

具体来说 - 左侧是右对齐的标签,右侧是左对齐的值。

我不想硬编码左列的宽度——应该根据最长标签的宽度来确定。

我正在寻找一种至少具有合理语义的方法,它适用于屏幕阅读器(例如,屏幕阅读器应该阅读标签,然后是值,而不是所有标签,然后是所有值),并且不需要一大堆额外的<div>元素。

这似乎是一个相当常见的布局,所以我假设有一个非常简单的方法来做到这一点。但我自己还没有弄清楚。

A<table>可以完美地工作,但正如每个人都提醒我的那样,永远不要将 a<table>用于布局。这显然不是表格数据。

提前致谢!

标签: htmlcss

解决方案


有几个选项,使用最少的 HTML;一个使用 CSS Grid,另一个使用 CSS flex-box 布局。

CSS网格:

/* A simple reset to ensure that all elements and
   pseudo-elements have their margin and padding
   set to zero, and all are using the same box-sizing: */
*,
 ::before,
 ::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}


/* here we set the <form> element's layout to use
   grid layout: */
form {
  display: grid;
  /* we use the repeat() function to create 2 columns,
   each column sized with a minimum of 0 width and a
   maximum of 1fr; the 'fr' unit is a fractional unit
   and here forces each column to take one fractional
   unit of the available space to create two equal-sized
   columns: */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* we use the 'gap' property (formerly 'grid-gap') to
   specify the gap between grid elements; here we have
   0.5em above and below and 1em to the left and right: */
  gap: 0.5em 1em;
  width: 80vw;
  margin: 0 auto;
}

label {
  /* aligning the text to the right of the <label> element: */
  text-align: right;
}

label::after {
  /* using the 'content' property of the pseudo-element to
   add the colon character: */
  content: ':'
}
<form>
  <!-- using the 'for' attribute to associate the label with the
       relevant <input> element; the value of the 'for' attribute
       must be equal to the 'id' attribute-value of the relevant
       <input> -->
  <label for="input1">label</label>
  <input type="text" id="input1" placeholder="input 1">
  <label for="input2">A longer label</label>
  <input type="text" id="input2" placeholder="input 2">
  <label for="input3">Another slightly longer label</label>
  <input type="text" id="input3" placeholder="input 3">
  <label for="input4">A label that goes on, frankly, for quite a bit further than might be common for the average &lt;label&gt; element</label>
  <input type="text" id="input4" placeholder="input 4">
</form>

JS 小提琴演示

弹性盒:

/* A simple reset to ensure that all elements and
   pseudo-elements have their margin and padding
   set to zero, and all are using the same box-sizing: */
*,
 ::before,
 ::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* setting the layout of the <form> to flexbox: */
form {
  display: flex;
/* allowing the child elements of the <form> to wrap
   to new lines when necessary: */
  flex-wrap: wrap;
  width: 80vw;
  margin: 0 auto;
}

/* Setting common properties for the <label> and <input>
   elements: */
label,
input {
/* assigning the flex-grow and flex-shrink (respectively)
   properties to 1, in order that they grow/shrink by the
   same amount relative to each other; and setting the
   flex-basis to 40% (the percentage derived from the parent)
   in order to assign a width that's too large to accommodate
   more than two elements per line: */
  flex: 1 1 40%;
/* setting the margin above/below each 'row' to be 0.5em,
   and 0 to the left and right: */
  margin: 0.5em 0;
}

label {
  text-align: right;
/* setting the margin-right of the <label> to 1em to enforce a
   gutter between the <label> and the neighbouring <input> (the
   CSS Box Alignment module (level 3) introduces the 'gap' property
   that can also be used in the flexbox layout (among others) but
   that's not yet supported by browsers, so we have to use margins: */
  margin-right: 1em;
}

label::after {
  content: ':'
}
<form>
  <label for="input1">label</label>
  <input type="text" id="input1" placeholder="input 1">
  <label for="input2">A longer label</label>
  <input type="text" id="input2" placeholder="input 2">
  <label for="input3">Another slightly longer label</label>
  <input type="text" id="input3" placeholder="input 3">
  <label for="input4">A label that goes on, frankly, for quite a bit further than might be common for the average &lt;label&gt; element</label>
  <input type="text" id="input4" placeholder="input 4">
</form>

JS 小提琴演示

现在,对于上述两种方法,有一个要求可能不适合您的用例:<label>元素必须实现一个for属性,这要求关联对象<input>也必须具有一个id属性(并且这些值必须是等效的)。

为避免这种情况,我们可以改为将 嵌套<input>在 中<label>,它会自动关联<label>and <input>; 这将提供(可能)更干净的 HTML:

网格,再次:

*,
::before,
::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5em 1em;
  width: 80vw;
  margin: 0 auto;
}

label {
/* it's a crude simplification, but effectively we remove
   the <label> from the DOM and instead show its contents;
   the text portion, and the <input>, become independant
   grid-items (sort of): */
  display: contents;
}
<form>
  <label>label
    <input type="text" placeholder="input 1"></label>
  <label>A longer label
    <input type="text" placeholder="input 2"></label>
  <label>Another slightly longer label
    <input type="text" placeholder="input 3"></label>
  <label>A label that goes on, frankly, for quite a bit further than might be common for the average label element
    <input type="text" placeholder="input 4"></label>
</form>

JS 小提琴演示

在上面的代码片段中,您可能会注意到:

  1. 我们不再使用label::after插入表示':'性字符,因为这显然会被放置在<label>内容之后,在<input>and 之后会创建另一个网格项(demo),并且
  2. <label>文本不再右对齐;这是因为text-align似乎不适用于文本。

*,
::before,
::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5em 1em;
  width: 80vw;
  margin: 0 auto;
}

label {
  display: contents;
  text-align: right;
}
<form>
  <label>label
    <input type="text" placeholder="input 1"></label>
  <label>A longer label
    <input type="text" placeholder="input 2"></label>
  <label>Another slightly longer label
    <input type="text" placeholder="input 3"></label>
  <label>A label that goes on, frankly, for quite a bit further than might be common for the average label element
    <input type="text" placeholder="input 4"></label>
</form>

JS 小提琴演示

我们可以在视觉上改进一些东西,将 的文本内容包装<label>在一个元素中,例如<span>恢复该表示:

*,
::before,
::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5em 1em;
  width: 80vw;
  margin: 0 auto;
}

label {
  display: contents;
  text-align: right;
}

label span::after {
  content: ':';
}
<form>
  <label><span>label</span>
    <input type="text" placeholder="input 1"></label>
  <label><span>A longer label</span>
    <input type="text" placeholder="input 2"></label>
  <label><span>Another slightly longer label</span>
    <input type="text" placeholder="input 3"></label>
  <label><span>A label that goes on, frankly, for quite a bit further than might be common for the average label element</span>
    <input type="text" placeholder="input 4"></label>
</form>

还值得注意的是,CSS Grid 和 CSS Flexbox 都允许我们在浏览器中以不同于在 DOM 中显示的方式重新排列元素;这可以让我们<label>根据关联的状态来设置元素的样式<input>,例如,

网格:

*,
::before,
::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

form {
  display: grid;
  /* to ensure that the layout backfills the empty spaces
     created by moving content around: */
  grid-auto-flow: dense;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5em 1em;
  width: 80vw;
  margin: 0 auto;
}

label {
  display: contents;
  text-align: right;
}

input {
  /* positioning the <input> elements in the secpond
     grid-track/column: */
  grid-column: 2;
}

label span {
  /* positioning the <span> elements in the first
     grid-track/column: */
  grid-column: 1;
}

label span::after {
  content: ':';
}

input:placeholder-shown+span {
  color: rebeccapurple;
}

input:not(:placeholder-shown)+span {
  color: limegreen;
  font-weight: bold;
}

input:focus+span,
input:active+span {
  color: #f90;
}
<form>
  <label>
    <input type="text" placeholder="input 1">
    <span>label</span>
  </label>
  <label>
    <input type="text" placeholder="input 2">
    <span>A longer label</span>
  </label>
  <label>
    <input type="text" placeholder="input 3">
    <span>Another slightly longer label</span>
  </label>
  <label>
    <input type="text" placeholder="input 4">
    <span>A label that goes on, frankly, for quite a bit further than might be common for the average label element</span>
  </label>
</form>

JS 小提琴演示

弹性盒:

*,
::before,
::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

form {
  width: 80vw;
  margin: 0 auto;
}

label {
  display: flex;
  margin: 0.5em 0;
}

label span,
label input {
  flex: 1 1 50%;
}

label span {
  order: 1;
  text-align: right;
  margin-right: 1em;
}

label input {
  order: 2;
}

label span::after {
  content: ':';
}

label span::after {
  content: ':';
}

input:placeholder-shown+span {
  color: rebeccapurple;
}

input:not(:placeholder-shown)+span {
  color: limegreen;
  font-weight: bold;
}

input:focus+span,
input:active+span {
  color: #f90;
}
<form>
  <label>
    <input type="text" placeholder="input 1">
    <span>label</span>
  </label>
  <label>
    <input type="text" placeholder="input 2">
    <span>A longer label</span>
  </label>
  <label>
    <input type="text" placeholder="input 3">
    <span>Another slightly longer label</span>
  </label>
  <label>
    <input type="text" placeholder="input 4">
    <span>A label that goes on, frankly, for quite a bit further than might be common for the average label element</span>
  </label>
</form>

JS 小提琴演示

但值得注意的是,grid-auto-flow: dense除了重新排列元素的视觉呈现外,还可能给那些使用屏幕阅读器或键盘交互的网络用户带来问题。因此,虽然它带来了一些漂亮,但值得考虑的是,这种漂亮是否以可用性(以及潜在的法律要求的可访问性)为代价。

此外,在后面的示例中,使用嵌套<span>元素来允许对元素的文本内容进行样式化<label>(无论是出于对齐原因,添加':'字符还是样式化以响应与<input>元素的交互,这可能是不必要的复杂化一个潜在的“干净”的 HTML 标记。

参考:

参考书目:


推荐阅读