table 按内容列宽但不超过 50%

table column width by content but not more the 50%

美好的一天。 我有 table 两列:第一列用于标签,第二列用于控制元素。标签可以很短也可以很长。

table{
padding-bottom: 20px;
}

td{ border: 1px solid}
<table style="width:100%;">
    <tr>
      <td class="first-column">very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very label</td>
      <td class="second-column"><input style="width:100%"/></td>
    </tr>
    <tr>
      <td class="first-column">label</td>
      <td class="second-column"><input style="width:100%"/></td>
    </tr>
</table>
    
  
<table style="width:100%;">
    <tr>
      <td class="first-column">medium label</td>
      <td class="second-column"><input style="width:100%"/></td>
    </tr>
    <tr>
      <td class="first-column">label</td>
      <td class="second-column"><input style="width:100%"/></td>
    </tr>
 </table>

 <table style="width:100%;">
    <tr>
      <td class="first-column">label</td>
      <td class="second-column"><input style="width:100%"/></td>
    </tr>
    <tr>
      <td class="first-column">label</td>
      <td class="second-column"><input style="width:100%"/></td>
    </tr>
 </table>

我需要根据内容(最长标签的宽度)调整第一列的大小,但如果标签很长,它不应填充超过 table 的 50%。 像这样的东西

你能帮我为这个案例写出正确的 css 吗?我不能使用 Javascript,它应该是 css-only 解决方案。

td{
  border: 1px solid black;
  width: 100%;
}
td.first-column {
  max-width: 50%;
  min-width: 10px;
}
<table style="width:100%;">
<tr>
  <td class="first-column">very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very label</td>
  <td class="second-column"><input style="width:100%"/></td>
</tr>
</table>

<table style="width:100%;">
<tr>
  <td class="first-column">short label</td>
  <td class="second-column"><input style="width:100%"/></td>
</tr>
</table>

我自己的建议是,首先——除非由于其他原因这不切实际——将所有标签和 <input> 元素合并到同一个父元素中,这样可以简化所有列的对齐。

为此,我将元素分组为单个 <fieldset> 元素,它本身位于 <form>:

// This JavaScript has no part to play in the resizing of elements,
// but merely logs the size of each of the two columns in the
// relevant elements (the last <label> and the last <input>:
const D = document,
            logLengths = () => {
    let label = D.querySelector('label:last-of-type'),
        input = D.querySelector('input:last-of-type');
  [label, input].forEach(
    (el) =>{
        el[ 
        el.tagName.toLowerCase() === 'input' ? 'value' : 'textContent'
        ] = Math.round(el.getBoundingClientRect().width*10)/10 + 'px';
    });
};

logLengths();
D.querySelectorAll('label').forEach(
    (label) => label.addEventListener('input', logLengths)
);
/* Generic CSS reset: */
*,
::before,
::after {
  box-sizing: border-box;
  font-size: 16px;
  line-height: 1.5;
  margin: 0;
  padding: 0;
}

form {
  border: 1px solid currentColor;
  /* using CSS logical properties to place a 1em margin
     on the block axis (top/bottom) in left-to-right, top-
     to-bottom languages) */
  margin-block: 1em;
  /* ...and a margin of auto (to centre the element) on
     the inline-axis (left and right in ltr languages): */
  margin-inline: auto;
  padding: 0.2em;
  width: 90vw;
}

fieldset {
  /* in order to use CSS Grid: */
  display: grid;
  /* setting gaps/'gutters' between rows of 0.5em,
     and 0.25em gaps between adjacent columns: */
  gap: 0.5em 0.25em;
  /* defining two columns, the first of which is sized
     'auto', allowing the layout to be sized appropriately
     to the content within the limits of other columns,
     the second column is sized using the 'minmax()'
     function between a minimum size of 50% (because the
     maximum permitted size of the first column is 50%),
     and 1fr, which is the fractional unit of the remaining
     space available: */
  grid-template-columns: auto minmax(50%, 1fr);
}

/* this styles the 'information' <div> that provides
   guidance about the interactivity of the editable
   <label> elements: */
fieldset div {
  grid-column: 1 / -1;
  text-align: center;
  padding: 0 2rem;
}

code, kbd {
  font-family: consolas, ubuntu mono, monospace;
}

kbd {
  border: 1px solid #aaa;
  border-radius: 0.4em;
  padding: 0.1em 0.3em;
}

label {
  /* to indicate interactivity: */
  cursor: pointer;
  /* for positioning the pseudo element: */
  position: relative;
}

/* entirely irrelevant to the demo, but just to
   make it look a little prettier (adjust to
   taste or remove as you like): */
label::before {
  content: '';
  background: linear-gradient(90deg, lime, #ffff);
  height: 100%;
  position: absolute;
  transform: scaleX(0);
  transition: transform 0.3s ease-in-out;
  transform-origin: 0 100%;
  width: 100%;
  z-index: -1;
}

label:hover::before {
  transform: scaleX(1);
}

.length {
  background: revert;
  background-color: #fff;
  border-color: transparent;
  border-top: 1px solid currentColor;
  cursor: not-allowed;
  font-style: italic;
  text-align: center;
}
<form action="#">
  <fieldset>
  <!-- this <div> is here simply to provide some guidance as to the
       interactivity of the <label> elements for the purpose of this
       demo; otherwise it can be removed entirely: -->
  <div>Most labels are <code>contentEditable</code>, though they will focus the &lt;input&gt; element when clicked; <kbd>shift</kbd>&plus;<kbd>tab</kbd> to focus the &lt;label&gt;</div>
    <!-- because we're using a grid (and the majority of browsers don't yet
         support display: subgrid) to create the aligned columns, the <input>
         elements are the following-siblings of the <label> elements; in order
         to associate the <label> with the correct <input> I've added an 'id'
         attribute to each <input>, and the same id-value for the associated
         <label> in their 'for' attribute; this means clicking/tapping on a
         <label> will focus the appropriate <input>: -->
    <label for="inputElement_0" contentEditable>longer label</label>
    <input id="inputElement_0">
    <label for="inputElement_1" contentEditable>label</label>
    <input id="inputElement_1">
    <label for="inputElement_2" contentEditable>medium label</label>
    <input id="inputElement_2">
    <label for="inputElement_3" contentEditable>label</label>
    <input id="inputElement_3">
    <label for="inputElement_4" contentEditable>label</label>
    <input id="inputElement_4">
    <label for="inputElement_5" contentEditable>label</label>
    <input id="inputElement_5">
    <!-- these elements are here purely to report the length of the 
         grid-columns in which they appear: -->
    <label for="lengthInput" class="length"></label>
    <input id="lengthInput" type="text" class="length" readonly>
  </fieldset>
</form>

JS Fiddle demo.

如果 subgrid 在您的浏览器中可用(目前它仅在 Firefox 中可用),那么以下是可能的:

// Again, this JavaScript has nothing to do with the resizing, but only shows
// the widths of the grid-tracks the elements are in:
const D = document,
  logLengths = () => {
    let label = D.querySelector('label:last-of-type'),
      input = label.querySelector('input:last-of-type');
    [label, input].forEach(
      (el) => {
        let width = Math.round(el.getBoundingClientRect().width / 10) * 10,
          val = `${width}px`;
        if (el.matches('label')) {
          label.firstChild.nodeValue = val;
        } else if (el.matches('input')) {
          el.value = val;
        }
      });
  };

logLengths();
D.querySelectorAll('label').forEach(
  (label) => label.addEventListener('input', logLengths)
);
*,
::before,
::after {
  box-sizing: border-box;
  font-size: 16px;
  line-height: 1.5;
  margin: 0;
  padding: 0;
}

form {
  border: 1px solid currentColor;
  margin-block: 1em;
  margin-inline: auto;
  padding: 0.2em;
  width: 90vw;
}

fieldset {
  display: grid;
  gap: 0.5em 0.25em;
  grid-template-columns: auto minmax(50%, 1fr);
}

fieldset div {
  grid-column: 1 / -1;
  text-align: center;
  padding: 0 2rem;
}

code,
kbd {
  font-family: consolas, ubuntu mono, monospace;
}

kbd {
  border: 1px solid #aaa;
  border-radius: 0.4em;
  padding: 0.1em 0.3em;
}

label {
  cursor: pointer;
  position: relative;
  /* Using display: grid to allow us to make use
     of subgrid: */
  display: grid;
  /* instructing the layout engine to use the
     grid-columns/grid-tracks of the parent
     element: */
  grid-template-columns: subgrid;
  /* positioning the grid-start in the first
     grid-column and the grid-end in the last
     grid-column: */
  grid-column: 1 / -1;
}

label::before {
  content: '';
  background: linear-gradient(90deg, lime, #ffff);
  height: 100%;
  position: absolute;
  transform: scaleX(0);
  transition: transform 0.3s ease-in-out;
  transform-origin: 0 100%;
  width: 100%;
  z-index: -1;
}

label:hover::before {
  transform: scaleX(1);
}

.length {
  background: revert;
  background-color: #fff;
  border-color: transparent;
  border-top: 1px solid currentColor;
  cursor: not-allowed;
  font-style: italic;
  text-align: center;
}
<form action="#">
  <fieldset>
    <div>All labels are <code>contentEditable</code>, though they will focus the &lt;input&gt; element when clicked; <kbd>shift</kbd>&plus;<kbd>tab</kbd> to focus the &lt;label&gt;</div>
    <!-- because we can use subgrid, we can place the <input> elements
         inside of their associated <label> elements, which removes the
         need to assign either a 'for' attribute to the <label> or an
         'id' attribute to the <input>, since they're automatically
         associated this way via nesting: -->
    <label contentEditable>longer label
      <input>
    </label>

    <label contentEditable>label
      <input>
    </label>
    <label contentEditable>medium label
      <input>
    </label>
    <label contentEditable>label
      <input>
    </label>
    <label contentEditable>label
      <input>
    </label>
    <label contentEditable>label
      <input>
    </label>
    <label class="length">
      <input type="text" class="length" readonly>
    </label>
  </fieldset>
</form>

JS Fiddle demo.

参考文献: