This question already has answers here:
Prevent a child element from overflowing its parent in flexbox [duplicate]
(1 answer)
Why don't flex items shrink past content size?
(5 answers)
Ellipsis not working in flex container
(1 answer)
How to make a sticky footer using flexbox in IE11?
(5 answers)
Closed 5 years ago.
I have container with flex. I want the middle child will take the entire space so I set it flex: 1. So far so good.
The next level is that the middle child has 2 child so I want to set it flex too (If you lost me, just skip to the snippet) and the first child I set ellipsis styles. Now, the ellipsis stops working.
If you will click on the button, you will see that everything good with short text;
Any ideas?
function toggle() {
var el = document.querySelector('.el');
el.textContent = el.textContent === 'short' ? 'long long long text' : 'short';
}
.wrapper {
display: flex;
width: 200px;
align-content: stretch;
padding: 5px;
min-width: 0;
border: 1px solid
}
.wrapper .child2 {
flex-grow: 1;
}
.flex {
display: flex;
min-width: 0;
}
.el {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.child1 {
background: red;
}
.child2 {
background: blue;
}
.child3 {
background: green;
}
.wrapper>* {
padding: 5px;
}
<div class="wrapper">
<div class="child1">child1</div>
<div class="child2">
<div class="flex">
<div class="el">long long long text</div>
<div>a</div>
</div>
</div>
<div class="child3">child3</div>
</div>
<button onclick="toggle()">Toggle ellipsis text</button>
Add overflow: hidden; to .wrapper .child2.
Actually, as Mosh Feu suggests in his answer, min-width: 0 should also work, and does, cross browser, though IE is buggy and need overflow
The reason also the child2 need it, is because it is also a flex item, and flex item's, in this case min-width, defaults to auto, and won't allow it to be smaller than its content, so by adding overflow: hidden (or any value but visible), or min-width: 0, will let it.
function toggle() {
var el = document.querySelector('.el');
el.textContent = el.textContent === 'short' ? 'long long long text' : 'short';
}
.wrapper {
display: flex;
width: 200px;
align-content: stretch;
padding: 5px;
min-width: 0;
border: 1px solid
}
.wrapper .child2 {
flex-grow: 1;
overflow: hidden;
}
.flex {
display: flex;
min-width: 0;
}
.el {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.child1 {
background: red;
}
.child2 {
background: lightblue;
}
.child3 {
background: green;
}
.wrapper>* {
padding: 5px;
}
<div class="wrapper">
<div class="child1">child1</div>
<div class="child2">
<div class="flex">
<div class="el">long long long text</div>
<div>a</div>
</div>
</div>
<div class="child3">child3</div>
</div>
<button onclick="toggle()">Toggle ellipsis text</button>
Is this what you want.?
I just put overflow:hidden;text-overflow: ellipsis; to your .child2
function toggle() {
var el = document.querySelector('.el');
el.textContent = el.textContent === 'short' ? 'long long long text' : 'short';
}
.wrapper {
display: flex;
width: 200px;
align-content: stretch;
padding: 5px;
min-width: 0;
border: 1px solid
}
.wrapper .child2 {
flex-grow: 1;
}
.flex {
display: flex;
min-width: 0;
}
.el {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.child1 {
background: red;
}
.child2 {
background: blue;
overflow:hidden;
text-overflow: ellipsis;
}
.child3 {
background: green;
}
.wrapper>* {
padding: 5px;
}
<div class="wrapper">
<div class="child1">child1</div>
<div class="child2">
<div class="flex">
<div class="el">long long long text</div>
<div>a</div>
</div>
</div>
<div class="child3">child3</div>
</div>
<button onclick="toggle()">Toggle ellipsis text</button>
Apparently if I set the min-width: 0 of the middle child (.child2) it magically works.
Update According #LGSon it doesn't works on IE.
function toggle() {
var el = document.querySelector('.el');
el.textContent = el.textContent === 'short' ? 'long long long text' : 'short';
}
.wrapper {
display: flex;
width: 200px;
align-content: stretch;
padding: 5px;
min-width: 0;
border: 1px solid
}
.wrapper .child2 {
flex-grow: 1;
min-width: 0;
}
.flex {
display: flex;
}
.el {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.child1 {
background: red;
}
.child2 {
background: blue;
}
.child3 {
background: green;
}
.wrapper>* {
padding: 5px;
}
<div class="wrapper">
<div class="child1">child1</div>
<div class="child2">
<div class="flex">
<div class="el">long long long text</div>
<div>a</div>
</div>
</div>
<div class="child3">child3</div>
</div>
<button onclick="toggle()">Toggle ellipsis text</button>
That's because class .el doesn't have a width set. Use the CSS below and it works well:
.el {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 3em;
}
Related
I'm developing an app with the interface that is supposed to fit the page (only some internal elements may have scrolling). The basic layout consists of a header and the main section:
<div class="page">
<Navigation/> <!-- a Vue component -->
<main class="page__main">
...
</main>
</div>
currently, CSS has hardcoded height of the header (Navigation):
.page {
height: 100vh;
}
.page__main {
height: calc(100vh - 80px); /* 80px is the height of the header */
}
I'd like to get rid of this hardcoded bit but make sure .page__main's height gets no larger than 100vh - height of Navigation. Is there a way to do this without JS? I suspect that there are some options that can be used with
.page {
height: 100vh;
display: flex;
flex-direction: column;
}
but just using that with
.page__main {
flex-shrink: 1;
}
doesn't work: .page__main has children which use height in percents and once I set flex-shrink: 1; instead of height: calc(100vh - 80px); those grow and the interface is broken.
To illustrate the problem better, here's the current state:
body { padding: 0; margin: 0; }
.page {
height: 100vh;
background: blue;
}
.page__navigation {
height: 80px;
background: gray;
}
.page__main {
height: calc(100vh - 80px);
}
.part1 {
height: 50%;
background: #eeeeee;
overflow-y: scroll;
}
.part2 {
height: 50%;
background: #cccccc;
}
<div class="page">
<div class="page__navigation">nav stuff</div>
<main class="page__main">
<div class="part1">
this one usually has more elements than it could contain and those are shown with scrolling
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line
</div>
<div class="part2">
some
</div>
</main>
</div>
and here's what happen when I try to "set height" via flex:
body { padding: 0; margin: 0; }
.page {
height: 100vh;
display: flex;
flex-direction: column;
background: blue;
}
.page__navigation {
height: 80px;
background: gray;
}
.page__main {
flex-shrink: 1;
}
.part1 {
height: 50%;
background: #eeeeee;
overflow-y: scroll;
}
.part2 {
height: 50%;
background: #cccccc;
}
<div class="page">
<div class="page__navigation">nav stuff</div>
<main class="page__main">
<div class="part1">
this one usually has more elements than it could contain and those are shown with scrolling
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line
</div>
<div class="part2">
some
</div>
</main>
</div>
You can consider a nested flexbox container and don't forget the use of min-height:0; to allow the elements to shrink.
body { padding: 0; margin: 0; }
.page {
height: 100vh;
display: flex;
flex-direction: column;
background: blue;
}
.page__navigation {
height: 80px;
background: gray;
}
.page__main {
flex-grow: 1; /* Fill the remaining space*/
display:flex; /* Nested Container*/
flex-direction:column;
min-height:0; /* Allow the element to shrink */
}
.part1 {
flex-basis: 50%;
background: #eeeeee;
overflow-y: scroll; /* Allow the element to shrink */
}
.part2 {
flex-basis: 50%;
min-height:0; /* Allow the element to shrink */
background: #cccccc;
}
<div class="page">
<div class="page__navigation">nav stuff</div>
<main class="page__main">
<div class="part1">
this one usually has more elements than it could contain and those are shown with scrolling
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line
</div>
<div class="part2">
some
</div>
</main>
</div>
Use flex-grow. Keep everything as the second one (flex one) and change:
Edit
.page {
height: 100vh;
display: flex;
flex-direction: column;
background: blue;
}
.page__main {
height: 100%;
min-height: 0;
flex: 1 1 auto;
}
Three value flex means flex: flex-grow | flex-shrink | flex-basis.
Flex-grow tells our element whether or not it can take up additional space.
Flex-shrink works very similarly to flex-grow, only instead of dealing with extra space, it deals with space not needed by an elements content.
Flex basis is best used when in conjunction with either flex-shrink or flex-grow.
You can check this article to understand better.
I would suggest css-grid approach : -
.page {
background: gray;
display: grid;
grid-template-rows: 100px auto;
height: 100vh;
color: white;
}
.nav {
grid-row: 1/2;
background: brown;
}
.main {
grid-row: 2/3;
background: green;
display: grid;
grid-template-rows: 30% 70%;
}
.part1 {
overflow: auto
}
.part2 {
background: blue
}
<div class="page">
<div class="nav">Nav</div>
<div class="main">
<div class="part1">
this one usually has more elements than it could contain and those are shown with scrolling
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line
</div>
<div class="part2">
some
</div>
</div>
</div>
</div>
I have this right now:
#outer-div {
border: 1px dashed red;
line-height: 50px;
width: 350px;
}
.inner-div {
border: 1px dashed red;
float:left;
height: 50px;
width:80px;
}
#title {
/*white-space: nowrap;*/
}
<div id="outer-div">
<div class="inner-div"></div>
<div class="inner-div"></div>
<span id="title">Random long text not to break<span>
</div>
and it looks like:
and I'd need this:
Tried solutions from other threads but they not work as this. white-space: nowrap moves the entire span to the next line.
Here is a working code: https://codepen.io/neptune01/pen/ppMyEQ
Can this be done using only css?
It's a bit tricky, but doable using Flexbox (as is almost anything), text-overflow: ellipsis, and, yes, white-space: nowrap :
Bonus: you don't need line-height or float.
#outer-div {
border: 1px dashed red;
width: 300px;
display: flex;
align-items: center;
}
.inner-div {
border: 1px dashed red;
height: 50px;
width: 80px;
flex-shrink: 0;
}
#title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
border: blue dashed 1px;
}
<div id="outer-div">
<div class="inner-div"></div>
<div class="inner-div"></div>
<span id="title">Random long text not to break</span>
</div>
You can do it with the white-space: nowrap together with the text-overflow: ellipsis and overflow: hidden, also the #title span needs to be displayed as block or you can just use another div instead:
#outer-div {
border: 1px dashed red;
line-height: 50px;
width: 350px;
}
.inner-div {
border: 1px dashed red;
float: left;
height: 50px;
width: 80px;
}
#title {
display: block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
<div id="outer-div">
<div class="inner-div"></div>
<div class="inner-div"></div>
<span id="title">Random long text not to break</span>
</div>
Assign id just to your parent div and not to other elements present inside.
white-space:nowrap makes contents present inside applied div to not wrap them next line, so you need to add overflow:hidden too, which hides content from overflowing outside .title or targeted div, thus this hides content and then apply text-overflow:ellipsis to get (...) in end of overflowed content.
#outer-div {
border: 1px dashed red;
line-height: 50px;
width: 350px;
}
.inner-div {
border: 1px dashed red;
float: left;
height: 50px;
width: 80px;
}
.title {
display: block; /*Changed display to block*/
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
<div id="outer-div">
<div class="inner-div"></div>
<div class="inner-div"></div>
<span class="title">Random long text overflows content out of box.</span>
</div>
I want my two buttons, that are actually <a> tags, stick with my input, and be the same size as input. Image perfectly describes what I want to achieve.
Note that I am just starting to learn SASS and CSS. I have tried with this but no luck
NumberInput.js
<div
className="NumberInput"
data-key={dataKey}>
<div className="numberInputField">
<input
data-key={dataKey}
type="text"
name="number"
value={getValue(datakey)}
onChange={onChange(datakey)}/>
</div>
<div className="buttonsField">
<div className="row">
<ValueChangeButton/>
</div>
<div className="row">
<ValueChangeButton/>
</div>
</div>
</div>
NumberInput.scss
$inputMaxWidth: 450px;
$maxHeight: 80px;
$btnFieldMaxWidth: 150px;
.NumberInput{
max-width: $inputMaxWidth;
max-height: $maxHeight;
.numberInputField{
display: inline-block;
text-align: center;
max-width: inherit;
max-height: inherit;
}
.buttonsField{
display: inline-block;
max-width: $btnFieldMaxWidth;
max-height: $maxHeight;
.button{
width: auto;
height: auto;
}
}
}
The result I get is, buttons are contained in their respective rows, but are not the same size as input, and they are flying all around the page. Also, if I change the className of my input, and set the className of its <div> to "numberInputField", it doesn't change its width and height.
Flexbox is perfect for this:
body {
margin: 1em;
}
.NumberInput {
display: flex;
max-width:450px;
margin:auto;
}
.numberInputField {
flex: 3; /* say 3/4 of width */
display: flex;
flex-direction: column;
}
input {
padding: 1em 4em;
flex: 1;
}
.buttonsField {
flex: 1; /* say 1/4 of width */
display: flex;
flex-direction: column;
}
.row {
flex: 1; /* share width equally */
}
a {
width: 100%;
display: block;
background: rebeccapurple;
text-align:center;
text-decoration: none;
font-size: 1.5em;
color: white;
border:1px solid grey;
}
<div class="NumberInput">
<div class="numberInputField">
<input type="submit" />
</div>
<div class="buttonsField">
<div class="row">
↑
</div>
<div class="row">
↓
</div>
</div>
</div>
I have a nested flex container whose text I want to truncate when it isn't wide enough to show (https://codepen.io/BigMike/pen/mmMxQN).
What I want
(Large Screen)
(Small Screen with truncation)
I thought I could do it with
.truncated {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
but as I shrink the screen, the child reaches a point where it won't get any smaller.
Any suggestions?
Based on your comment, if you add this rule, it will all stay on one line
.regular {
flex-shrink: 0; /* won't allow element to become smaller than its content */
}
Updated codepen
.rows {
display: flex;
flex-wrap: wrap;
}
.child {
flex-basis: 50%;
background-color: red;
min-width: 0px;
}
.nested-row {
display: flex;
padding: 20px;
background-color: blue;
justify-content: space-between;
color: white;
}
.nested-child {
border: solid white 1px;
}
.truncated {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border: solid white 1px;
min-width: 0;
}
.regular {
flex-shrink: 0;
}
<div class="rows">
<div class="child">
<div class='nested-row'>
<div class="nested-child truncated">
I want this text to truncate but it breaks on a small screen
</div>
<div class='nested-child regular'>
Now this doesn't wrap
</div>
</div>
</div>
<div class="child">
Some other content
</div>
<div class="child">
Some other content
</div>
<div class="child">
Some other content
</div>
</div>
The problem is the white space property. You can just edit your style sheet like so.
.truncated {
overflow: hidden;
text-overflow: ellipsis;
white-space: wrap;
}
Seems like it should be possible with flexbox, but I can't figure it out.
http://codepen.io/MichaelJCole/pen/NGBVGe
Goals:
textarea (for typeing in messages) stays at the bottom the whole time.
chats start at the bottom, then scroll up as needed.
If you use the "Google Hangouts", like the message app in that.
Here's the markup:
<div id="chatBar">
<div id="chatList">
<div class="chat mine">hello world</div>
<div class="chat theirs">hello moon</div>
</div>
<input id="chatBarInput" class="form-control" type="textarea">
</div>
And here's the CSS:
html, body { height: 100%; }
#chatBar {
height: 100%;
display: flex;
flex-flow: column nowrap;
justify-content: flex-end;
overflow: none;
}
#chatList {
flex: 0 1 auto;
display: flex;
flex-flow: column nowrap;
justify-content: flex-end;
overflow-y: scroll;
}
#chatBarInput {
flex: 1 0 auto;
}
.chat {
flex: none;
align-self: flex-start;
background-color: lightgreen;
}
.chat.mine {
align-self: flex-end;
background-color: pink;
}
I can't get #chatBar to "squeeze" #chatList without setting a height. Which is what I was trying to avoid by using flexbox :-/
Sorry, I'm a backend coder. Tried a bunch of stuff, then pared it down for the CodePen.
Seems like I should be able to tell the inner flexbox to scroll, while leaving the outer alone. Do I have to use position:absolute?
I can't get #chatBar to "squeeze" #chatList without setting a height.
Which is what I was trying to avoid by using flexbox
You had the flex-basis set to auto for all elements. Without explicit height, the flex model will automatically try to accommodate everything inside the available space by shrinking or expanding the elements. This is why you are unable to get the #chatList to work as intended. The div itself as well as the individual chats all expand or shrink within the available space.
What you should do is to start simple:
#chatBar {
height: 100%; overflow: hidden;
display: flex; flex-flow: column;
}
#chatList {
/* grow or shrink as required from flex-basis height of 20% */
flex: 1 1 20%;
display: flex; flex-direction: column;
overflow: auto;
}
/* do not grow or shrink with a flex-basis height of 80% */
#chatBarInput { flex: 0 0 80%; }
And you will be able to see it working. You could then take it further from here.
Your modified codepen: http://codepen.io/Abhitalks/pen/ZbjNvQ/
Goals:
textarea (for typeing in messages) stays at the bottom the whole time.
chats start at the bottom, then scroll up as needed.
If you use the "Google Hangouts", like the message app in that.
The trick would be to use flex-direction: column-reverse and prepend the new messages to the container instead of appending those.
I took an old answer of mine and changed the layout to flex-model for a demo of this purpose. You can peruse the code to see how it's done.
Demo Fiddle: http://jsfiddle.net/abhitalks/khj4903t/
Demo Snippet:
var btn = document.getElementById('btn'),
inp = document.getElementById('inp'),
chats = document.getElementById('chatWindow')
;
btn.addEventListener('click', postMsg);
inp.addEventListener('keyup', function(e) {
if (e.keyCode == 13) { postMsg(); }
});
function postMsg() {
var msg = inp.value,
bubble = document.createElement('div'),
p = document.createElement('p');
if (msg.trim().length <= 0) { return; }
bubble.classList.add('bubble');
bubble.classList.add('right');
p.textContent = msg;
bubble.appendChild(p);
inp.value = '';
chats.insertBefore(bubble, chats.firstChild);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; overflow: hidden; }
.wrap {
margin: 8px; height: 90%; width: 50%;
display: flex; flex-direction: column;
}
.container {
flex: 1 1 90%; display: flex; flex-direction: column;
background-color: #eee; border: 1px solid #ccc; overflow: auto;
}
.form { flex: 0 0 32px; display: flex; border: 1px solid #ddd; }
.form > input[type=text] { flex: 1 1 auto; border: 1px solid #eee; }
.form > input[type=button] { flex: 0 0 20%; border: 1px solid #eee; }
.bubble { flex: 1 1 auto; clear: both; } /* clear the floats here on parent */
.bubble p {
border-radius: 5px;
padding: 8px; margin: 8px 12px;
max-width: 80%; /* this will make it not exceed 80% and then wrap */
position: relative; transition: background-color 0.5s;
}
.left p { background-color: #ccc; float: left; } /* floated left */
.right p { background-color: #33c; color: #fff; float: right; } /* floated right */
/* classes below are only for arrows, not relevant */
.left p::before {
content: ''; position: absolute;
width: 0; height: 0; left: -8px; top: 8px;
border-top: 4px solid transparent;
border-right: 8px solid #ccc;
border-bottom: 4px solid transparent;
}
.right p::after {
content: ''; position: absolute;
width: 0; height: 0; right: -8px; bottom: 8px;
border-top: 4px solid transparent;
border-left: 8px solid #33c;
border-bottom: 4px solid transparent;
}
<div class="wrap">
<div id="chatWindow" class="container">
<div class="bubble left"><p>msg</p></div>
<div class="bubble left"><p>long message</p></div>
<div class="bubble right"><p>ultra long message which can wrap at eighty percent </p></div>
<div class="bubble left"><p>lorem ipsum</p></div>
<div class="bubble right"><p>very long message</p></div>
<div class="bubble right"><p>one more message</p></div>
<div class="bubble left"><p>lorem ipsum</p></div>
<div class="bubble right"><p>another message</p></div>
<div class="bubble left"><p>lorem ipsum</p></div>
<div class="bubble right"><p>yet another message</p></div>
<div class="bubble left"><p>lorem ipsum</p></div>
</div>
<div id="inputWindow" class="form">
<input id="inp" type="text" />
<input id="btn" type="button" value="Send" />
</div>
</div>
The vertical scrollbar on the browser exists because you've set a height: 100% to the body, and the user agent stylesheet applies a default margin to the body, typically 8px all around. So, 100% + 16px launches the vertical scroll.
Add this to your CSS: body { margin: 0; }
To apply the scrollbar to the inner flexbox (.chatlist), here are two adjustments:
#chatList {
flex: 0 1 75px; /* specify a height */
display: flex;
flex-flow: column nowrap;
/* justify-content: flex-end; REMOVE */
overflow-y: scroll;
}
DEMO: http://jsfiddle.net/5p2vy31p/1/