CSS 在响应式网格上设置多个“相同高度”行部分的唯一解决方案

CSS only solution to set MULTIPLE “same height” row sections on a responsive grid

想要:一个CSS唯一的解决方案,可以在每行的基础上启用多个等高网格"sections",这也是响应式的。

注意:这是 的后续问题,每个项目只有一个 "equal height" 部分 - 这可以通过 flexbox

实现

下图应该有助于解释需求:

"item grid" 应该是响应式的 - 因为它可以根据视口宽度在每行显示不同数量的卡片(台式机上 4 个,移动设备上 2 个)。在给定的行中,等效的 "content" 和 "feature" 部分应该具有相同的高度。

在下面 HTML 和 CSS - 项目卡片被分成我们需要的行(在两个示例断点桌面和移动)但内容部分高度是可变的:

.items {
  max-width: 1200px;
}

.item {
  width: 25%;
  box-sizing: border-box;
  display: inline-block;
  vertical-align: top;
  padding: 0 12px;
  margin: 24px -4px 24px 0;
}

@media (max-width: 600px) {
  .item {
    width: 50%;
  }
}

.item__heading {
  background-color: #d4d0f5;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}

.item__content {
  padding: 10px;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
}

.item__features {
  padding: 10px;
  border-top: 1px solid #bbbbbb;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
  background-color: #f7cbb1;
}

.item__features ul {
  margin: 0px;
}

.item__price {
  background-color: #e0f6d9;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}
<div class="items">

  <div class="item">
    <div class="item__heading">
      Item 1
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £99.99
    </div>
  </div>


  <div class="item">
    <div class="item__heading">
      Item 2
    </div>
    <div class="item__content">
      Some content that is longer than other items on the same row and sets the height of this section as it spans many more lines than the rest of the other content sections on this row
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £69.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 3
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>                         <li>feature 2</li>
        <li>feature 3</li>
      </ul>
    </div>
    <div class="item__price">
      £69.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 4
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £109.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 5
    </div>
    <div class="item__content">
      Some content that is a medium kind of length blah blah
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £29.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 6
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
        <li>feature 2</li>
      </ul>
    </div>
    <div class="item__price">
      £99.99
    </div>
  </div>

    
</div>

我创建了以下代码笔作为实现预期结果的基于 JavaScript 的解决方案 - 但我希望尽可能用 CSS 解决方案替换它:http://codepen.io/rusta/pen/xdmdxm

限制

基于 Flexbox 的解决方案似乎无法应对项目有多个部分需要对齐的事实

我希望新的 CSS 网格系统能够帮助实现上述目标,但我已经尝试了几次,但都没有成功,所以我将其开放给社区,看看我是否遗漏了东西

进一步说明:我说的是 CSS 唯一的解决方案,我指的是非 JS 解决方案。如果 HTML 块需要更改(order/nesting/class 名称)以支持非 JS 解决方案,这是一个可行的选择

我正在为我自己的问题提供一个答案 - 但我会推迟接受它,以防其他人提出更好的答案,因为这个答案无疑会打破一些可访问性规则并且几乎不可能为不支持 CSS 网格但...

的浏览器提供优雅的回退

如果您将 HTML 内容项分成四个块,您可以使用 CSS 网格样式规则实现所需的结果,不需要 JavaScript

.items {
  max-width: 1200px;
}

.item__heading {
  margin-top: 30px;
  background-color: #d4d0f5;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}

.item__content {
  padding: 10px;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
}

.item__features {
  padding: 10px;
  border-top: 1px solid #bbbbbb;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
  background-color: #f7cbb1;
}

.item__price {
  background-color: #e0f6d9;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}

/* DESKTOP GRID */

/* DESKTOP COLUMN LAYOUT - 4 columns, one row */
.item-block-of-four {
  display: grid;
  grid-template-columns: 2% 22.5% 2% 22.5% 2% 22.5% 2% 22.5% 2%;
}
.item-1 {
  grid-column-start: 2;
  grid-column-end: 3;
}
.item-2 {
  grid-column-start: 4;
  grid-column-end: 5;
}
.item-3 {
  grid-column-start: 6;
  grid-column-end: 7;
}
.item-4 {
  grid-column-start: 8;
  grid-column-end: 9;
}

/* ROW LAYOUT - one row for all 4 items */
.item__heading {
  grid-row-start: 1;
  grid-row-end: 2;
}
.item__content {
  grid-row-start: 2;
  grid-row-end: 3;
}
.item__features {
  grid-row-start: 3;
  grid-row-end: 4;
}
.item__price {
  grid-row-start: 4;
  grid-row-end: 5;
}

/* MOBILE GRID */

@media (max-width: 600px) {
  /* MOBILE COLUMN LAYOUT - 2 columns 2 rows */
  .item-block-of-four {
    display: grid;
    grid-template-columns: 6% 41% 6% 41% 6%;
  }
  .item-1, .item-3 {
    grid-column-start: 2;
    grid-column-end: 3;
  }
  .item-2, .item-4 {
    grid-column-start: 4;
    grid-column-end: 5;
  }

  /* MOBILE ROW LAYOUT - two sets of rows */

  /* first row set */
  .item-1.item__heading, .item-2.item__heading {
    grid-row-start: 1;
    grid-row-end: 2;
  }

  .item-1.item__content, .item-2.item__content {
    grid-row-start: 2;
    grid-row-end: 3;
  }

  .item-1.item__features, .item-2.item__features {
    grid-row-start: 3;
    grid-row-end: 4;
  }

  .item-1.item__price, .item-2.item__price {
    grid-row-start: 4;
    grid-row-end: 5;
  }

  /* second row set */
  .item-3.item__heading, .item-4.item__heading {
    grid-row-start: 6;
    grid-row-end: 7;
  }

  .item-3.item__content, .item-4.item__content {
    grid-row-start: 7;
    grid-row-end: 8;
  }

  .item-3.item__features, .item-4.item__features {
    grid-row-start: 8;
    grid-row-end: 9;
  }

  .item-3.item__price, .item-4.item__price {
    grid-row-start: 9;
    grid-row-end: 10;
  }
}
<div class="items">

  <div class="item-block-of-four">

    <div class="item-1 item__heading">
      Item 1
    </div>
    <div class="item-2 item__heading">
      Item 2
    </div>
    <div class="item-3 item__heading">
      Item 3
    </div>
    <div class="item-4 item__heading">
      Item 4
    </div>

    <div class="item-1 item__content">
      Some content that is not that long
    </div>
    <div class="item-2 item__content">
      Some content that is longer than other items on the same row and sets the height of this section as it spans many more lines than the rest of the other content sections on this row
    </div>
    <div class="item-3 item__content">
      Some content that is not that long
    </div>
    <div class="item-4 item__content">
      Some content that is not that long
    </div>

    <div class="item-1 item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item-2 item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item-3 item__features">
      <ul>
        <li>feature 1</li>
        <li>feature 2</li>
        <li>feature 3</li>
      </ul>
    </div>
    <div class="item-4 item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>

    <div class="item-1 item__price">
      £99.99
    </div>
    <div class="item-2 item__price">
      £69.99
    </div>
    <div class="item-3 item__price">
      £69.99
    </div>
    <div class="item-4 item__price">
      £109.99
    </div>

  </div><!-- /item-block-of-four -->

  <div class="item-block-of-four">

    <div class="item-1 item__heading">
      Item 5
    </div>
    <div class="item-2 item__heading">
      Item 6
    </div>

    <div class="item-1 item__content">
      Some content that is a medium kind of length blah blah
    </div>
    <div class="item-2 item__content">
      Some content that is not that long
    </div>

    <div class="item-1 item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item-2 item__features">
      <ul>
        <li>feature 1</li>
        <li>feature 2</li>
      </ul>
    </div>

    <div class="item-1 item__price">
      £29.99
    </div>
    <div class="item-2 item__price">
      £99.99
    </div>

  </div><!-- /item-block-of-four -->

</div><!-- /items-container -->

根据您自己的回答,您将它们按 4 分组,您也可以使用 CSS Flexbox 来做到这一点。

要使它们在少于 4 时表现良好,可以使用 nth-child 选择器来实现,但使用 last* class 更简单,所以我选择了后者。

甚至可以在没有 .group_of_4 包装器的情况下使用一些聪明的 nth-child 规则来做到这一点,但是同样,为了更简单,因为它没有附带任何明显的限制

Fiddle demo

.items {
  display: flex;
  flex-direction: column;
  max-width: 1200px;
}

.items .group_of_4 {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;        /*  updated  */
}

.items .group_of_4 ~ .group_of_4 {
  margin-top: 24px;
}

.items .group_of_4 > div {
  width: calc(25% - 12px);                /*  updated  */
  box-sizing: border-box;
  padding: 12px;
}


.item__heading {
  background-color: #d4d0f5;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
  order: 1;
}

.item__content {
  padding: 10px;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
  order: 2;
}

.item__features {
  padding: 10px;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
  background-color: #f7cbb1;
  order: 3;
}

.item__price {
  background-color: #e0f6d9;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
  order: 4;
}

/* one item in a group */
.items .group_of_4 .last1 {
    margin-right: calc(75%  6px);        /*  updated  */
}
/* two items in a group */
.items .group_of_4 .last2 {
    margin-right: calc(50% + 6px);       /*  updated  */
}
/* three items in a group */
.items .group_of_4 .last3 {
    margin-right: calc(25% + 6px);       /*  updated  */
}

@media (max-width: 600px) {
  .items .group_of_4 > div:nth-child(8) ~ .item__heading {
    margin-top: 24px;
    order: 5;
  }
  .items .group_of_4 > div:nth-child(8) ~ .item__content {
    order: 6;
  }
  .items .group_of_4 > div:nth-child(8) ~ .item__features {
    order: 7;
  }
  .items .group_of_4 > div:nth-child(8) ~ .item__price {
    order: 8;
  }
  
  .items .group_of_4 > div {
    width: calc(50% - 12px);             /*  updated  */
  }

  /* one item in a group */
  /* three items in a group */
  .items .group_of_4 .last1,
  .items .group_of_4 .last3 {
    margin-right: 50%;
  }
  /* two items in a group */
  .items .group_of_4 .last2 {
    margin-right: 0%;
  }  
}
<div class="items">

  <div class="group_of_4">
    <div class="item__heading">
      Item 1
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £99.99
    </div>

    <div class="item__heading">
      Item 2
    </div>
    <div class="item__content">
      Some content that is longer than other items on the same row and sets the height of this section as it spans many more lines than the rest of the other content sections on this row
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £69.99
    </div>

    <div class="item__heading">
      Item 3
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
        <li>feature 2</li>
        <li>feature 3</li>
      </ul>
    </div>
    <div class="item__price">
      £69.99
    </div>

    <div class="item__heading">
      Item 4
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £109.99
    </div>
  </div>
  
  
  <div class="group_of_4">
    <div class="item__heading">
      Item 5
    </div>
    <div class="item__content">
      Some content that is a medium kind of length blah blah
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £29.99
    </div>

    <div class="item__heading last2">
      Item 6
    </div>
    <div class="item__content last2">
      Some content that is not that long
    </div>
    <div class="item__features last2">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price last2">
      £99.99
    </div> 

  </div>
</div>


这是一个基于脚本的解决方案,适用于任何需要它的人。

它缺少一个调整大小事件处理程序,它会重新计算每行的最大高度。

(function(d) {
  window.addEventListener("load", function() {
    var item = d.querySelector('.items');
    var items = item.querySelectorAll('.item__features');
    var heights = [], i = 0, css;
    for (i = 0; i < items.length; i++) {
      heights.push(parseFloat(window.getComputedStyle(items[i], null).getPropertyValue("height")));
    }
    css = ".item__features { height: " + Math.max.apply(null, heights) + "px; }" ;
    var st = d.createElement('style');
    st.type = 'text/css';
    if (st.styleSheet) {
      st.styleSheet.cssText = css
    } else {
      st.appendChild(d.createTextNode(css));
    }
    (d.head || d.getElementsByTagName('head')[0]).appendChild(st);
  }, false);
}(document));
.items {
  display: flex;
  flex-wrap: wrap;
  max-width: 1200px;
}

.item {
  display: flex;
  flex-direction: column;
  width: 25%;
  box-sizing: border-box;
  padding: 0 12px;
  margin: 24px -4px 24px 0;
}

@media (max-width: 600px) {
  .item {
    width: 50%;
  }
}

.item__heading {
  background-color: #d4d0f5;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}

.item__content {
  flex: 1 1 auto;
  padding: 10px;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
}

.item__features {
  padding: 10px;
  border-top: 1px solid #bbbbbb;
  border-left: 1px solid #bbbbbb;
  border-right: 1px solid #bbbbbb;
  background-color: #f7cbb1;
}

.item__features ul {
  margin: 0px;
}

.item__price {
  background-color: #e0f6d9;
  padding: 10px;
  text-align: center;
  border: 1px solid #bbbbbb;
}
<div class="items">

  <div class="item">
    <div class="item__heading">
      Item 1
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £99.99
    </div>
  </div>


  <div class="item">
    <div class="item__heading">
      Item 2
    </div>
    <div class="item__content">
      Some content that is longer than other items on the same row and sets the height of this section as it spans many more lines than the rest of the other content sections on this row
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £69.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 3
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
        <li>feature 2</li>
        <li>feature 3</li>
      </ul>
    </div>
    <div class="item__price">
      £69.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 4
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £109.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 5
    </div>
    <div class="item__content">
      Some content that is a medium kind of length blah blah
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
      </ul>
    </div>
    <div class="item__price">
      £29.99
    </div>
  </div>

  <div class="item">
    <div class="item__heading">
      Item 6
    </div>
    <div class="item__content">
      Some content that is not that long
    </div>
    <div class="item__features">
      <ul>
        <li>feature 1</li>
        <li>feature 2</li>
      </ul>
    </div>
    <div class="item__price">
      £99.99
    </div>
  </div>

</div>