Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fitting as many divs as possible horizontally in HTML, and filling the line width

Tags:

html

css

I have a bunch of fixed-width div elements styled to flow inline using inline-block display type. This leaves an empty space at the end of the line (where the next div could not be fitted and wrapped to the next line).

What I'd like to do, is to expand all of the divs on the row evenly to fill up the row, similar to the "Justify" alignment for the text.

In other words, I want to have a minimum width on div elements and fit as many of them as possible in a single row, and fill the entire row.

Here's my sample HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <style>
        #container { margin: 100px; padding: 10px; border: 1px solid blue; }
        .item { margin: 10px; width: 300px; min-width: 300px; display: inline-block; border: 1px solid red; }
    </style>
</head>
<body>
    <div id="container">
        <div class="item">Item One</div>
        <div class="item">Item Two</div>
        <div class="item">Item Three</div>
        <div class="item">Item Four</div>
    </div>
</body>
</html>

Is that possible with pure CSS + HTML? Or do I have to write a script to achieve this result?


UPDATE: Since people keep suggesting to use percentage widths to fit items in a single row, I have to note that it's NOT the intention of this question. I want to have something like "Justified" text, but with blocks. Number of items are variable, and can be too many.

The blocks need to be the same size, have a default (minimum) width that will cause them to wrap to next line if needed, and the container's width is needed to be filled with child items by extending their width.


UPDATE 2:

Current sample produces something like this:

|--------------------------- Container -----------------------------|
| |------ 1 ------| |------ 2 ------| |------ 3 ------|             |
| |------ 4 ------| |------ 5 ------| |------ 6 ------|             |
| |------ 7 ------|                                                 |

I'm looking to see something like this:

|---------------------------- Container ----------------------------|
| |-------- 1 --------| |-------- 2 --------| |-------- 3 --------| |
| |-------- 4 --------| |-------- 5 --------| |-------- 6 --------| |
| |-------- 7 --------|                                             |

Or this:

|---------------------------- Container ----------------------------|
| |-------- 1 --------| |-------- 2 --------| |-------- 3 --------| |
| |-------- 4 --------| |-------- 5 --------| |-------- 6 --------| |
| |------------------------------ 7 ------------------------------| |

Each item has a minimum size, so in the above example item #4 and #7 won't fit the remaining space on the line and would wrap to the next line. I want the ones already fitted to the line to fill up the space.

Note that the container may re-size because the browser can be re-sized. So, if it becomes smaller to the extent that only two items fit in a row, I'd want to see something like this:

|----------------- Container -----------------|
| |-------- 1 --------| |-------- 2 --------| |
| |-------- 3 --------| |-------- 4 --------| |
| |-------- 5 --------| |-------- 6 --------| |
| |------------------- 7 -------------------| |

I hope this clears the intent of the question.

like image 674
Iravanchi Avatar asked Apr 21 '13 09:04

Iravanchi


People also ask

How do I fill a div horizontally?

The width property is used to fill a div remaining horizontal space using CSS. By setting the width to 100% it takes the whole width available of its parent. Example 1: This example use width property to fill the horizontal space. It set width to 100% to fill it completely.

How do I make div width fit to content?

Using inline-block property: Use display: inline-block property to set a div size according to its content.

How do you put a horizontal space between two divs in HTML?

Add space between divs by using a margin, and it will still fit! Notice that we've added space by adding margin-right: 20px to just the first . flex-child element.

How do you make a full width in HTML?

If you set the width to 100% on the body element you will have a full page width. This is essentially equivalent to not setting a width value and allowing the default. If you want to use the body element as a smaller container and let the HTML element fill the page, you could set a max-width value on the body.


2 Answers

You can do this with the flexbox model, there are some issues though.

There is an old and a new flexbox model, which does make things kind of complicated. Currently only Chrome and Opera support the new model, and other browsers have "Partial support", which means that they might be supporting:

  1. An older version of the w3 specifications
  2. An older syntax
  3. Or they just haven't gotten around to fully implementing the new or even the old model yet

With whatever is available today I was able to put something together which works in Chrome 26, Safari 5.1.7, IE10, IE10 in IE9 mode and IE10 in IE8 mode. Sadly, my current Firefox version 20 is not in that list. I am not sure about if it will work in older versions of Firefox (I have added the required CSS in the jsFiddle near the bottom of the post, but I don't have any older versions installed on my PC).

This link explains why it does not work in Firefox 20. If you scroll down to the bottom of the page, it states about the flex-wrap property:

Firefox (Gecko) - Not Supported. Firefox supports only single-line flexbox.

I'm not 100% sure about this, but it seems to me that they have never supported the flex-wrap property even in older versions. Correct me if I'm wrong, to anyone with a Firefox 19- version out there, but I think if they had, Firefox 20 would at least fall back on it for as long as they have not yet fully implemented the new flexbox model.

This flex-wrap property is the one that makes the multi-line magic happen in most of the other browsers. The forecast is that Firefox will be fully supporting the new flexbox model in version 22, which is scheduled to be released on June 25, 2013. UPDATE: Firefox now does support the flex-wrap property from version 28 upwards. /UPDATE

After all that, here's a jsFiddle which does the job as well as possible, according to my recent research. This seems to be the best you can do in pure CSS + HTML with the current browser support.

If you want something better right now, you would have to come up with a JS solution. Maybe have a look at the source of flexieJS and modify the browser detection code so that it will handle Firefox 20 as well. box-pack and box-orient are on the list of supported properties of FlexieJS, they are the properties that are making this work on older iOS and Safari.

Edit: As @Cimmanon points out in the comments, to get this to work in older browsers they would need to support box-lines: multiple, which they don't. So this isn't going to work on older versions of iOS, Safari and Firefox. I guess this explains why Firefox 20 does not have an old implementation to fall back on. Since FlexieJS does not have support for box-lines: multiple either, its source is probably not going to be of much help with trying to fix this issue. Writing your own JS fix seems to be the only right now solution. The developer of FlexieJS has been working on a polyfill for the new specification. This would have to include some code to fix the multiline in older browsers. It does not look like there is any at the moment though. If you're going to write something, maybe you could contact him for some insights. Pass him your code if you get it working. If you are lucky he has got something going already which isn't up on GitHub yet.

The CSS:

#container {
  margin: 100px;
  padding: 10px;
  border: 1px solid blue;
  display: -webkit-box;     /* iOS 6-, Safari 3.1-6 */
  display: -moz-box;        /* Firefox 19- */
  display: -ms-flexbox;     /* IE 10 */
  display: -webkit-flex;    /* Chrome */
  display: flex;            /* Opera 12.1, Firefox 20+ */

  /* iOS 6-, Safari 3.1-6 */
  -webkit-box-orient: horizontal;
  -webkit-box-pack: justify;
  -webkit-lines: multiple;  /* Only here for informative purpose, this line is what should have made it work, it has never been implemented */ 

  /* Firefox 19- */
  -moz-flex-flow: row wrap;
  -moz-justify-content: space-between;
  -moz-box-lines: multiple; /* Only here for informative purpose, this line is what should have made it work, it has never been implemented */ 

  /* Chrome */
  -webkit-flex-flow: row wrap;
  -webkit-justify-content: space-between;

  /* IE10 */
  -ms-flex-flow: row wrap;
  -ms-justify-content: space-between;

  /* Opera 12.1, Firefox 20+ */
  flex-flow: row wrap;
  justify-content: space-between;
}
.item {
  margin: 10px;
  width: 300px;
  border: 1px solid red;
  -webkit-box-flex: auto;    /* iOS 6-, Safari 3.1-6 */
  -moz-box-flex: 1.0;        /* Firefox 19- */
  -webkit-flex: auto;        /* Chrome */
  -ms-flex: auto;            /* IE10 */
  flex: auto;                /* Opera 12.1, Firefox 20+ */
}
like image 199
tom-19 Avatar answered Nov 09 '22 07:11

tom-19


You can do this two ways (if you know how many .items there are):

Example one: Floating the .items works, see fiddle http://jsfiddle.net/David_Knowles/wh5bP/

#container { 
    margin: 100px; 
    overflow:hidden;
    border: 1px solid blue; 
}
.item { 
    width: 23%; /* important: (100% / numberOfItems - margin%) */
    margin: 0 1%; /* important */
    float:left; /* important */
    -moz-box-sizing: border-box; /* only needed to compensate for the border used in your debugging */
    -webkit-box-sizing: border-box; 
    box-sizing: border-box;
    border: 1px solid red; 

}

Example Two:

Inline-block elements are sensitive to the white space between the line breaks. Notice no spaces between the .items. http://jsfiddle.net/David_Knowles/wh5bP/1/

<div id="container">
    <div class="item">Item One</div><div class="item">Item Two</div><div class="item">Item Three</div><div class="item">Item Four</div>
</div>

.item { 
    width: 23%;
    margin: 0 1%;
    display:inline-block;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box; 
    box-sizing: border-box;
    border: 1px solid red; 

}

If you never know how many .items there will be then you could take a look the flexbox model. http://css-tricks.com/using-flexbox/ http://caniuse.com/flexbox

If your target audience use IE9 or less then you will need to also use a polyfill. http://flexiejs.com/

like image 27
Timidfriendly Avatar answered Nov 09 '22 06:11

Timidfriendly