How to make an inline-block element fill the remainder of the line?
Asked Answered
I

10

211

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.

Ichthyosaur answered 4/4, 2011 at 15:9 Comment(14)
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 <table>, unless somebody manages to come up with a genius simple idea that works withoutNostology
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.Ichthyosaur
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.Ichthyosaur
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 css-tricks.com/containers-dont-clear-floats for a good discussion of why you don't get auto-clearing.Deckert
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?Steapsin
css3 to the rescue! the new flexible box layout module will help, see my answer here for an example: #4434431. unfortunately, as always, this is not possible in internet explorer.Prolific
@thirtydot: Let's see it. I'd love to analyze your solution.Ichthyosaur
@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.Ichthyosaur
@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.Ichthyosaur
@Triynko: This is what I made earlier: 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.Steapsin
@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: #3568762Ichthyosaur
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.Ichthyosaur
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.Ichthyosaur
You mentioned white-space, well this does work with a table.Inverson
S
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?

Steapsin answered 7/4, 2011 at 20:57 Comment(7)
Can you get this to work if the second div element is instead an input text field ?Cockatiel
@tribalvibes: Like this? Or maybe this?Steapsin
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.Ichthyosaur
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).Ladonna
sadly this doesnt work if you revert left and right :(Stricken
This causes the items in the right column to render partially outside the visible area. The display:table solution below gives more correct behavior.Rhianna
@RMorrisey: Probably just needs some box-sizing: border-box on the divs. 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. 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.Steapsin
F
75

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/

Fumble answered 29/5, 2015 at 10:12 Comment(5)
display flex and width 100% .. gotta rememberHead
when using flex, why not use flex: 1 instead of width: 100% ?Quinquennium
For those new to flexbox: flex: 1 is shorthand for flex-grow: 1. It's a combination attribute: flex: <grow> <shrink> <basis>.Oversubtle
Just a quick note that display: flex is unsupported in IE < 11, and very buggy in 11.Lussi
@EricShields this shouldn't prevent anyone from using Flexbox. Nowadays we have flexbugs you know.Yapon
F
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>
Frasquito answered 26/11, 2013 at 16:23 Comment(4)
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.Centipede
This doesn't answer how to make inline-block fill the remainder of the line.Yapon
@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.Frasquito
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).Complementary
R
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/

Rachitis answered 10/8, 2018 at 12:32 Comment(0)
B
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.

Brassbound answered 26/5, 2019 at 18:40 Comment(0)
G
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/

Gayla answered 26/9, 2014 at 19:15 Comment(0)
T
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)

Thurmond answered 28/2, 2018 at 11:53 Comment(0)
B
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>
Bernadette answered 3/5, 2022 at 1:30 Comment(0)
B
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>
Blocking answered 2/11, 2022 at 11:5 Comment(0)
S
0

Wow, no decent answer with flex. Here it goes

<div class="flex">
  <span>first</span>
  <span>rest</span>
</div>
<style>
.flex {display: flex;}
.flex > :nth-child(1) {flex: 0; background: lightblue;}
.flex > :nth-child(2) {flex: 1; background: lightgreen;}
</style>

The key is in flex: 0 and flex: 1 which is short for flex-grow, which, when 1, takes the remainder space. There is more to it than just 0 or 1, but for the purposes of this answer that is all you need to know.

Example here.

Swami answered 23/4 at 0:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.