英文:
Is it possible to adjust div spacing with just css when user data is dynamically absent?
问题
我正在尝试实现的目标是:
我有一个卡片元素,左边有一张图像,右边有一些行。使用网格,它可能会像这样:
.container>* {
border: 1px solid black;
}
.container {
display: grid;
grid-template-columns: [first] 70px [second] 1fr;
grid-template-rows: 1fr 25% 25% 25% [lastRow];
height: 70px;
width: 220px;
grid-template-areas: "date title" "date location" "date notes" "date links";
}
.title {
grid-area: title;
overflow: hidden;
}
.location {
grid-area: location;
}
.notes {
grid-area: notes;
}
.links {
grid-area: links;
}
.date {
grid-area: date;
}
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='location'>location</div>
<div class='notes'>notes</div>
<div class='links'>links</div>
<div class='date'>date</div>
</div>
有一些条件:可能不会出现某些行(只有title
是确保存在的),并且标题可能非常长。溢出是可以接受的,但如果在title
下方缺少行,则我希望它尽可能多地占用该空间,同时仍然允许溢出工作。理想情况下,下面的所有其他行都会移到底部(以尽可能多地为标题提供空间),但我可以在服务器端通过更改行的区域来管理这一点(但我宁愿不更改CSS,因为页面上有多个这些卡片)。
另外,如果所有行都存在,它们应该占用相同的高度。
问题:是否可以只使用CSS实现这个目标?
我尝试过:
我尝试将grid-row-end:lastRow;
添加到.title
类,但虽然这有助于实现间距效果,但它破坏了overflow
,因为元素现在认为它可以使用直到卡片末尾的所有空间,因此文本出现在其他元素的后面。
我还尝试过通过flex来实现这一点。实际上,这是我最接近的解决方案,但我无法弄清楚如何在缺少元素时调整title
行以占用更多空间(请参见我的尝试以下,注意如果删除带有item2或item3的div中的所有内容会发生什么)。
我将flex-grow
设置为所有元素的值都为1,因为当它们都存在时,它们需要相同的值,但对于空元素,将其设置为0。这会删除它们,但在剩余项目之间均匀分配高度,但我希望所有额外的高度都分配给title
元素。我还无法弄清楚是否可以使用flex-basis
来实现这一点。
编辑:最终,我选择了flexbox解决方案,因为它在我的测试中具有非常大的优势:所有行都可以根据需要变得更大,而无需额外的操作。
我对接受的答案进行了一些更改:
一个问题是当非标题行也很长时,它们会优先增长。为了解决这个问题,我在container>*
中添加了min-height:<font-height>
,并在.container>:empty
中添加了min-height:0px
。然后,我为titlerow设置了flex-grow: 2
,并为所有其他元素设置了flex: 1 1 auto
。这允许优雅地处理多行太长的情况。
由于我还希望在所有元素都存在时保持相等的高度,我在.container >*
中设置了max-height:25%
,并在:has
选择器中取消了这个设置,以确保空元素保持隐藏。但如果不需要等高线要求,可以放弃max-height和整个:has选择器。
英文:
What I'm trying to achieve:
I have a card element that has an image on the left, with a number of rows on the right. With grid, it might look something like this:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.container>* {
border: 1px solid black;
}
.container {
display: grid;
grid-template-columns: [first] 70px [second] 1fr;
grid-template-rows: 1fr 25% 25% 25% [lastRow];
height: 70px;
width: 220px;
grid-template-areas: "date title" "date location" "date notes" "date links";
}
.title {
grid-area: title;
overflow: hidden;
}
.location {
grid-area: location;
}
.notes {
grid-area: notes;
}
.links {
grid-area: links;
}
.date {
grid-area: date;
}
<!-- language: lang-html -->
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='location'>location</div>
<div class='notes'>notes</div>
<div class='links'>links</div>
<div class='date'>date</div>
</div>
<!-- end snippet -->
There are a few conditions: some of the the rows might not be present (only title
is guaranteed), and title might be super long. Overflow is okay, but if rows are missing below title
, I would like it to take up as much of that space as possible, while still allowing overflow to work. Ideally, all of the other rows below would go to the bottom (to give title as much space as possible), but I'm able to manage that on the server side by changing which areas the lines go to (but I would rather not change the css because there are multiple of these cards on the page).
Also, if all rows are present, they should take up the same amount of height.
Question: Is this possible with just css?
What I've tried:
I tried adding grid-row-end:lastRow;
to the .title
class, but while this helped me achieve the spacing effect, it broke overflow
, as the element now thought that it had all of the space available to it up until the end of the card, so text appeared positioned behind other elements.
I also tried doing this through flex. This was actually the closest I got, but I wasn't able to figure out how to get the flex-grow
parameter to adjust the title
row to take up more space when an element was missing (see my attempt below, and note what happens if you remove everything inside one of the divs with item2 or item3).
I set the flex-grow to be 1 for all elements, since they need to be the same when they're all present, but set it to 0 for empty elements. This deletes them, but spreads the height between remaining items equally, but I want all of that extra height to go to the title
element. I also couldn't figure out if I could achieve this with flex-basis
.
Edit: I ended up going with the flexbox solution, because it has a very big advantage over the grid solution (at least in my testing): all lines can become bigger if they need to be, right out of the box.
I did make a few changes to the accepted answer:
One issue was when the non title lines were also long, they grew preferentially. To get around this, I added min-height:<font-height>
in container>*
and add min-height:0px
to .container>:empty
. Then I gave the titlerow flex-grow: 2
, and flex: 1 1 auto
for all other elements. This allows to gracefully handle multiple rows being too long.
Since I also wanted to maintain equal height when the elements all present, I set max-height:25%
in .container >*
, and unset this in the :has
selector, to make sure that empty elements stay hidden. But if the equal line requirement isn't necessary, you can ditch the max-height and the whole :has selector.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
p {
margin: 0;
padding-left: 1%;
padding-right: 1%;
text-align: center;
max-height: 100%;
}
.title {
overflow: hidden;
}
.container {
display: flex;
border: 1px solid red;
flex-direction: column;
height: 150px;
width: 80px;
}
.container>* {
flex: 1 1;
border: 1px solid #000;
display: flex;
justify-content: center;
align-items: center;
}
.container> :first-child {
flex: 1 1;
}
.container> :empty {
flex-grow: 0;
border: 0px;
padding: 0px;
}
<!-- language: lang-html -->
<div class="container">
<div class="title">
<p>item1 a really long piece of text that doesn't fit into our container at all</p>
</div>
<div>
<p>item2</p>
</div>
<div>
<p>item3</p>
</div>
</div>
<!-- end snippet -->
答案1
得分: 1
在flex
简写中为flex-basis
声明一个值是可选的。当省略时,flex-basis
将设置为0(替代默认值"auto")。
根据Flexbox示例,当存在空的子元素时,您可以使用:has()
伪类来声明flex-basis: auto
。
我可以使用:has()
吗? 目前显示支持率为87.03%。(在Firefox中默认情况下未启用,但用户不到3%。)
我还想指出:empty
元素必须完全为空,没有空白、换行等。
.container:has(> :empty) > * {
flex-basis: auto;
}
<div class="container">
<div class="title">
<p>row1 一段非常长的文本,完全不适合我们的容器</p>
</div>
<div>
<p>row2</p>
</div>
<div>
<p>row3</p>
</div>
<div>
<p>row4</p>
</div>
</div>
<hr>
<div class="container">
<div class="title">
<p>row1 一段非常长的文本,完全不适合我们的容器</p>
</div>
<div>
<p>row2</p>
</div>
<div></div>
<div>
<p>row4</p>
</div>
</div>
<hr>
<div class="container">
<div class="title">
<p>row1 一段非常长的文本,完全不适合我们的容器</p>
</div>
<div></div>
<div>
<p>row3</p>
</div>
<div></div>
</div>
英文:
Declaring a value for flex-basis
in the flex
shorthand is optional. When omitted, flex-basis
is given 0 (replacing the default value "auto".)
Going with the Flexbox example, you can use the :has()
pseudo-class to declare flex-basis: auto
on the child elements when there are empty child elements.
Can I use :has()
? currently shows 87.03% support. (It's not enabled by default in Firefox, but that's less than 3% of users.)
I also want to point out that the :empty
element must be completely empty with no whitespace, linebreaks, etc.
.container:has(> :empty) > * {
flex-basis: auto;
}
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
p {
margin: 0;
padding: 0 1%;
text-align: center;
max-height: 100%;
}
.title {
flex-grow: 1;
overflow: hidden;
}
.container {
display: flex;
border: 1px solid red;
flex-direction: column;
height: 150px;
width: 80px;
}
.container > * {
display: flex;
flex: 1 1;
justify-content: center;
align-items: center;
border: 1px solid;
}
.container > :empty {
flex-grow: 0;
border: 0;
}
.container:has(> :empty) > * {
flex-basis: auto;
}
<!-- language: lang-html -->
<div class="container">
<div class="title">
<p>row1 a really long piece of text that doesn't fit into our container at all</p>
</div>
<div>
<p>row2</p>
</div>
<div>
<p>row3</p>
</div>
<div>
<p>row4</p>
</div>
</div>
<hr>
<div class="container">
<div class="title">
<p>row1 a really long piece of text that doesn't fit into our container at all</p>
</div>
<div>
<p>row2</p>
</div>
<div></div>
<div>
<p>row4</p>
</div>
</div>
<hr>
<div class="container">
<div class="title">
<p>row1 a really long piece of text that doesn't fit into our container at all</p>
</div>
<div></div>
<div>
<p>row3</p>
</div>
<div></div>
</div>
<!-- end snippet -->
答案2
得分: 1
我已经简化了您的CSS,并使用了nth-child。这样我们可以使网格的行变得动态,可能解决了这个问题:
.container > * {
border: 1px solid black;
}
.container {
display: grid;
grid-template-columns: 70px 1fr;
grid-template-rows: 1fr;
grid-auto-rows: 25%;
height: 70px;
width: 220px;
margin-top: 5px;
}
.title {
overflow: hidden;
}
.title, .location, .notes, .links {
grid-column: 2;
}
.date {
grid-column: 1;
grid-row-start: 1;
}
.date:nth-child(5) {
grid-row-end: 5;
background-color: lightblue;
}
.date:nth-child(4) {
grid-row-end: 4;
background-color: lightgreen;
}
.date:nth-child(3) {
grid-row-end: 3;
background-color: lightyellow;
}
.date:nth-child(2) {
grid-row-end: 2;
background-color: coral;
}
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='location'>location</div>
<div class='notes'>notes</div>
<div class='links'>links</div>
<div class='date'>date</div>
</div>
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='location'>location</div>
<div class='links'>links</div>
<div class='date'>date</div>
</div>
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='location'>location</div>
<div class='date'>date</div>
</div>
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='date'>date</div>
</div>
英文:
I have simplified your CSS, and make some use of nth-child.
This way we can make the rows of the grid dynamic, and probably solve the problem:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.container > * {
border: 1px solid black;
}
.container {
display: grid;
grid-template-columns: 70px 1fr;
grid-template-rows: 1fr;
grid-auto-rows: 25%;
height: 70px;
width: 220px;
margin-top: 5px;
}
.title {
overflow: hidden;
}
.title, .location, .notes, .links {
grid-column: 2;
}
.date {
grid-column: 1;
grid-row-start: 1;
}
.date:nth-child(5) {
grid-row-end: 5;
background-color: lightblue;
}
.date:nth-child(4) {
grid-row-end: 4;
background-color: lightgreen;
}
.date:nth-child(3) {
grid-row-end: 3;
background-color: lightyellow;
}
.date:nth-child(2) {
grid-row-end: 2;
background-color: coral;
}
<!-- language: lang-html -->
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='location'>location</div>
<div class='notes'>notes</div>
<div class='links'>links</div>
<div class='date'>date</div>
</div>
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='location'>location</div>
<div class='links'>links</div>
<div class='date'>date</div>
</div>
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='location'>location</div>
<div class='date'>date</div>
</div>
<div class='container'>
<div class='title'>Title that is super long and we are going to have a problem fitting it</div>
<div class='date'>date</div>
</div>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论