Reputation:
I have two side-by-side grid columns. I want the grid to be the width of its container. Within that space, I want to ensure that it will always be the case that as much of each column's content is as visible as possible.
I made this demo where the grid is 300px wide, and the two columns are content editable so you can type to change the content length (and thus the values of min-content
and max-content
will also change as you type).
I am using grid-template-columns: minmax(min-content, 1fr) minmax(min-content, 1fr);
which is my best attempt yet to achieve what I want, but still isn't sufficient.
html, body {
width: 300px;
}
html * {
border: 1px solid black;
}
span {
cursor: pointer;
}
body {
display: grid;
grid-template-columns: minmax(min-content, 1fr) minmax(min-content, 1fr);
}
<span contenteditable></span>
<span contenteditable></span>
When there is nothing in the columns, the grid is balanced 50/50. Each column is 150px wide. This is good.
When there is more than 150px of content in one column, it becomes wider and begins to expand into the space the other column had available to itself. This is also good.
But: when the sum of the two columns' contents is greater than 300px, the grid starts overflowing horizontally:
Instead I would like the grid's width to be constrained to 300px, and the widest cell should shrink itself until as much as possible of the smaller cell's content is visible:
And the cell's overflow-x: ellipsis
is applied.
This should happen in the other direction too. Current and undesirable behaviour:
Desired behaviour:
When each cell on its own is already wider than half the grid, 150px in this case (current undesired behaviour):
They should both be shrunk until proportionally balanced (desired behaviour):
With a final (optional) goal of having constrained widths which are representative of their actual content widths:
I am surprised I haven't yet been able to find a standard solution for balancing side-by-side grid columns, it seems like it would be a common use case. It's very similar to this: Is it possible to set proportional grid columns based on content?, except I don't know the proportions ahead of time and they're co-dependent. I would really appreciate being pointed towards anything I may have missed.
To try to achieve this, I have tried a variety of grid-template-columns
values already, but there seems to be a CSS-expressivity problem that the widths of columns cannot depend on the contents of the other columns. Unfortunately calc(min-content/100%)
seems to invalidate the property value. I may just be missing a way to accomplish it with pure CSS, I would be over the moon if there were a CSS-only answer.
I have been trying with JavaScript since but because min-content and max-content cannot be discovered easily using JavaScript I thought I would ask here before committing several more hours to pursuing the solution I have thought of for now: allowing the grid width to be totally unlimited, measuring the outerWidth of each cell, then setting grid-template-columns
as the outerWidth / sumOfBothOuterWidth
percentage for each cell. All of this would have to be put into some MutationObserver callback too, so that it can re-calculate widths when content changes. Luckily because of using percentage values instead of hardcoded ones, resizing the window/container should redraw automatically.
Could anyone recommend an alternative solution?
The best answer would be CSS-only, and I will generally prefer solutions that require less JavaScript DOM manipulation and JavaScript calculation.
Upvotes: 2
Views: 3122
Reputation: 15120
It sounds like you are looking for a two column, fixed width grid where column widths are proportional to the text content they contain (and overflowing text content is truncated). I think the below gets you closer, but you will run into some issues once the combined text content exceeds the width of the grid. The grid columns will not continue to resize proportionally once the combined text content exceeds the width of the grid. Also, if you actually plan to use contenteditable
elements in your grid, the combination of overflow: hidden
and text-overflow: ellipsis
will cause some browsers to stop displaying your input once the text begins to overflow (which means that if you enter enough characters, the column will eventually appear empty).
.grid {
display: grid;
grid-template-columns: repeat(2, auto);
width: 300px;
}
.column {
border: 1px solid gray;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
<div class="grid">
<div class="column" contenteditable="true">
abcdefghijklmnop
</div>
<div class="column" contenteditable="true">
abcdefghijklmnopqrstuvwxyz
</div>
</div>
Upvotes: 1
Reputation: 94
This is a case where I think flexbox is the perfect solution to use in conjunction with grid. You can place a flex element within a grid row and it will easily handle the grow and shrink features you are looking for with minimal css and no JS.
In my example below the column with the most content will grow larger without overflowing into the next column while the other column shrinks proportionally. The child elements of the flex container are set to grow and shrink equally (flex: 1 1) or occupy 150px (flex: 1 1 150) of available space if the content is less than the available width. The flex-basis is set to 150 which is half of the width you specified. For larger widths adjust the flex-basis accordingly.
*, ::before, ::after {
box-sizing: border-box;
}
body {
width: 300px;
}
html * {
border: 1px solid black;
}
.flex-col {
display: flex;
flex-wrap: nowrap;
gap: 1rem;
}
.flex-col > * {
flex: 1 1 150;
}
.col {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
<div class="flex-col">
<div class="col" contenteditable>Lorem ipsum dolor sit amet consectetur adipisicing elit. </div>
<div class="col" contenteditable>
Alias, soluta Lorem ipsum dolor sit, amet consectetur adipisicing elit. Veritatis natus odio dolorum. Dolorem minus, labore distinctio eum praesentium eos ullam ducimus repellat, rerum vel eveniet soluta laborum odit odio sequi. </div>
</div>
Upvotes: 2