Block Positon changes after changing innerHTML - javascript

I am trying to make a X-O game
so html is this:
const blocks = document.querySelectorAll(".block")
let turn = "X"
for (let i = 0; i < blocks.length; i++) {
blocks[i].addEventListener("click", function () {
if (this.innerHTML == "") {
this.innerHTML = turn
}}
)}
.block {
width: 100px;
height: 100px;
border: 1px solid black;
text-align: center;
line-height: 100px;
font-size: 50px;
font-weight: bold;
cursor: pointer;
padding: 0;
display: inline-block;
}
#game{
max-width: 400px;
max-height: 400px;
}
<div id="game">
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
</div>
And every thing is OK.
But when I press a block it's position changes a little bit:

I have a slightly different solution using rows and column structure and flex.
Divide the structure into a tabular structure (ex: row, column). Try using display:flex to display the boxes in the same line.
const blocks = document.querySelectorAll(".col");
let turn = "X";
for (let i = 0; i < blocks.length; i++) {
blocks[i].addEventListener("click", function () {
if (this.innerHTML == "") {
this.innerHTML = turn;
}
});
}
.row{
display: flex;
}
.col {
width: 100px;
height: 100px;
border: 1px solid black;
text-align: center;
line-height: 100px;
font-size: 50px;
font-weight: bold;
cursor: pointer;
padding: 0;
}
#game {
max-width: 400px;
max-height: 400px;
}
<div id="game">
<div class="row">
<div class="col"></div>
<div class="col"></div>
<div class="col"></div>
</div>
<div class="row">
<div class="col"></div>
<div class="col"></div>
<div class="col"></div>
</div>
<div class="row">
<div class="col"></div>
<div class="col"></div>
<div class="col"></div>
</div>
</div>

Instead of using inline-block for your blocks, you could use a flex (display: flex) and wrapped (flex-wrap: wrap) container with fixed width and height for your #game div.
Using flex-wrap saves you from manually splitting your rows.
display: flex, by default, set flex-direction to row.
Using flex-wrap: wrap, if the content horizontally exceeds the width of the container, it is wrapped.
In our case, the container has dimention 400px, so we will get 3 element per row (since every block has width: 100px).
As in this runnable example
const blocks = document.querySelectorAll(".block")
let turn = "X"
for (let i = 0; i < blocks.length; i++) {
blocks[i].addEventListener("click", function() {
if (this.innerHTML == "") {
this.innerHTML = turn
}
})
}
#game {
/* Flex Wrapped Container*/
display: flex;
justify-content: start;
align-items: center;
flex-wrap: wrap;
/* Dimensions */
max-width: 400px;
max-height: 400px;
}
.block {
width: 100px;
height: 100px;
border: 1px solid black;
margin: 1px; /* margin for some gap between boxes */
text-align: center;
line-height: 100px;
font-size: 50px;
font-weight: bold;
cursor: pointer;
padding: 0;
display: inline-block;
vertical-align: center;
}
<div id="game">
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
</div>

Related

Make rows expand to scrollable parent's width

I’m making a table-like layout using flexbox.
It has a body, rows and cells.
Cells have the fixed widths in pixels.
Body should be horizontally scrollable.
I would like to achieve that rows have the same width as cumulative widths of cells.
Here’s the codepen.
<div class="grand-parent">
<div class="parent">
<div class="child">Child 1</div>
<div class="child">Child 2</div>
<div class="child">Child 3</div>
</div>
<div>
Is there any way to do this using CSS only?
P.S. I know I can calculate the widths using JS and set that to parent, or I can set the background color to grand-parent to make it look nicer, but that's not what I need here.
one way is to not fix size on child component but use flex-grow: 1; to let them take all available size
.child {
flex-grow: 1;
color: white;
background: blue;
border: 1px solid black;
}
OR
if you want to force container to take child width you have to :
.child have display: inline-flex;
.parent have width: max-content;
.parent {
background: red;
padding: 2em 0;
width: max-content;
}
.child {
display: inline-flex;
width: 300px;
color: white;
background: blue;
border: 1px solid black;
}
following the running snippet of this reply
.grand-parent {
width: 800px;
overflow: auto;
}
.parent {
display: flex;
background: red;
padding: 2em 0;
}
.child {
flex-grow:1;
color: white;
background: blue;
border: 1px solid black;
}
.parent2 {
background: red;
padding: 2em 0;
width: max-content;
}
.child2 {
display: inline-flex;
width: 300px;
color: white;
background: blue;
border: 1px solid black;
}
::-webkit-scrollbar {
-webkit-appearance: none;
width: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: rgba(0, 0, 0, .5);
box-shadow: 0 0 1px rgba(255, 255, 255, .5);
}
<h1> child take width of parent</h1>
<div class="grand-parent">
<div class="parent">
<div class="child">Child 1</div>
<div class="child">Child 2</div>
<div class="child">Child 3</div>
</div>
<div>
<h1> parent take width of child</h1>
<div class="grand-parent">
<div class="parent2">
<div class="child2">Child 1</div>
<div class="child2">Child 2</div>
<div class="child2">Child 3</div>
</div>
<div class="parent2">
<div class="child2">Child 1</div>
<div class="child2">Child 2</div>
<div class="child2">Child 3</div>
</div>
<div>
Well, just like Jeremy's answer, you can set them to take available space
OR
Instead of making the grand-parent fixed-width, you can set the parent to be of fixed width;
.parent {
display: flex;
background: red;
padding: 2em 0;
width: 800px;
overflow: auto;
}
I took all the styles of grand-parent and added the to the style of parent
The following snippet works perfectly fine:
.parent {
display: flex;
background: red;
padding: 2em 0;
width: 800px;
overflow: auto;
}
.child {
flex: 0 0 300px;
color: white;
background: blue;
border: 1px solid black;
}
<div class="grand-parent">
<div class="parent">
<div class="child">Child 1</div>
<div class="child">Child 2</div>
<div class="child">Child 3</div>
</div>
</div>

How to use scrollWidth value of parent element using css?

I want to set width and height for cover element same as scrollWidth and scrollHeight of container element. So during the scrolling it will fully cover container. At the moment it uses width and height of container element cropped by scroll.
The whole area should be covered by green color. Is it possible to do using only css?
.container {
width: 400px;
height: 500px;
border: solid 2px;
position: relative;
overflow: auto;
}
.row {
width: 500px;
height: 60px;
border: solid 4px blue;
margin: 10px;
}
.cover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: green;
opacity: 0.5;
}
<div class="container">
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="cover"></div>
</div>
I don't know your the final goal is, but have you tried simply applying the background to the container?
.container {
width: 400px;
height: 500px;
border: solid 2px;
overflow: auto;
background-color: rgba(0,128,0,.5);
}
.row {
width: 500px;
height: 60px;
border: solid 4px blue;
margin: 10px;
}
<div class="container">
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
</div>
You can use JavaScript to get the scrollWidth and scrollHeight of the parent.
const container = document.querySelector('.container');
const cover = document.querySelector('.cover');
cover.style.height = container.scrollHeight + "px";
cover.style.width = container.scrollWidth + "px";
.container {
width: 400px;
height: 500px;
border: solid 2px;
position: relative;
overflow: auto;
}
.row {
width: 500px;
height: 60px;
border: solid 4px blue;
margin: 10px;
}
.cover {
position: absolute;
top: 0;
left: 0;
background-color: green;
opacity: 0.5;
}
<div class="container">
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="cover"></div>
</div>
Here's a Javascript code to do it.
Quick reminder: document.querySelector(".cover") here return just the first element met but if there are more use document.querySelectorAll()
EDIT : You can do it with only CSS too by wrapping the rows in the cover (It's why i put Javascript in comments)
/*const { scrollWidth, scrollHeight } = document.querySelector(".container");
const coverElement = document.querySelector(".cover");
coverElement.style.width = `${scrollWidth}px`;
coverElement.style.height = `${scrollHeight}px`; */
.container {
width: 400px;
height: 500px;
border: solid 2px;
position: relative;
overflow: auto;
}
.row {
width: 500px;
height: 60px;
border: solid 4px blue;
margin: 10px;
}
.cover {
position: absolute;
top: 0;
left: 0;
background-color: green;
opacity: 0.5;
}
<div class="container">
<div class="cover">
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
</div>
</div>
try this
.container {
width: 400px;
height: 500px;
border: solid 2px;
position: relative;
overflow: auto;
}
.cover {
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
background-color: green;
opacity: 0.5;
}
.row {
width: 500px;
height: 60px;
border: solid 4px blue;
margin: 10px;
}
<div class="container cover">
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>
</div>
</div>

How to hide a flexbox element with smoth effect

I need to center divs and hide them on each click, the problem is when I use hide() and flexbox it makes a rude effect after dissapear, but if you just simply float elements to left it makes fine, how can I achieve this?
I need to apply exactly the same disappearing effect that is in the
first example to the second one (with flexbox).
Here is the example:
$(".example1, .example2").click(function(){
$(this).hide("slow")
});
.main{
border: 2px solid black;
height: 100%;
width: 100%;
}
.example1{
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
float: left;
}
.example2{
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
float: left;
text-align: center;
margin-left: 8px;
}
.second{
border: 2px solid black;
display: flex;
display: -webkit-flex;
flex-wrap: wrap;
-webkit-flex-wrap: wrap;
justify-content: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
With simple float left it hides slowly fine:
<div class="first">
<div class="example1">1</div>
<div class="example1">2</div>
<div class="example1">3</div>
<div class="example1">4</div>
<div class="example1">5</div>
<div class="example1">6</div>
<div class="example1">7</div>
<div class="example1">8</div>
<div class="example1">9</div>
<div class="example1">10</div>
<div class="example1">11</div>
<div class="example1">12</div>
<div class="example1">12</div>
<div class="example1">13</div>
<div>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
Now flex center, when you hide it makes rude effect, it isnt like div.example1:
<div class="second">
<div class="example2">1</div>
<div class="example2">2</div>
<div class="example2">3</div>
<div class="example2">4</div>
<div class="example2">5</div>
<div class="example2">6</div>
<div class="example2">7</div>
<div class="example2">8</div>
<div class="example2">9</div>
<div class="example2">10</div>
<div class="example2">11</div>
<div class="example2">12</div>
<div class="example2">13</div>
<div class="example2">14</div>
</div>
</div>
Use flex-start for justify content instead of center. Now it has the same effect as with float. You can also use fadeOut instead of hide to achieve effect you want.
$(".example1, .example2").click(function(){
$(this).fadeOut("slow")
});
.main{
border: 2px solid black;
height: 100%;
width: 100%;
}
.example1{
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
float: left;
}
.example2{
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
display: flex;
justify-content: flex-start;
text-align: center;
margin-left: 8px;
}
.second{
border: 2px solid black;
display: flex;
display: -webkit-flex;
flex-wrap: wrap;
-webkit-flex-wrap: wrap;
justify-content: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
With simple float left it hides slowly fine:
<div class="first">
<div class="example1">1</div>
<div class="example1">2</div>
<div class="example1">3</div>
<div class="example1">4</div>
<div class="example1">5</div>
<div class="example1">6</div>
<div class="example1">7</div>
<div class="example1">8</div>
<div class="example1">9</div>
<div class="example1">10</div>
<div class="example1">11</div>
<div class="example1">12</div>
<div class="example1">12</div>
<div class="example1">13</div>
<div>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
Now flex center, when you hide it makes rude effect, it isnt like div.example1:
<div class="second">
<div class="example2">1</div>
<div class="example2">2</div>
<div class="example2">3</div>
<div class="example2">4</div>
<div class="example2">5</div>
<div class="example2">6</div>
<div class="example2">7</div>
<div class="example2">8</div>
<div class="example2">9</div>
<div class="example2">10</div>
<div class="example2">11</div>
<div class="example2">12</div>
<div class="example2">13</div>
<div class="example2">14</div>
</div>
</div>
First, you can notice that this issue doesn't happen when you try to remove an item from the last row (excluding the first one in the last row). The issue appears when the first element of the row n suddenly go to the row n-1 because of 2 things :
You are trying to remove this first element so its width is going to 0 then for sure he will be able to fit into the previous row.
You are trying to remove any element so its width is going to 0 and you are creating enough space for the first element of next row to jump on it.
And this is simply due to center alignment as there is no difference if you do it with float, inline-block or flex. What is happening is that during the transition all the elements are moving to the center and when the new element comes (the first one of the next row) all the elements are re-placed again to keep the center alignement and then you have the rude effect !
With left alignment all the elements will move to the left during the transition and they won't move again at the end of transition (when the new element comes) so we don't have any rude effect.
Here is a snippet that shows inline-block and flex working fine with left alignment :
$(".example2, .example1").click(function() {
$(this).hide("slow");
});
.main {
border: 2px solid black;
height: 100%;
width: 100%;
}
.example1 {
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
text-align: center;
display:inline-block;
margin: 8px;
transition:margin 0.6s;
}
.example2 {
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
text-align: center;
float:left;
display:inline-block;
margin: 8px;
transition:margin 0.6s;
}
.first {
border: 2px solid black;
display: flex;
flex-wrap: wrap;
}
.second {
border: 2px solid black;
display: flex;
flex-wrap: wrap;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
inline-block
<div class="first">
<div class="example1">1</div>
<div class="example1">2</div>
<div class="example1">3</div>
<div class="example1">4</div>
<div class="example1">5</div>
<div class="example1">6</div>
<div class="example1">7</div>
<div class="example1">8</div>
<div class="example1">9</div>
<div class="example1">10</div>
<div class="example1">11</div>
<div class="example1">12</div>
<div class="example1">13</div>
<div class="example1">14</div>
<div class="example1">15</div>
<div class="example1">16</div>
<div class="example1">17</div>
<div class="example1">18</div>
<div class="example1">19</div>
</div>
flex solution
<div class="second">
<div class="example2">1</div>
<div class="example2">2</div>
<div class="example2">3</div>
<div class="example2">4</div>
<div class="example2">5</div>
<div class="example2">6</div>
<div class="example2">7</div>
<div class="example2">8</div>
<div class="example2">9</div>
<div class="example2">10</div>
<div class="example2">11</div>
<div class="example2">12</div>
<div class="example2">13</div>
<div class="example2">14</div>
<div class="example2">15</div>
<div class="example2">16</div>
<div class="example2">17</div>
<div class="example2">18</div>
<div class="example2">19</div>
</div>
</div>
Unfortunately, I don't have a solution to this if you want to only use the hide() of jQuery. Maybe some ideas of solution is to make a more complex code that will avoid the centered elements to move in two directions (you may for example change margin property at the same time to cancel the movement) or you can keep the left alignment and find some trick to simulate the centering (dynamically add some margin when window resize for example).
Hope this will help you to investigate more (even if I didn't really give a solution).
Well as pointed out already it would require some kind of "physics engine" moving the other blocks up smoothly etc.
But I made an attempt anyway which looks a bit more smooth at least.
$(".example1, .example2").click(function(){
var time = 600;
var $parent = $(this).parent();
$parent.animate({'width': '90%'}, time/2, function() {
$parent.animate({'width': '100%'}, time/2);
});
$(this).hide(time);
});
.main{
border: 2px solid black;
height: 100%;
width: 100%;
}
.example1{
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
float: left;
}
.example2{
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
float: left;
text-align: center;
margin-left: 8px;
}
.second{
display: flex;
display: -webkit-flex;
flex-wrap: wrap;
-webkit-flex-wrap: wrap;
justify-content: center;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
<div class="second">
<div class="example2">1</div>
<div class="example2">2</div>
<div class="example2">3</div>
<div class="example2">4</div>
<div class="example2">5</div>
<div class="example2">6</div>
<div class="example2">7</div>
<div class="example2">8</div>
<div class="example2">9</div>
<div class="example2">10</div>
<div class="example2">11</div>
<div class="example2">12</div>
<div class="example2">13</div>
<div class="example2">14</div>
</div>
</div>
You can achieve the above without flex by making the children div's as inline-block with the parent being set with text-align:center, please take a look at this.
$(".example1, .example2").click(function(){
$(this).fadeOut("slow");
});
.main{
border: 2px dotted black;
height: 100%;
width: 100%;
}
.example1{
display: inline-block;
background-color: steelblue;
color: #FFF;
cursor: pointer;
margin: 10px;
padding: 15px 20px;
}
.first{
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
<div class="main">
<div class="first">
<div class="example1">1</div>
<div class="example1">2</div>
<div class="example1">3</div>
<div class="example1">4</div>
<div class="example1">5</div>
<div class="example1">6</div>
<div class="example1">7</div>
<div class="example1">8</div>
<div class="example1">9</div>
<div class="example1">10</div>
<div class="example1">11</div>
<div class="example1">12</div>
<div class="example1">12</div>
<div class="example1">13</div>
</div>
</div>
My idea is: fade the whole parent container during reordering.
The effect will not so rude.
$(".second div").click(function() {
$(this).hide("slow");
var p = $(this).parent();
p.addClass("hidden");
setTimeout(function() {
p.removeClass("hidden")
}, 300);
});
p {
clear: both;
}
.second {
display: flex;
flex-wrap: wrap;
justify-content: center;
border: 2px solid black;
transition: 200ms;
}
.second div {
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
}
.hidden {
opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="second">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
</div>
Instead of justify-content: center I changed it to justify-content: space-evenly (in your case, looks somewhat similar to center only) also updated the function from simply hiding to .animate and then .hide. Will it do?
$(".example1, .example2").click(function(){
var _this = this;
$(_this).animate({width: "0"}, 500, function(){ $(_this).hide(500) })
});
.main{
border: 2px solid black;
height: 100%;
width: 100%;
}
.example1{
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
float: left;
}
.example2{
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
float: left;
text-align: center;
margin-left: 8px;
overflow: hidden;
}
.second{
border: 2px solid black;
display: flex;
display: -webkit-flex;
flex-wrap: wrap;
-webkit-flex-wrap: wrap;
justify-content: space-evenly;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
With simple float left it hides slowly fine:
<div class="first">
<div class="example1">1</div>
<div class="example1">2</div>
<div class="example1">3</div>
<div class="example1">4</div>
<div class="example1">5</div>
<div class="example1">6</div>
<div class="example1">7</div>
<div class="example1">8</div>
<div class="example1">9</div>
<div class="example1">10</div>
<div class="example1">11</div>
<div class="example1">12</div>
<div class="example1">12</div>
<div class="example1">13</div>
<div>
<br><br><br><br><br><br><br><br><br><br><br><br><br>
Now flex center, when you hide it makes rude effect, it isnt like div.example1:
<div class="second">
<div class="example2">1</div>
<div class="example2">2</div>
<div class="example2">3</div>
<div class="example2">4</div>
<div class="example2">5</div>
<div class="example2">6</div>
<div class="example2">7</div>
<div class="example2">8</div>
<div class="example2">9</div>
<div class="example2">10</div>
<div class="example2">11</div>
<div class="example2">12</div>
<div class="example2">13</div>
<div class="example2">14</div>
</div>
</div>

A non table-cell in a display: table behaves different

In this JsBin example, a particular row which does not have cells but has generic divs, does not consider the table's 100% width even though table width is explicitly specified as 100%.
Is there a way to fix this?
<div class="table">
<div class="row clearfix">
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>
</div>
<div class="row">
<div class="cell">cell1</div>
<div class="cell">cell2</div>
<div class="cell">cell3</div>
</div>
<div class="row">
<div class="cell">cell1</div>
<div class="cell">cell2</div>
<div class="cell">cell3</div>
</div>
</div>
.table {
display: table;
width: 100%;
border: 1px solid blue;
}
.row {
display: table-row;
width: 100%;
}
.cell {
display: table-cell;
border: 1px solid black;
}
.section {
width: 30%;
float: left;
height: 30px;
border: 1px solid red
}
.clearfix:after {
content: " ";
display: table;
clear: both;
}

text-overflow: ellipsis and flex

I have a container element with display: flex property set.
A children have fixed width (flex: 0 0 auto), another one no (flex: 1). The flexible children has some other children: I want this container (inner) to clip its children according to the parent width.
I managed to do this, but also I'd like to get the ellipsis overflow in case the content is clipped (the number of children is not fixed).
This is my code so far:
.outer {
border: 1px solid red;
display: flex;
width: 400px;
}
.inner {
display: flex;
min-width: 0;
overflow: hidden;
flex: 1;
text-overflow: ellipsis;
}
.child {
display: inline-block;
white-space: nowrap;
flex: 1;
border: 1px solid blue;
}
.btn {
border: 1px solid green;
flex: 0 0 auto;
}
Live here: http://jsbin.com/niheyiwiya/edit?html,css,output
How can I get the following desired result? (hacks welcome - css only please!)
There are a few problems with your layout:
text-overflow: ellipsis only works with display: block and display: inline-block containers. It's failing because you have .inner set to display: flex.
text-overflow: ellipsis must include white-space: nowrap in the same declaration. It's missing in your .inner rule.
ellipsis works on text, not block-level elements
Try this:
* {
margin: 15px 1px
}
.outer {
border: 1px solid red;
display: flex;
width: 400px;
}
.inner {
/* display: flex */ /* removed */
min-width: 0;
overflow: hidden;
flex: 1;
text-overflow: ellipsis;
white-space: nowrap; /* new */
}
.child {
display: inline; /* adjusted */
white-space: nowrap;
flex: 1;
}
.btn {
border: 1px solid green;
flex: 0 0 auto;
}
<div class="outer">
<div class="inner">
<div class="child">child 1</div>
<div class="child">child 2</div>
<div class="child">child 3</div>
<div class="child">child 4</div>
<div class="child">child 5</div>
<div class="child">child 6</div>
<div class="child">child 7</div>
</div>
<div class="btn">My big big big button!</div>
</div>
More about text-overflow: ellipsis here: Applying an ellipsis to multiline text
Here is JS approach where you could find which child div have position that overflows with position of button, and hide all divs after that div and append ... after that div.
var child = $('.child');
var btn = $('.btn');
var oW = $('.outer').innerWidth();
var w = btn.outerWidth();
var c = oW - w;
var last;
child.each(function() {
var l = $(this).position().left + $(this).outerWidth();
if (l > c) {
if (last == undefined) last = $(this).index() - 1;
}
})
$('.child:gt(' + last + ')').css('display', 'none');
$('.child:eq(' + last + ')').after(' ...')
* {
margin: 15px 1px
}
.outer {
border: 1px solid red;
display: flex;
width: 400px;
}
.inner {
overflow: hidden;
white-space: nowrap;
flex: 1;
}
.child {
border: 1px solid blue;
display: inline-block;
}
.btn {
border: 1px solid green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer">
<div class="inner">
<div class="child">child 1</div>
<div class="child">child 2</div>
<div class="child">child 3</div>
<div class="child">child 4</div>
<div class="child">child 5</div>
<div class="child">child 6</div>
<div class="child">child 7</div>
</div>
<div class="btn">My big big big button!</div>
</div>
In addition to the previous answer:
if you nest the flex-elements, than you have to add
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
to the parent container otherwise the hiding of the overflow may not work any more.
In some cases you need width: 0; in addition/instead of to min-width: 0;.
A great example with min-width: 0; which just works: https://css-tricks.com/flexbox-truncated-text/
BUT:
If your text to trancate is inside the a-tag, than you have to wrap that a-tag, in e.g. h2 or p. Otherwise the text will not be truncated because of the a-tag!
So here is the working example with the a-tag basing on example above:
.flex-parent {
display: flex;
align-items: center;
}
.long-and-truncated-with-child-corrected {
flex: 1;
min-width: 0;
/* or some value */;
}
.long-and-truncated-with-child-corrected h2 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.short-and-fixed {
white-space: nowrap;
}
.short-and-fixed > div {
width: 30px;
height: 30px;
border-radius: 10px;
background: lightgreen;
display: inline-block;
}
<div class="flex-parent">
<div class="long-and-truncated-with-child-corrected">
<h2>
333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
</h2>
</div>
<div class="long-and-truncated-with-child-corrected">
<h2>
<a href="https://vgn.de">
44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444
</a>
</h2>
</div>
<div class="short-and-fixed">
<div></div>
<div></div>
<div></div>
</div>
</div>

Categories