I am trying to figure out what HTML / CSS I need to get a certain breaking behavior depending on how much space is available. Basically, I want to have a long line of text automatically break at certain places, and all at one), when there is no longer space for it.
Example 1:
There is enough horizontal space for the entire line of text:
Example 2: This is the same as the previous example, yet it shows that I'd like to be able to use this in a nested fashion too:
There is enough horizontal space for the entire line of text:
There is just not enough horizontal space for the entire line, so each cell goes onto its own line
There is not enough horizontal space for the second cell
I'm looking for a solution that uses only HTML and CSS (or only uses JavaScript very lightly) since my intended use-case is to use this pretty heavily in automatically generated HTML documentation pages.
I've come across examples like these, but I'm not sure I see how to make this work for subitems (and how to make this work without assigning a fixed size to each cell - I want to the cell-size to be determined by its content).
Just bringing extra emphasis to a crucial part of the question which has been overlooked. I never want to find myself in a state where there are two items on one line and the third item on the next line. The three items should go straight from being all on one line to all on separate lines.
As I have already stated in the comments, what the OP is looking for cannot be achieved without the use of Javascript, so here is what I came up with.
Initially we have a normal table structure, we calculate each table's width and apply a data-width
attribute on each table - parent or nested.
On resize, as soon as each table is longer than the window width, it changes its contents display from table-cell
to block
, thus stacking all children.
As soon as the window width is expanded it is compared to each table's data-width
attribute and when it fits, it expands.
edit: I thought for some reason it wouldn't work under some cases, but it actually works, so I remove the bloat!
HTML:
<div class="table">
<div class="cell">
Content 1
</div>
<div class="cell">
<div class="table">
<div class="cell">
Content 1
</div>
<div class="cell">
Cont
</div>
<div class="cell">
Content 3
</div>
</div>
</div>
<div class="cell">
Content 3Content 3Content 3
</div>
</div>
CSS:
.table {
display: table;
border: 1px solid #e4e5e7;
white-space: nowrap;
}
.cell {
display: table-cell;
vertical-align: middle;
padding: 10px;
border: 1px solid #e4e5e7;
}
.cell .table {
margin-bottom: -2px;
}
.table.wrap {
white-space: normal;
}
.table.wrap > .cell {
display: block;
}
Javascript:
$('.table').each(function(i) {
tw = 0;
$(this).children('.cell').each(function() {
tw += $(this).outerWidth(true);
});
$(this).attr('data-width', tw);
});
$(window).on('load resize', function() {
w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
$('.table').each(function(i) {
if ($(this).width() >= w) {
$(this).addClass('wrap');
} else if (w >= $(this).attr('data-width')) {
$(this).removeClass('wrap');
}
});
}).resize();
And here is a working fiddle
Another variant with pure JS
window.addEventListener('load', collapse);
window.addEventListener('resize', collapse);
function collapse() {
var parents = document.querySelectorAll('.parent');
var children;
var width;
for (var i = 0; i < parents.length; i++) {
parents[i].classList.remove('collapsed');
width = 0;
children = parents[i].children;
for (var j = 0; j < children.length; j++) {
width += children[j].offsetWidth;
}
if (width > parents[i].offsetWidth) {
parents[i].classList.add('collapsed');
}
}
}
.parent,
.child {
border: 1px solid gray;
margin: 5px;
padding: 5px;
}
.parent {
display: flex;
flex-wrap: wrap;
}
.collapsed {
display: block;
}
.child {
flex-grow: 1;
}
<div class="parent">
<div class="child">Some thing one</div>
<div class="parent child">
<div class="child">Some inner</div>
<div class="child">long thing</div>
<div class="child">two</div>
</div>
<div class="child">Thing three</div>
</div>
One approach is to use @media
queries to alternate between
display: block
and
display: inline-block
Working Example:
.long-row {
display: inline-block;
width: 200px;
padding-left: 4px;
font-size: 16px;
line-height: 32px;
border: 1px solid rgb(0, 0, 0);
}
@media only screen and (min-width: 801px) {
.long-row {
display: inline-block;
float: left;
}
}
@media only screen and (max-width: 800px) {
.long-row {
display: block;
border-top: none;
}
div:first-of-type.long-row {
border-top: 1px solid rgb(0, 0, 0);
}
}
@media only screen and (max-width: 600px) {
.long-row {
}
.short-row {
display: block;
margin-left: -4px;
padding-left: 4px;
border-bottom: 1px solid rgb(0, 0, 0);
}
span:last-of-type.short-row {
border-bottom: none;
}
}
<div class="long-row">Some thing one</div>
<div class="long-row">
<span class="short-row">Some inner</span>
<span class="short-row">long thing</span>
<span class="short-row">two</span>
</div>
<div class="long-row">Thing three</div>
Before getting to the script, there are couples of notes that should be considered :
At my actual state of my knowledge, this cannot be done using only CSS. So the question is how to do this using pure JavaScript with the best performance possible? And I've come up with some guidelines :
for
loops as they are the fastest using cache of the number of iterationsAvoiding :
recursive functions
[].foreach constructs
DOM access whenever possible
In addition to that it can be nested to any level
The idea:
The script takes all elements, force them into a single line, and see if there is not enough space, it make them displayed on top of each other, otherwise they stayed on the same line.
It does this every time the document's markup is loaded, and when the window is resized.
https://jsfiddle.net/nasdao/qzdtr9ym/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With