display: inline-block vs display: table (no display: table-cell) - html

I have used both of the display properties whenever I wanted the width of an element to be the same as its content.
But is one better than the other? I'm referring to accessibility, browser compatibility, responsiveness, etc.
Note: I am asking because I'm planing to use only one of these in my new web site. I just don't know which one is better, if any.

Ultimately, it depends on the use case:
display: inline-block will create an inline-block element
display: table will create a table element
Here they are in use:
span.mySpan {
background-color: red;
}
<div>
<span>A span element.</span>
<span class="mySpan" style="display: table;">a <code>display: table</code> element.</span>
<span>Another span element.</span>
</div>
<br/>
<br/>
<div>
<span>A span element.</span>
<span class="mySpan" style="display: inline-block;">a <code>display: inline-block</code> element.</span>
<span>Another span element.</span>
</div>
As can be seen, the results are very different. The table element positions itself on a new line, and causes the next element to be on a new line as well. The inline-block element positions itself inline with it's sibling elements.
In many cases, the above differences will be enough to choose one or the other.
If not, let's continue...
There are some cases when display: table is useful:
Horizontal and vertical centering of elements
Equal height elements
However, browsers can produce inconsistent results when not implemented correctly so you should always couple display: table with the standard table markup (using rows and cells):
.table {
display: table;
}
.table-row {
display: table-row:
}
.table-cell {
display: table-cell;
background-color: #eaeaea;
padding: 10px;
}
<div class="table">
<div class="table-row">
<div class="table-cell">
Content
</div>
<div class="table-cell" style="height: 100px;">
Content
</div>
<div class="table-cell">
Content
</div>
</div>
</div>
This becomes pretty tedious. And with modern CSS we can accomplish the same using display: flex, with a simpler HTML structure and less CSS:
.flex {
display: flex;
}
.flex-cell {
background-color: #eaeaea;
padding: 10px;
}
<div class="flex">
<div class="flex-cell">
Content
</div>
<div class="flex-cell" style="height: 100px;">
Content
</div>
<div class="flex-cell">
Content
</div>
</div>
Honestly, I can't think of many times I would need to decide between display: inline-block and display: table as they produce such different results. However, if I were on the fence I'd follow this decision tree:
Do I need to make a table? Use a true <table></table> element
Do I need equal height/width elements, and/or vertical centering? Use a display: flex element
Otherwise, use the appropriate HTML element (display: inline-block)

Related

Width of vertically aligning inline-flexbox grows with number of children [duplicate]

This question already has answers here:
Make container shrink-to-fit child elements as they wrap
(4 answers)
CSS when inline-block elements line-break, parent wrapper does not fit new width
(2 answers)
Closed 4 years ago.
I want to visualize source-code-like structures in HTML using some sort of "block" analogy. But for whatever reason I walk into some strange sizing issues with my flexboxes. The following snippet displays a visualisation of a program that would "normally" be printed like this:
while(true) {
goForward();
goForward();
goForward();
goForward();
goForward();
}
The outer box for the while is displayed as inline-flex to consume as little horizontal space as possible. But as you can see in the screenshot (or you may run the snippet yourself) there is quite a lot space wasted:
What I expect would look like this:
If you however click the goForward() blocks (which marks them with display: none), the width of the parenting block suddenly shrinks. From what I can tell it shrinks about as much as the width of the now hidden block.
The "linebreaks" between goForward() blocks are implemented using height: 0 but width: 100% elements. I also tried to do the breaks without empty elements and break-after: always, but this leads to the exact some behavior of the outer flexbox. I have observed this behavior in the most recent versions of Firefox and Chrome.
Why does the width of the outermost inline-flex element change (seemingly) with the number of items it displays vertically? And how could I "properly" implement this kind of layout where I basically want to have a block layout with arbitrary "linebreaks"?
// Hide blocks on click to demonstrate width changes
Array.from(document.querySelectorAll(".forward")).forEach(
elem => elem.addEventListener('click', () => elem.classList.add("hide"))
);
.code-block {
border: 2px solid blue;
border-radius: 5px;
padding: 5px;
display: inline-flex;
flex-flow: row wrap;
align-items: baseline;
}
.line-break {
width: 100%;
}
.forward {
cursor: pointer;
}
.hide {
display: none;
}
<div class="code-block">
<div class="terminal">while(</div>
<div class="code-block">true</div>
<div class="terminal">)</div>
<div class="line-break"></div>
<div class="code-block forward">
<div class="terminal">goForward()</div>
</div>
<div class="line-break"></div>
<div class="code-block forward">
<div class="terminal">goForward()</div>
</div>
<div class="line-break"></div>
<div class="code-block forward">
<div class="terminal">goForward()</div>
</div>
<div class="line-break"></div>
<div class="code-block forward">
<div class="terminal">goForward()</div>
</div>
<div class="line-break"></div>
<div class="code-block forward">
<div class="terminal">goForward()</div>
</div>
</div>
Instead of flex-flow: row wrap;, try flex-direction: column;. This gets each item to go vertically. Then wrap everything you want to be in a single "line" in its own inside of .code-block, to keep the contents of each line together. So,
<div class="code-block">
<div class="line-break">
<div class="terminal">while(</div>
<div class="code-block">true</div>
<div class="terminal">)</div>
</div>
...and so on for the others.
(You shouldn't need align-items: baseline; either.)

Making text elements in HTML take up a fixed amount of space without using tables?

I have a web page that has five buttons of width 50 arranged next to each other in a row, and above each one, I want there to be a text item. However, putting each one in a <span> or a <div style="display:inline"> does not pad them correctly with either "width="50"" or adding "width:50px" to the style; they just appear next to each other. The "obvious" answer is to put each item into a table cell, but W3C says this is a Bad Thing now.
I also tried using input tags with readonly set; these space properly, but the text appears in input boxes rather than "on the page background."
Is there a way to align label elements (that can be changed in the script) evenly spaced horizontally without using a table?
There are a few possible solutions. Either you can use display: table-cell, which perfectly follows the W3C recommendations or you can use a flex box which is an even better solution. However the flex box is still quite new and you may want to support an older browser so the display: table-cell approach might work at least as a fallback.
Please, see the working fiddle.
HTML
<div class="container">
<div class="row">
<span>Text 1</span>
<span>Text 2</span>
<span>Text 3</span>
<span>Text 4</span>
</div>
</div>
CSS
.container {
display: table;
width: 100%;
}
.row {
display: table-row
}
span {
display: table-cell;
text-align: center;
}
Inline elements can't have fixed width or height. Try adding display: inline-block;.
Elements of display: inline don't take a width property, their size is dictated by their contents; to allow for elements to appear in-line with their siblings and to also accept a width switch their display property to that of inline-block.
It's because inline elements does not have fixed width. They are automatically set to fit in the space. You need to set display: inline-block to set width of an inline element.
Try this:
<style>
.btnbtnSpace{
width:50px;
display: inline-block;
}
</style>
<div class="btnbtnSpace">Text<br /><button > 1</button></div>
<div class="btnbtnSpace">Text<br /><button > 2</button></div>
<div class="btnbtnSpace">Text<br /><button > 2</button></div>
<div class="btnbtnSpace">Text<br /><button > 4</button></div>
<div class="btnbtnSpace">Text<br /><button > 5</button></div>
"display: inline-block" worked. I didn't notice it at first because the options on the "CSS Display" page at W3Schools didn't include it; inline-block gets its own page for some reason.

Display p elements as block within an inline-block span

So I have some inline-block elements like so:
<span style="display: inline-block">
<img>
<p>Some text</p>
<p>Some more text</p>
<button>A button</button>
</span>
I want them all inline except I want the first p element positioned on top of the other one yet have both together inline with the rest of the span. From what I've been reading, it's bad practice to put a div inside a span, so what's the best way to do this?
It's not "bad practice", it's simply impossible. The browser will "correct" your HTML and it will not behave as expected.
Try using <div style="display:inline-block"> as your container instead.
Here's a wild guess at what you're after based on my comment above.
.table {
display: table;
width: 100%;
}
.row {
display: table-row;
}
.cell {
display: table-cell;
padding: 5px;
background: #ddd;
border: 1px solid #aaa;
vertical-align: top;
}
<div class="table">
<div class="row">
<div class="cell">
<img src="http://lorempixel.com/300/200" />
</div>
<div class="cell">
<div class="table">
<div class="row">
<div class="cell">
<p>Paragraph one. Paragraph one. Paragraph one.</p>
</div>
</div>
<div class="row">
<div class="cell">
<p>Paragraph two. Paragraph two. Paragraph two.</p>
</div>
</div>
</div>
</div>
<div class="cell">
<button>Button</button>
</div>
</div>
</div>
As #Neit pointed out, the browser will correct the DOM when you put block-level elements inside inline elements (see first example). A div, or maybe a section is definitely a better choice both for valid and semantic markup.
Using CSS to change display: does work, but it isn't best practice (for example an em in place of your span above will render exactly the same). Certain versions of browsers will also ignore some types of display: changes; thus, your code would fail. So using a better container is going to provide the fewest headaches.
See the code here:
https://jsfiddle.net/9mf91b1v/

Why does this behave the way it does with max-width: 0?

See http://jsfiddle.net/mpx7os95/14/
The behavior is the desired behavior, which allows the center column in a 3 column layout to take up all the space available AND still allow the text inside of it to overflow into ellipsis when shrunk far enough. This seems to work due to max-width: 0, but why does it produce this effect?
What's so special about max-width: 0 in this case?
.row {
width: 100%;
display: table;
}
.col {
position: relative;
min-height: 1px;
border: 1px #000 solid;
display: table-cell;
}
.x {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width:100%;
max-width: 0;
}
<div class="row">
<div class="col">abc</div>
<div class="col x">test blah blah blah blah blah zzzzz.</div>
<div class="col">def</div>
</div>
Note: It's not just max-width:0, it's any width less than the text content's width.
The mixture of display: table-cell, max-width, and width:100% make an interesting situation that the specifications don't explicitly cover. However, by making some observations and thinking, we can understand why we have the same behavior across all major browsers.
max-width:0 tries to make the element width 0, of course, meaning that everything should be overflow. However, the combination of display: table-cell and width:100%, as seen in the linked demo, override that command for the div (maybe for the same reason why max-width does not apply to table rows) for the most part. As such, the element displays itself like it would without the max-width:0 until the width of the div is no longer large enough to contain all of the text content.
A thing to note before we continue is that the text itself by default has a width that is set by the characters inside of it. It's kind of a child element of the parent div, though there are no tags.
This innate width of the text content is the reason why max-width:0 is needed to create the effect. Once the width of the div is no longer large enough to contain all of the content, the max-width:0 property enables the width to become less than the width of the text content, but forces the text that can no longer fit to become overflow of the div itself. Thus, since the div now has text overflow and text-overflow: ellipsis is set, the text overflow is ellipsed (if that's a word).
This is very useful to us because otherwise we couldn't make this effect without a lot of messy JavaScript. Use case demo
Note: This answer describes the behavior and gives some insight as to why this is the case. It doesn't cover how display:table-cell overrides part of the max-width's effect.
This is not to be a complete answer, but a contribution.
What's so special about max-width: 0 in this case?
I'm not sure, but the cell seems to give another chance to adjust. (maybe this, algorithm point 2)
According to my experiments applies only replaced elements with intrinsic width. In this case the text block has intrinsic width by white-space: nowrap.
Items without intrinsic width fit well without using max-width: 0.
http://jsfiddle.net/rnrlabs/p3dgs19m/
* {
box-sizing: border-box;
}
.table {
display: table;
width: 300px;
}
.row {
width: 100%;
display: table-row;
}
.col {
border: 1px #000 solid;
display: table-cell;
}
.a {
min-width: 80px;
}
.x {
background: #0cf;
overflow: hidden;
text-overflow: ellipsis;
width:100%;
}
.y {
max-width: 0;
}
.z {
white-space: nowrap;
}
<div class="table">
<div class="row">
<div class="col">abc</div>
<div class="col x">
<img src="http://placehold.it/500x50&text=InlineReplacedIntrinsicWidth" />
</div>
<div class="col">end</div>
</div>
</div>
<div class="table">
<div class="row">
<div class="col">abc</div>
<div class="col x y">
<img src="http://placehold.it/500x50&text=InlineReplacedIntrinsicWidthPlusMaxWidth" />
</div>
<div class="col">end</div>
</div>
</div>
<div class="table">
<div class="row">
<div class="col">abc</div>
<div class="col x z">Intrinsic Width due white-space property set to nowap.</div>
<div class="col">end</div>
</div>
</div>
<div class="table">
<div class="row">
<div class="col">abc</div>
<div class="col x y z">Intrinsic Width due white-space property set to nowap and Max-Width</div>
<div class="col">end</div>
</div>
</div>
<div class="table">
<div class="row">
<div class="col">abc</div>
<div class="col x"><span>IntrinsicWidthduenospacesintextandblahIntrinsicWidthduenospacesintextandblah</div>
<div class="col">end</div>
</div>
</div>
<div class="table">
<div class="row">
<div class="col">abc</div>
<div class="col x y"><span>IntrinsicWidthduenospacesintextandMaxWidth</div>
<div class="col">end</div>
</div>
</div>
<div class="table">
<div class="row">
<div class="col">abc</div>
<div class="col x y">Non Intrinsic Width. Inline, non-replaced elements. <strong> Inline, non-replaced elements. </strong> Inline, non-replaced elements.</div>
<div class="col">end</div>
</div>
</div>
I think BoltClock's comment on Zach Saucier's answer - that the behaviour is undefined - is a good reason to avoid relying on the fact that today's browsers happen to exhibit the desired behaviour.
Many people (like me) will arrive at this question not because they have an academic interest in what max-width: 0 means in this case, but because they want to know whether it's OK to use that hack to get the ellipsis to show for text in a table cell, and FWIW I think the answer is: "not really".
A better way to get the ellipsis to work in that case is to use "table-layout: fixed" on the <table> element, as suggested in this answer.

Achieve table cell effect with floated divs

If I try to apply min-width, max-width to a floating div so that it expands to max-width when the right content is hidden does not work.
But, if I use table and 2 tds in it, the left td will expand to 100% if the right td is hidden.
Can I achieve this table effect with floated divs?
I don't think you can do what you are asking, but you can make it look like what you are asking.
Make it into two tds and put a max-width on a div inside the td. Would that work?
This isn't going to work with floats. Luckily we now have more tools at our disposal.
Here are two very simple methods to expand a div to 100% of the available width if a sibling horizontally to it is hidden or removed.
#1 – Using display: flex
Compatibility: Edge and all modern browsers. IE 10 and 11 support the non-standard -ms-flexbox.
The Basic Markup
<div class="container">
<div>
First Column
</div>
<div>
This second column can be hidden or not exist and the first column will take up its space
</div>
</div>
The CSS
The container div is given display: flex.
The containers children are give flex: 1 and they will be assigned equal width, can grow and can shrink.
.container {
width: 500px;
display: flex;
}
.container>div {
flex: 1;
background: #FF6961;
height: 200px;
margin-bottom: 20px;
}
.container>div:nth-child(even) {
background: #006961;
}
<div class="container">
<div>
Content
</div>
<div>
Content
</div>
</div>
<div class="container">
<div>
Content takes up the whole width when other divs are hidden.
</div>
<div style="display: none">
Content
</div>
</div>
<div class="container">
<div>
Content takes up the whole width when there is no other div.
</div>
</div>
Read this guide to flexbox
Read more about flexbox on the MDN
#2 – Using display: table
Compatibility: IE8+ and all modern browsers
The Basic Markup
<div class="container">
<div>
First Column
</div>
<div>
This second column can be hidden or not exist and the first column will take up its space
</div>
</div>
The CSS
The container is given display: table
The containers children are given display: table-cell and will act the same as cells in an HTML table. If a cell is hidden or is removed the other cell will take its space.
.container{
display: table;
width: 600px;
margin: 20px;
}
.container>div {
display: table-cell;
height: 200px;
background: #FF6961;
}
.container>div:nth-child(even) {
background: #006961;
}
<div class="container">
<div>
Content
</div>
<div>
Content
</div>
</div>
<div class="container">
<div>
Content takes up the whole width when other divs are hidden.
</div>
<div style="display: none">
Content
</div>
</div>
<div class="container">
<div>
Content takes up the whole width when there is no other div.
</div>
</div>
<div class="container">
<div>
Content takes up the remaining width if a cell has a fixed width.
</div>
<div style="width: 200px">
Content
</div>
</div>
Read more about CSS tables on the MDN