Reputation: 123
CSS display: inline
elements with text in them "flow" over the end of a line and then carry on the next line when the container isn't large enough for them.
I want to achieve this same horizontal wrapping effect but with block elements that I can control the width of.
If I use display: inline-block
this allows me to control the block width however it then prevents line breaking from working.
I can't seem to find any set of CSS settings that will allow me to both wrap and control the width of the element. However it seems that this should be possible. Presumably internally the browser's own layout engine is doing this once it calculates the width of various text strings.
There are two work-arounds I've considered which give me the desired visual output:
Neither of these are very nice solutions and if this is possible by pure CSS i'm keen to learn how.
Here's a link to the examples in the screenshots interactively rendered:
https://jsfiddle.net/n3wzd29v/
let tags = document.getElementsByClassName("js");
let currentLeft = 0;
for (let i=0; i<tags.length; i++) {
let tag = tags[i];
// dont know how to get the "width this would be if it was not inline cleanly".
tag.style.display="inline-block";
let requestedWidth = tag.offsetWidth;
tag.style.display="inline";
let parentWidth = tag.parentNode.offsetWidth;
console.log("requestedWidth:" + requestedWidth);
console.log("parentWidth:" + parentWidth);
console.log("currentLeft:" + currentLeft);
//FIXME: handle spanning over arbitary number of lines.
if ((currentLeft + requestedWidth) < parentWidth) {
// Got plenty of space for this element on this line so just make it a inline-block
tag.style.display = "inline-block";
currentLeft += requestedWidth;
console.log("Just setting inline-block, its wide enough");
} else {
// This element needs breaking up into two elements, one for the part that fits on this
// line, and another for the remainder on the next line.
let part1Width = parentWidth - currentLeft;
let part1 = document.createElement("span");
part1.style.display = "inline-block"
part1.style.width = part1Width + "px";
part1.style.verticalAlign = "top";
part1.style.height = "16px";
let part2Width = requestedWidth - part1Width;
let part2 = document.createElement("span");
part2.style.display = "inline-block"
part2.style.width = part2Width + "px";
part2.style.verticalAlign = "top";
part2.style.height = "16px";
currentLeft = 0 + part2Width;
tag.appendChild(part1);
tag.appendChild(part2);
console.log("Adding two fixed width children: " + part1Width + " " + part2Width);
}
}
.container {
width: 200px;
background-color: lightblue;
}
span {
word-break: break-all;
font-family: monospace;
}
.bad {
display: inline-block;
height: 16px;
}
.stuffed {
line-height:22px;
}
.js {
height: 15px;
vertical-align: top;
}
.color1 {
background-color: hsl(0, 80%, 50%);
}
.color2 {
background-color: hsl(20, 80%, 50%);
}
.color3 {
background-color: hsl(40, 80%, 50%);
}
.color4 {
background-color: hsl(60, 80%, 50%);
}
.color1.stuffed {
color: hsl(0, 80%, 50%);
}
.color2.stuffed {
color: hsl(20, 80%, 50%);
}
.color3.stuffed {
color: hsl(40, 80%, 50%);
}
.color4.stuffed {
color: hsl(60, 80%, 50%);
}
<html>
<body>
<p>Inline elements with text in them "flow" over the end of a line
and then carry on the next line when the container isn't large enough for them.
</p>
<div class="container">
<span class="good color1">These inline</span><span class="good color2">spans will wrap over</span><span class="good color3">the end of the container</span><span class="good color4">exactly how i would like them to</span>
</div>
<br/>
<p>
However i want to do the same layout but with block elements that i can set
the width of. Using inline-block allows me to control the block width however
it seems to prevent line-breaking from working.
</p>
<div class="container">
<span class="bad color1" style="width:96px"></span><span class="bad color2" style="width:160px"></span><span class="bad color3" style="width:192px"></span><span class="bad color4" style="width:256px"></span>
</div>
<p>
Obviously i can acheieve something of the same behaviour by stuffing my inline
elements with hidden text, but that seems sub-optimal
</p>
<div class="container">
<span class="stuffed color1">████████████</span><span class="stuffed color2">████████████████████</span><span class="stuffed color3">████████████████████████</span><span class="stuffed color4">████████████████████████████████</span>
</div>
<p>
Finally, it is ofc also possible to fix this problem in javascript, but i'm tenative
to do layout in javascript when the browser already clearly have a well optimised
layout engine for doing this on text already.
</p>
<div class="container">
<span class="js color1" style="width:96px"></span><span class="js color2" style="width:160px"></span><span class="js color3" style="width:192px"></span><span class="js color4" style="width:256px"></span>
</div>
<br/>
<br/>
<br/>
</body>
</html>
Upvotes: 5
Views: 1530
Reputation: 273626
If this is only for the visual purpose then you might have a way using a span element where you apply multiple gradient.
Here is a self-explanatory example:
.container {
width: 200px;
padding:5px;
background-color: lightblue;
text-align:justify;
}
span {
padding:0;
background-image:
linear-gradient(hsl(0, 80%, 50%),hsl(0, 80%, 50%)),
linear-gradient(hsl(20, 80%, 50%),hsl(20, 80%, 50%)),
linear-gradient(hsl(40, 80%, 50%),hsl(40, 80%, 50%)),
linear-gradient(hsl(60, 80%, 50%),hsl(60, 80%, 50%));
background-size:
100px 100%, /* 1st color width = 100px */
280px 100%, /* 2nd color width = 280px - 100px = 180px */
450px 100%, /* 3rd color width = 450px - 280px = 170px */
520px 100%; /* 4th color width = 520px - 450px = 70px */
background-repeat:no-repeat;
}
/* we fill the span with content */
span::before,
span::after{
content:"█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █";
color:transparent;
}
<div class="container">
<span ></span>
</div>
Here is another very crazy idea where I will rely on the use of columns:
.container {
height: 120px; /* this will define the visual width of container */
column-width: 20px; /* this will define the visual height of rows*/
display: inline-block;
column-gap: 4px; /* gap between rows */
/* this transformation will invert everything like we want */
transform-origin: top left;
transform: rotate(90deg) scaleY(-1);
/**/
}
span {
display: block;
}
span:hover {
filter: invert(1);
}
/* all the height below will define the visual width */
span:nth-child(1) {
background: hsl(0, 80%, 50%);
height: 80px;
}
span:nth-child(2) {
background: hsl(20, 80%, 50%);
height: 100px;
}
span:nth-child(3) {
background: hsl(40, 80%, 50%);
height: 80px;
}
span:nth-child(4) {
background: hsl(60, 80%, 50%);
height: 200px;
}
<div class="container">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
Upvotes: 1
Reputation: 9
NO.
I do not think that the solution you're trying to reach is logical. If I understood your question correctly, you are trying to set an element's WIDTH to span across multiple lines.
Take a look at the first "BAD" option. You want to set a width for the element with the yellow background for instance, and then, have it continue that width on multiple lines?
Perhaps, you might want to use your JS solution, if it is absolutely necessary to layout your content in this manner.
Upvotes: 0