205

Is such a thing possible using CSS and two inline-block (or whatever) DIV tags instead of using a table?

The table version is this (borders added so you can see it):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head></head>
<body>
<table style="width:100%;">
<tr>
<td style="border:1px solid black;width:100px;height:10px;"></td>
<td style="border:1px solid black;height:10px;"></td>
</tr>
</table>
</body>
</html>

It produces a left column with a FIXED WIDTH (not a percentage width), and a right column that expands to fill THE REMAINING SPACE on the line. Sounds pretty simple, right? Furthermore, since nothing is "floated", the parent container's height properly expands to encompass the height of the content.

--BEGIN RANT--
I've seen the "clear fix" and "holy grail" implementations for multi-column layouts with fixed-width side column, and they suck and they're complicated. They reverse the order of elements, they use percentage widths, or they use floats, negative margins, and the relationship between the "left", "right", and "margin" attributes are complex. Furthermore, the layouts are sub-pixel sensitive so that adding even a single pixel of borders, padding, or margins will break the whole layout, and send entire columns wrapping to the next line. For example, rounding errors are a problem even if you try to do something simple, like put 4 elements on a line, with each one's width set to 25%.
--END RANT--

I've tried using "inline-block" and "white-space:nowrap;", but the problem is I just can't get the 2nd element to fill the remaining space on the line. Setting the width to something like "width:100%-(LeftColumWidth)px" will work in some cases, but performing a calculation in a width property is not really supported.

Triynko
  • 18,766
  • 21
  • 107
  • 173
  • 1
    I don't think there is a sane way to do this except turning this into a `display: table-*` construct which will work, but isn't really "more semantic" either (being a terrible case of `div` soup) and breaks IE6 compatibility. I personally would stick with the ``, unless somebody manages to come up with a genius simple idea that works without
    – Pekka Apr 04 '11 at 15:11
  • 53
    Yeah. I keep running into all these "avoid tables" arguments from the dawn of the CSS age, and they're worded to make you sound like an incompetent lazy moron if you still use tables for layouts. Fast forward a decade, and it's still an idealistic pipe-dream. The fact is, flow layout semantics SUCK for fixed-but-flexible layouts like user interfaces and forms. The truth is that smart people will use tables where convenient, because they've exhausted every possible CSS solution and realized that they're all imperfect and significantly more complex than just using a table. – Triynko Apr 04 '11 at 15:23
  • 4
    Floats? Show me working code, where end-of-line elements don't line-wrap unpredictably and borders and margins don't break the layout. That's what's wrong with them. Also, does the automatically-sized parent container properly expand to encompass floating elements with out the "clear fix" hacks? I didn't think so. – Triynko Apr 04 '11 at 15:24
  • If you've got at least one non-floated element in your parent container, then it's not really a "hack" to clear floats, now is it? Remember that CSS has its roots in printing - see http://css-tricks.com/containers-dont-clear-floats/ for a good discussion of _why_ you don't get auto-clearing. – Chowlett Apr 04 '11 at 15:48
  • I dislike the amount of whine in your question. I made something that looks exactly like your demo (without using any kind of `table`) somewhat easily, but I'm still unclear what you actually want; do you just want a version of that *exact* `table` code with no `table`, or are you after something more? What goes inside the cells? Do they have to maintain equal height? – thirtydot Apr 04 '11 at 15:48
  • css3 to the rescue! the new flexible box layout module will help, see my answer here for an example: http://stackoverflow.com/questions/4433931/floating-second-element-right-w-o-specifying-width-of-first/4449776#4449776. unfortunately, as always, this is not possible in internet explorer. – schellmax Apr 04 '11 at 16:03
  • @thirtydot: Let's see it. I'd love to analyze your solution. – Triynko Apr 04 '11 at 17:39
  • @Chowlett. There are no non-floating elements in my container. There are exactly TWO DIVs. A left column, and a right column. I want the left column to have a fixed-width, and the right column to expand to fill the rest of the line width. Additionally, I want the parent container's height to expand to encompass the two columns, so they don't overflow. Floating elements normally do not have a size as far as the parent container is concerned so they overflow, unless parent "overflow" is something other than visible, and that will probably have to be "hidden" so scroll bars don't show up. – Triynko Apr 04 '11 at 17:45
  • @thirtydot: To be clear. One container DIV with whatever width (fixed/percent/don't care as long as it has some width). Two child DIVs forming side-by-side columns. Left DIV is FIXED PIXEL WIDTH, variable height. Right DIV is also variable height, but its width expands to fill the remaining width of the container. Columns WILL NOT be the same size, and should expand (height-wise) to fit whatever I want to put in them. Container DIV should expand height-wise to fit (the larger of) the columns. BTW, the whining is necessary to describe solutions that are insufficient and unstable. – Triynko Apr 04 '11 at 17:52
  • 3
    @Triynko: This is what I made earlier: http://jsfiddle.net/thirtydot/qx32C/ - I think it hits most of your points. I'll hear your critique of that demo I did, and try to fix it afterwards. – thirtydot Apr 04 '11 at 21:28
  • @thirtydot: Thanks, that is the solution as far as I can see. It's simple, it works, it meets all requirements. If you post that as the answer, I'll mark it as such. See also the same solution here: http://stackoverflow.com/questions/3568262/css-columns-fixed-width-and-remainder – Triynko Apr 07 '11 at 14:51
  • Damn. If you add overflow:hidden or overflow:auto to the right column, it reacts differently in Safari and Firefox. Safari seems to mirror the left margin on the right, causing the right column to appear centered and squished so the right margin is the same as the left. Firefox doesn't do this, and leaves the right column the same size. Also, when an overflow is set for the right column, it responds to, for example, a right-margin on the floating left column, whereas without setting the overflow such a margin on the left column has no effect on the right column. The inconsistency SUCKS. – Triynko Apr 12 '11 at 12:45
  • And without overflow:hidden, in Safari, some of the content in my right column is forced under the left one... but not all of it! Just the second or third element, for no apparent reason. This does not happen on Firefox. – Triynko Apr 12 '11 at 13:10
  • You mentioned white-space, well this does work with a table. – Carson Reinke Nov 04 '13 at 18:58

9 Answers9

176

See: http://jsfiddle.net/qx32C/36/

.lineContainer {
    overflow: hidden; /* clear the float */
    border: 1px solid #000
}
.lineContainer div {
    height: 20px
} 
.left {
    width: 100px;
    float: left;
    border-right: 1px solid #000
}
.right {
    overflow: hidden;
    background: #ccc
}
<div class="lineContainer">
    <div class="left">left</div>
    <div class="right">right</div>
</div>

Why did I replace margin-left: 100px with overflow: hidden on .right?

TylerH
  • 20,799
  • 66
  • 75
  • 101
thirtydot
  • 224,678
  • 48
  • 389
  • 349
  • Can you get this to work if the second div element is instead an input text field ? – tribalvibes Jun 09 '12 at 22:18
  • @tribalvibes: Like [this](http://stackoverflow.com/questions/6938900/css-input-take-remaining-space/6938990#6938990)? Or maybe [this](http://stackoverflow.com/questions/7189608/how-do-i-make-an-input-element-occupy-all-remaining-horizontal-space/7190310#7190310)? – thirtydot Jun 10 '12 at 00:35
  • 18
    Overflow hidden is not a solution. Suppose you don't want the overflow of the right container hidden. This doesn't make the size of the right container fill the remaining space on the line. This is an example of a two-year-old question that I still haven't marked an answer for, because there's still no satisfactory answer. – Triynko Mar 02 '13 at 01:42
  • 3
    Triynko: even though you're using 'overflow:hidden', nothing will generally be hidden (at least if you just have text in there). The text/elements inside the div will be arranged so that they fit inside the div (unless you have an element that is larger than the div, of course). – CpnCrunch Jan 16 '15 at 04:07
  • sadly this doesnt work if you revert left and right :( – Srneczek Sep 23 '16 at 19:30
  • This causes the items in the right column to render partially outside the visible area. The display:table solution below gives more correct behavior. – RMorrisey Aug 25 '17 at 19:07
  • 1
    @RMorrisey: Probably just needs some `box-sizing: border-box` on the `div`s. Just a guess, since you didn't provide a demo showing the behaviour you describe. That being said, [the `display: table`-based solution is usually better](https://stackoverflow.com/questions/6938900/how-can-i-put-an-input-element-on-the-same-line-as-its-label/6938990#6938990). This is a very old question, but I think I was trying to avoid anything to do with tables in this question due to behaviour of OP. – thirtydot Aug 25 '17 at 22:13
73

A modern solution using flexbox:

.container {
    display: flex;
}
.container > div {
    border: 1px solid black;
    height: 10px;
}

.left {
   width: 100px;
}

.right {
    width: 100%;
    background-color:#ddd;
}
<div class="container">
  <div class="left"></div>
  <div class="right"></div>
</div>

http://jsfiddle.net/m5Xz2/100/

Panu Horsmalahti
  • 1,117
  • 9
  • 6
48

Compatible with common modern browers (IE 8+): http://jsfiddle.net/m5Xz2/3/

.lineContainer {
    display:table;
    border-collapse:collapse;
    width:100%;
}
.lineContainer div {
    display:table-cell;
    border:1px solid black;
    height:10px;
}
.left {
    width:100px;
}
 <div class="lineContainer">
    <div class="left">left</div>
    <div class="right">right</div>
</div>
Maxime Pacary
  • 22,336
  • 11
  • 85
  • 113
  • 11
    The argument against using tables has nothing to do with its presentation characteristics. It has to do with unmanageable markup, style/document muddling, and improper semantics. None of these arguments applies to `display:table`. – Rich Remer Mar 14 '17 at 05:04
  • 2
    This doesn't answer how to make `inline-block` fill the remainder of the line. – Neurotransmitter Jun 01 '18 at 11:32
  • 2
    @TranslucentCloud I agree that my answer does not exactly answers the question title, but it provides a way to fill the available width using divs, as asked in the question body. – Maxime Pacary Jun 01 '18 at 13:24
  • 1
    I like this solution a lot. You're not force to use some weird stylings which araise from hidden CSS logic (like for overflow hidden). – Chaoste Dec 12 '18 at 10:13
7

You can use calc (100% - 100px) on the fluid element, along with display:inline-block for both elements.

Be aware that there should not be any space between the tags, otherwise you will have to consider that space in your calc too.

.left{
    display:inline-block;
    width:100px;
}
.right{
    display:inline-block;
    width:calc(100% - 100px);
}


<div class=“left”></div><div class=“right”></div>

Quick example: http://jsfiddle.net/dw689mt4/1/

SuperIRis
  • 423
  • 5
  • 9
3

I've used flex-grow property to achieve this goal. You'll have to set display: flex for parent container, then you need to set flex-grow: 1 for the block you want to fill remaining space, or just flex: 1 as tanius mentioned in the comments.

0

If you can't use overflow: hidden (because you don't want overflow: hidden) or if you dislike CSS hacks/workarounds, you could use JavaScript instead. Note that it may not work as well because it's JavaScript.

var parent = document.getElementsByClassName("lineContainer")[0];
var left = document.getElementsByClassName("left")[0];
var right = document.getElementsByClassName("right")[0];
right.style.width = (parent.offsetWidth - left.offsetWidth) + "px";
window.onresize = function() {
  right.style.width = (parent.offsetWidth - left.offsetWidth) + "px";
}
.lineContainer {
  width: 100% border: 1px solid #000;
  font-size: 0px;
  /* You need to do this because inline block puts an invisible space between them and they won't fit on the same line */
}

.lineContainer div {
  height: 10px;
  display: inline-block;
}

.left {
  width: 100px;
  background: red
}

.right {
  background: blue
}
<div class="lineContainer">
  <div class="left"></div>
  <div class="right"></div>
</div>

http://jsfiddle.net/ys2eogxm/

Chris Forrence
  • 10,042
  • 11
  • 48
  • 64
Nick Manning
  • 2,828
  • 1
  • 29
  • 50
0

When you give up the inline blocks

.post-container {
    border: 5px solid #333;
    overflow:auto;
}
.post-thumb {
    float: left;
    display:block;
    background:#ccc;
    width:200px;
    height:200px;
}
.post-content{
    display:block;
    overflow:hidden;
}

http://jsfiddle.net/RXrvZ/3731/

(from CSS Float: Floating an image to the left of the text)

Fanky
  • 1,673
  • 1
  • 18
  • 20
0

If, like me, you want something that will expand to the end of the line even if the left-hand box wraps, then JavaScript is the only option.

I'd make use of the calc feature to get this right:

Array.from(document.querySelectorAll(".right")).forEach((el) => {
  el.style.width = `calc(100% - ${el.offsetLeft + 1}px)`;
});
.container {
    outline: 1px solid black;
}

.left {
   outline: 1px solid red;
}

.right {
    outline: 1px solid green;
}
<div class="container">
  <span class="left">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin tristique aliquet quam, at commodo lorem fringilla quis.</span>
  <input class="right" type="text" />
</div>
chowey
  • 9,138
  • 6
  • 54
  • 84
0

A solution using grid layout and fractional units (fr):

/* For debugging and visibility */
html, body {
    border: 2px  solid navy;
}
.grid-layout { 
    border: thick solid sandybrown;
    background-color: gray;
} 
.grid-layout div:nth-child(odd) {
    border: 2px solid brown;
    background-color: azure;
}
.grid-layout div:nth-child(even) {
    border: 2px solid red;
    background-color: lightyellow;
}

/* Grid layout.
 * Horizontal and vertical gaps.
 * two columns, fixed and responsive.
 * Note no containing div per line.
 */
.grid-layout {
    display: grid;
    gap: 4px 2px ;
    grid-template-columns: 100px 1fr;
}
<p>How to make an element fill the remainder of the line?</p>
<p>Note no encompassing div per line.</p>

<div class="grid-layout">
    <div>Lorem ipsum line 1</div>
    <div>Lorem ipsum dolor sit amet,
         consectetur adipiscing elit,
         sed do eiusmod tempor incididunt ut labore
         et dolore magna aliqua.</div>
    <div>Lorem ipsum line 2</div>
    <div>Ut enim ad minim veniam,
         quis nostrud exercitation ullamco laboris
         nisi ut aliquip ex ea commodo consequat.</div>
</div>

A similar solution with encompassing divs:

 .lineContainer {
   display: grid;
   gap: 2px 4px;
   grid-template-columns: 100px 1fr;
 }
<p>Display grid per line.</p>
<div class="lineContainer">
  <div style="border:1px solid black; ">
    Lorem ipsum &hellip;
  </div>
  <div style="border:1px solid black; ">
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit,
    sed do eiusmod tempor incididunt ut labore
    et dolore magna aliqua.
  </div>
</div>
clp
  • 1,098
  • 5
  • 11