【问题标题】:CSS: How to prevent empty cells in grid (by removing partially empty rows)CSS:如何防止网格中的空单元格(通过删除部分空行)
【发布时间】:2020-03-03 02:41:09
【问题描述】:

我正在尝试创建一个 div 网格,所有这些 div 都需要 X 像素宽,并根据需要包含尽可能多的行来适应所有这些。我不知道外部 div 的宽度。使用 css 网格很容易做到这一点:

#container {
  resize: both;
  overflow: auto;
  width: 170px;

  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
  
}

#container div {
  border: 1px solid red;
}
<div id="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
  <div>8</div>
</div>

现在,我的问题是我不希望网格中有任何空单元格(出于美学原因)。所以我想隐藏最后一行的所有元素,如下所示:

请注意,让网格适合 3 列只是问题的一个特例:如果我可以在网格中适合 5 列,我想删除第二行的所有项目。

是否可以在 CSS 中实现这一点? 理想情况下,无需手动添加媒体断点并手动隐藏 css 中的项目?

如果不行,可以用Javascript解决吗?

请注意,尽管我在示例中使用了 css display:grid,但这不是必需的 - 我想要的只是看起来像网格的东西。

【问题讨论】:

  • 不,CSS 不能检测溢出等,所以你需要 Javascript
  • 不知道列数是这里的杀手锏。使用 :nth-child 和 :last,您可以检测该行是否已满...您需要根据列数隐藏媒体查询。

标签: javascript html css css-grid


【解决方案1】:

仅使用 CSS 是无法实现的。 如果您在 html 中像这样分隔行,则可能是这样:

<div class="row">
    <div class="cell">1</div>
    <div class="cell">2</div>
    <div class="cell">3</div>
</div>
<div class="row">
    <div class="cell">4</div>
    <div class="cell">5</div>
    <div class="cell">6</div>
</div>
<div class="row">
    <div class="cell"></div>
    <div class="cell">7</div>
    <div class="cell">8</div>
</div>

并确保行中的第一个单元格是那个空的 然后您可以使用伪选择器:empty 和通用兄弟选择器~ 编写CSS 选择器

.row {
  display: block;
}

.cell {
  display: inline-block;
  width: 70px;
  height: 25px;
  background: yellow;
}

.cell:empty,
.cell:empty ~ .cell {
  display: none;
}
<div class="row">
    <div class="cell">1</div>
    <div class="cell">2</div>
    <div class="cell">3</div>
</div>
<div class="row">
    <div class="cell">4</div>
    <div class="cell">5</div>
    <div class="cell">6</div>
</div>
<div class="row">
    <div class="cell"></div>
    <div class="cell">7</div>
    <div class="cell">8</div>
</div>

另一种方法是使用javascript,这应该有点微不足道

const hideRowIfAnyCellEmpty = () => {
  const rows = document.querySelectorAll('.row');
  rows.forEach((row) => {
    if (hasRowEmptyCell(row)){
      row.classList.add('has-empty-cell');
    }
  });
}

const hasRowEmptyCell = (row) => {
  return row.querySelectorAll('.cell:empty').length;
};

hideRowIfAnyCellEmpty();
.row {
  display: block;
}

.row.has-empty-cell {
  display: none;
}

.cell {
  display: inline-block;
  width: 70px;
  height: 25px;
  background: yellow;
}
<div class="row">
    <div class="cell">1</div>
    <div class="cell">2</div>
    <div class="cell">3</div>
</div>
<div class="row">
    <div class="cell">4</div>
    <div class="cell">5</div>
    <div class="cell">6</div>
</div>
<div class="row">
    <div class="cell"></div>
    <div class="cell">7</div>
    <div class="cell">8</div>
</div>

【讨论】:

  • 谢谢!我以前没见过 :empty 选择器,很有趣。但是,由于我不知道前面的列数,我无法以这种方式呈现 HTML。
【解决方案2】:

一个技巧是考虑一个可以隐藏它们的伪元素。当然,你不会有透明度,你的元素也不适合容器的高度。

#container {
  resize: both;
  overflow: hidden;
  width: 170px;

  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
  
}

#container div {
  border: 1px solid red;
}

#container:after {
  content:"";
  background:#fff;
  margin-left:-100vw;
  margin-right:20px;
  
}
<div id="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
  <div>8</div>
</div>

【讨论】:

  • 谢谢 Temani!不幸的是,在我的情况下,我想在它的正下方显示另一个元素,所以通过在前面放置一些东西来隐藏它对我不起作用
【解决方案3】:

由于似乎仅使用 CSS 是不可能的,所以最后我决定使用 javascript 解决这个问题,如下所示:

var cg = document.getElementById('container');

var style = document.createElement('style');
document.head.appendChild(style);

var hideHalfEmptyColumns = function() {
  // How many columns it's gonna be
  var columns = Math.floor(cg.offsetWidth / 50);
  // How many elements are showing
  var elements = cg.childElementCount;
  // How many cells are on completely filled rows
  var elementsToShow = columns * Math.floor(elements / columns);
  style.innerHTML = `
            #container>div:nth-child(n + ${elementsToShow+1}) {
                display: none;
            }`;
}

// In the real world I'd run this on the window resize event as well, but since elements does not have this function the example is a bit broken
hideHalfEmptyColumns();
#container {
  resize: both;
  overflow: auto;
  width: 170px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
}

#container div {
  border: 1px solid red;
}
<div id="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
  <div>8</div>
</div>

【讨论】: