Grid -在不知道子节点大小的情况下,自动列的最大数量为nn



我正试图想出一种方法,有一个网格状的容器与nn最大的列数,但它会包装成更少的列,如果需要适应屏幕的宽度。

其中一个问题是孩子的大小是未知的,第二个问题,有一个粘性的伪元素在容器中,这也将不得不调整整个行生成的大小。

在下面的例子中,它设置为最大4列,但当容器调整大小时,它不会包装成更少的列。(另请注意,可调整大小的容器只是为了模拟屏幕大小,实际上它将是其内容的大小)

.box
{
--cols: 4;
display: grid;
grid-template-columns: repeat(var(--cols), 1fr);
min-width: min-content;
min-height: 4em;
overflow: auto;
resize: both;
border: 1px solid;
}
.box::before
{
content: "sticky element";
position: sticky;
top: 0;
background-color: lightblue;
grid-column-start: 1;
grid-column-end: calc(var(--cols) + 1);
height: 1em;
text-align: center;
}
.box > *
{
max-width: 3em;
max-height: 3em;
min-width: 3em;
min-height: 3em;
border: 1px solid;
background-color: pink;
}
<div class="box">
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
</div>

它不一定是网格,只要有一种方法可以在不改变html结构的情况下拥有最大数量的列。

提示吗?

如您所愿,我创建了一个Flexbox布局解决方案,做你想要的,但不能满足你的所有要求(目前)。作为一个"提示",我们可以讨论以下内容,并使其按要求工作。

中间解决方案

对于flexbox父容器(演示.grid),您可以使用column-gap值强制行内容换行到下一行:

  • 调整项目列间距相对于当前父客户端宽度所需列数除以列数

基本上取当前父宽度,减去所需子列的最大数量的总宽度,并在这些列之间分配剩余的空间。Flexbox机制将处理将CSS父属性和子属性值考虑在内的包装。的问题是需要知道列宽度,以便从父宽度中减去总列宽度。(我知道你不知道,因此,'tip'!)

  • 方程:columngap = (clientWidth - columnWidth * columns) / columns
  • css:column-gap: max(0px, calc( (var(--clientWidth) * 1px - var(--colSize) * var(--cols)) / var(--cols)))。用max(..)夹紧0px以防止产生负空间。

由于缺乏适当的CSS网格知识,我无法使上述工作与您的原始代码。Flexbox Layout似乎如我所料。

  • Javascript包含一个简单的ResizeObserver,在.grid调整大小时,将当前的grid.clientWidth传递给CSS自定义变量--clientWidth,用于上面的CSS计算。(未使用的clientHeight只是为了完整)

有趣的观察:CSS网格机制似乎在某些情况下使用类似的计算,因为原始的.box CSS grid间距等于建议的.grid flexbox calc(..)

当你运行代码片段时,点击"整页",尝试范围滑块并首先调整浏览器的大小,因为使用'resize-handle'会破坏常规的浏览器窗口大小调整效果。

顺便说一句,启用'console'后,代码片段仅在Stackoverflow上显示Javascript错误。在其他地方似乎不存在……

/* Reference to parent grid elements involved */
const grids = document.querySelectorAll(".grid");
// Set variables to initial values per grid
window.onload = function () { grids.forEach((grid) => { setVariables(grid) }) };
// Create a resize observer to set variables with changed grid size
const ro = new ResizeObserver(list => {
for (let entry of list) { setVariables(entry.target); }
});
// But only observe .grid(s)
grids.forEach((element) => { ro.observe(element) });
// Set custom variables to current size to enable use in CSS
function setVariables(grid) {
grid.style.setProperty("--clientWidth" , grid.clientWidth );
grid.style.setProperty("--clientHeight", grid.clientHeight); // here for completeness
};
* { box-sizing: border-box }
body {
--cols   : 4;
--colSize: 3em;
}
.grid {
position: relative; /**/
resize: both; overflow: auto;
display: flex; flex-flow: row wrap;
/* Flexbox child alignment, enable/change/fiddle what fits best */
/*    justify-content: start; /* Main axis */
/*    align-content  : start; /* Cross axis */
/*    align-items    : center; /* perpendicular to Main axis, when applicable */

column-gap: max(0px, calc( (var(--clientWidth) * 1px - var(--colSize) * var(--cols)) / var(--cols)));
/* Change to 'gap' for equal horizontal AND vertical speading */

width: 100%; /* stretch to fill parent width */
min-height: 4em;
border: 1px solid;
}
.grid::before {
content: "sticky element (solution .grid)";
position: sticky;
/* Shorthand for T/R/B/L; Over content */
inset: 0 0 auto 0; z-index: 1;
width: 100%; /* required, despite inset and z-index. Huh? */
/* Even parent `position: relative` has no effect. */
/* 'flex-basis: 100%' instead would work too */

height: 1.5em; /* 'min-height' maybe? For when line breaks? */
background-color: lightblue;
text-align: center;
}
.grid > * {
width : var(--colSize); /* Use range slider value */
height: var(--colSize);
border: 1px solid;
background-color: pink;
}
/* This and thats for the demo */
input[type="range"] { width: 100% }
label, input        { display: block; font-size: 1em }
label,li            { margin-bottom: 0.5rem }

/*******************************************
Original code with modded '.box > *'
using the '--colSize' variable for
comparison purposes.
Also: moved '--cols' to 'body' above.
*******************************************/
.box {
display: grid;
grid-template-columns: repeat(var(--cols), 1fr);
min-width: min-content;
min-height: 4em;
overflow: auto;
resize: both;
border: 1px solid;
}
.box::before {
content: "sticky element (original .box)";
position: sticky;
top: 0;
background-color: lightblue;
grid-column-start: 1;
grid-column-end: calc(var(--cols) + 1);
height: 1em;
text-align: center;
}
.box > * {
max-width : var(--colSize);
max-height: var(--colSize);
min-width : var(--colSize);
min-height: var(--colSize);
border: 1px solid;
background-color: pink;
}
<label><b>columns</b>&nbsp;-&nbsp;[1 - 9] <span id="info-cols"></span>
<input type="range" min="1" max="9" step="1" value="4"
oninput="document.body.style.setProperty('--cols', this.value);
document.getElementById('info-cols').innerHTML = ' > ' + this.value;">
</label>
<label><b>column size</b>&nbsp;-&nbsp;[1 - 15em] <span id="info-size"> &gt; 3em</span>
<input type="range" min="1" max="15" step="0.25" value="3"
oninput="document.body.style.setProperty('--colSize', this.value + 'em');
document.getElementById('info-size').innerHTML = ' > ' + this.value + 'em';">
</label>
<br>
<div class="box">
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
</div>
<br>
<div class="grid">
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
</div>

最新更新