I'm having a problem getting long lines of text to correctly break and wrap in a chat feature that I'm working on. The HTML below is the relevant set of nested elements, but the crux of the biscuit is with .chat__messages__item and .chat__messages__body (the whole block below is inside of an element that is set to 24vw, so it is all intended to be window-width-responsive).
First off, here's the HTML/CSS...
<style>
.chat__messages__inner{
display: table;
height: 100%;
width: 100%;
}
.chat__messages__list {
display: table-cell;
vertical-align: bottom;
margin: 0;
padding: 10px 20px 0;
list-style-type: none;
}
.chat__messages__item {
position: relative;
margin-bottom: 10px;
padding: 8px 10px;
background-color: #D8F2FD;
clear: both;
}
<!-- THIS STYLE HAS NO AFFECT UNLESS I SET A MAX-WIDTH ON .chat__messages__item -->
.chat__messages__body {
word-wrap: break-word;
}
</style>
<div class='chat__messages__inner'>
<ul class='chat__messages__list'>
<li class='chat__messages__item'>
<div class='chat__messages__body'>
hereisaverylonglineoftextthatiwouldliketobreakandwrap
</div>
</li>
<li class='chat__messages__item'>
<div class='chat__messages__body'>
here is a long sentence that will wrap and behave correctly
</div>
</li>
</ul>
</div>
The desired behavior is that the <div> and <li> containing the text should be no wider/taller than the text itself, but those elements also should never be wider than their parents - so for a few words, they might be 150px wide, but if the container shrinks to be less than 150px, these elements will also start to shrink and the text inside will start to wrap.
Playing with this code, I was able to get close to the desired result by setting the style for .chat__messages__body to include word-wrap: break-word and then setting the parent .chat__messages__item to include max-width: 300px (omitted above). Although the long word would break and wrap, it only produced the correct result on my full-screen window - if the window is resized or starts off at less-than-full, the word still wraps but the div is 300px wide (I tried setting this as a percentage, but that does not work, the word actually unwraps).
The long sentence that I included above does exactly what I would like - its parent <div> and <li> are both the size of the text, and if the window shrinks so that the width of these elements would be greater than their parents (which all scale to the 24vw ancestor), they begin to shrink as well and the text wraps on spaces.
In plain English, I would like the long word's container to never be wider than the 100% width ancestors, and it needs to resize dynamically with the window, breaking and wrapping along the way.
I'm not really a CSS/design expert, this is code I inherited from someone else, and I've been fighting with this for way too long... any guidance would be much appreciated.
Here is a question you could check out. One of the answers suggest what I would try, which is to use the <wbr> tag which creates a word break opportunity. You can read about it here.
Ok, turns out the thing to do was to set .chat__messages__inner and .chat__messages__list to display: inline-block with width: 100%, and .chat__messages__item needed to have max-width: 100%.
Related
I ran into an interesting CSS problem today, and I have been wracking my brain trying to solve it.
This is similar to the trivial problem of "a row of three elements, with a left, a right, and center," which can be solved easily with flexbox — but it has a couple of caveats that make it (I think) an impossible layout without JavaScript.
The desired goal
Consider a row-like container element and three children, "left", "right", and "center". The children may be of varying widths, but they are all the same height.
"Center" should try to stay centered relative to its container — but the three sibling elements must not overlap, and may push outside the container if necessary.
The markup, then, might look something like this:
<div class="container">
<div class="left">I'm the left content.</div>
<div class="center">I'm the center content. I'm longer than the others.</div>
<div class="right">Right.</div>
</div>
The CSS is where the challenge is.
Examples of what should happen
For wide containers, "center" is centered relative to the container (i.e., its siblings' widths do not matter), as in the image below; notice that midpoint of the "center" element matches the midpoint of the container, and that the left and right "leftover" spaces are not equal:
For narrower containers, "center" abuts the widest sibling, but it does not overlap. The remaining space is distributed only between the narrow sibling and the "center" sibling. Notice also that the container's midpoint, indicated by the caret, is no longer the same as "center's" midpoint:
Finally, as the container continues to shrink, there's no other option but to have all three elements lined up in a row, overflowing the parent:
My attempts to solve this
Surprisingly, I haven't found a good way to implement this in pure CSS.
You'd think flexbox would be the winner, but you can't really get flexbox to do it right: The space-between property distributes the space uniformly between the elements, so the center element doesn't actually end up centered. The flex-grow/shrink/basis properties aren't especially useful for this either, since they're responsible for controlling the size of the child elements, not for controlling the size of the space between them.
Using position:absolute can solve it as long as the container is wide enough, but when the container shrinks, you end up with overlap.
(And float layouts can't get within a mile of getting this right.)
I could combine the best two solutions above, and switch between them with a #media query — if all of the widths were known in advance. But they aren't, and the sizes may vary widely.
In short, there's no pure-HTML-and-CSS solution to this problem that I know of.
Conclusion, and a JSFiddle to experiment with
I created a JSFiddle that shows both the desired goal and a few non-solutions. Feel free to fork it and experiment. You can simulate the container resizing by grabbing the bar to the left of the content and dragging it. You are allowed to rearrange/restructure the HTML and CSS, if rewriting it gets you closer to a working answer.
https://jsfiddle.net/seanofw/35qmdnd6
So does anyone have a solution to this that doesn't involve using JavaScript to intelligently distribute the space between the elements?
With flexbox you should be able to solve that, by giving the left/right elements flex: 1 and the right text-align: right.
The main trick is flex: 1, which will make them share available space equally.
For more versions, see this brilliant question/answer, flexbox-justify-items-and-justify-self-properties
Fiddle snippet
Stack snippet
body {
font: 14px Arial;
}
.container {
display: flex;
border: 1px solid #00F;
}
.container > div > span {
display: inline-block;
background: #36F;
white-space: nowrap;
padding: 2px 4px;
color: #FFF;
}
.container > .center > span {
background: #696;
}
.container .left,
.container .right {
flex: 1;
}
.container .right {
text-align: right;
}
.center-mark {
text-align: center;
font-size: 80%;
}
.note {
text-align: center;
color: #666;
font-style: italic;
font-size: 90%;
}
<div class="container">
<div class="left">
<span>
I'm the left content.
</span>
</div>
<div class="center">
<span>I'm the center content. I'm longer than the others.</span>
</div>
<div class="right">
<span>
Right.
</span>
</div>
</div>
<div class="center-mark">^</div>
<div class="note">(centered marker/text)</div>
I have a single-line fixed-width container div with two variable-width span inside. Any overflow of the first span should be hidden with an ellipsis. The second span floats on the right and should be shown in full. Please see this Fiddle:
<div class='container'>
<span class='left'>Long long variable stuff</span>
<span class='right'>Changing stuff</span>
</div>
I want the first span's width to dynamically adjust according to the width of the second span so that both span stay on the same line. How can I do that?
You can use Flexbox, so with flex: 1 on .right, .left will adjust its size and overflow will be hidden.
.container {
width: 200px;
display: flex;
border: 1px solid black;
}
.left {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.right {
flex: 1;
white-space: nowrap;
}
<div class='container'>
<span class='left'>Long long variable stuff</span>
<span class='right'>Changing stuff</span>
</div>
I don't think there's any way for CSS to dynamically know the length of an element without javascript. If you're looking for a purely CSS solution you're going to need to give it some direction in order for it to know the widths you want. Visually, that might be a bit of a compromise for you, but it will allow you to ensure that everything is always on one line.
For the solution I'm about to propose to work you need to know one width of the two. In this case I'm going to say that you can make a best guess of the "changing stuff" on the right.
Your first problem is that spans are inline elements by default - not inline-block. In order to get the overflow text property to work, you need to use it with an inline-block or block element.
The next piece is to use calc. Calc excepts mixed measurements so you can subtract an exact pixel value off of a percent. This works really well for responsive layouts.
I've created an updated version of your plunker to illustrate:
https://jsfiddle.net/n19ddahb/5/
For a webpage grid-layout I decided to use Flexbox. Now I wanted to implement some "auto-functionality", so that grid-boxes can later be inserted without the need to add classes or styles in the HTML. One of this features is to make a box allways be 75% as tall as it is wide - even if the box is resized by, for example, browserwindow resize. Off course, if the boxes content extends the 75%-height, it should (and only then should) increase its height to fit the content. I searched for hours to find a suitable solution, but I finally got it working. So I thought at least, until I added content to the box.
The auto aspect-ratio works fine, as long as the box is empty. If I add content, the 75% of the width is allways added to the height it has through extension by its content. I made a jsfiddle to clearly visualize the problem:
JSFiddle wd5s9vq0, visualizing the following Code:
HTML-Code:
<div class="container">
<div class="content-cell"></div>
<div class="content-cell"></div>
</div>
<div class="container">
<div class="content-cell">
This cell has an inreased height because of
it's content. The empty space below the
content is the 75% of the cells width.
</div>
<div class="content-cell"></div>
</div>
CSS:
.container {
display: flex;
width: 400px;
}
.content-cell {
flex: 1 1 0;
margin: 10px;
background-color: #ccc;
}
.content-cell::after {
content: "";
display: block;
padding-top: 75%;
}
If I didn't knew it better, it looks like a floating-problem - but I think the ::before / ::after selector should add the block-element before the element it is used on and not inside it.
Does anyone has an idea on how to fix this problem?
This seems to be a very widespread problem on the internet, and most solutions you find are either about wrapping the content, absolute-positioning the content or a mixture of both. This has numerous and case-dependent downsides. After hours of playing around with the code, I finally found a combination of CSS proporties that work without the need to add any DOM or make the content absolute-positioned. This looks quit basic, and I am wondering why it took me so long and why you can't find it out there on the web.
The HTML:
<div class="mybox aspect-full">
This is text, that would normally extend the box downwards.
It is long, but not so long that it extends the intended aspect-ratio.
</div>
The CSS:
.mybox {
width: 200px;
}
.aspect-full::before {
content: '';
display: block;
padding-top: 100%;
float: left;
}
The only downside I could find is that the content of your cell must float. If you use clear on one of your child objects, it is positioned below the expander-block and you are back to the original problem. If you need to clear the floating of divs inside of these aspect-ratio-cells, you might consider to wrap them and keep the wrapper floatable.
The problem we're trying to solve is that text is overflowing past the end of a <p>. It seems to be the result of its contents, which include a relatively-positions <a> element, with an absolute-positioned <span> element within it, which has padding. Firefox wraps the text as I would normally expect.
Here's an abstraction of my HTML:
<p>
In this second example,
<a href="#">
<span class="icon"><img src="play.gif"></span>
mo
</a>
muh...
</p>
And an abstraction of the CSS, as simplified as I think still makes sense:
a {
padding: 5px;
}
a span.icon {
position: absolute;
display: block;
width: 15px;
height: 15px;
}
Here's a screenshot of the problem (the highlighting is Chrome's element inspector with the <p> element highlighted). You can see the word immediately overflowing at the end of the <p>:
Any pointers in the right direction appreciated.
Instead of using absolute positioning, try using display:inline or display:inline-block (if you need to set height/width - Note: not supported in IE 7 and lower).
You could drop the display all together, because images and spans are inline to start with. I have an example here removing the span and just styling the image and surrounding link.
You could also use the image as a background for the button, and set the padding to account for the space. Example:
.button{
background: url(img/buttonIcon.png) no-repeat; /* 15x15 icon */
padding: 0 0 0 15px;
}
example
<a class="profile_link" href="">
<div class="thumb_container">
<img class="thumb_image" src="" alt="thumb"/>
<span class="model_names">name</span>
</div>
</a>
a.profile_link{
color: black;
outline: none;
text-decoration: none;
}
.thumb_container{
float:left;
padding-left: 9px;
padding-right: 9px;
padding-bottom: 10px;
}
img.thumb_image{
display: block;
}
.model_names{
font-size: 12px;
text-align: center;
}
This code kinda of gives me what I want but not quite. So I have these links looped, which contain a thumb and a model name centered right below it. I want these thumbs and names to be dynamically placed in rows and when there is not enough room it will create another row. It is doing it right now but sometimes gets buggy and skips a row... it's just a mess. Keep in mind, the thumbs can be different sizes; I don't mind gaps at the end of rows if there isn't enough room.
There is also a main container div I didnt paste which just has the dimensions of 800px width.
Maybe someone has a better and cleaner way of approaching this layout.
I think the problem is caused by your floated div being placed inside a non-floated a. Whilst this will float the div, the effect is negated because the a is an inline element.
Try adding the .thumb_container style declarations to the a element.
BTW, this is always going to have potential to look ugly if you're using thumbnails of different sizes. You could also try setting a specific width and height to the containing div and setting overflow to hidden. You will need to move your span outside of div.thumb_container, but that shouldn't be a problem. You could then use some CSS and/or JS effect to show the full thumbnail on hover.