tags:

views:

545

answers:

6

Hi,

I was working on creating a layout today which goes as follows:

+----+ +----+ +----+ +----+
|    | |    | |    | |    |
+----+ +----+ +----+ +----+

Looks simple, but I have some additional specs:

  1. The max number of columns (n) needs to be dynamic (This HTML page is generated at run-time).
  2. The entire set must be horizontally fluid (they compress as much as they can when window is narrowed.
  3. The entire set must also be centered on the screen with no more than n columns in any 1 row at a time.
  4. Ideally, the right-most "boxes" drop to the row below and re-arrange when necessary, i.e. when window is narrowed and n boxes do not fit on the screen.
  5. Of course, when there are more than n elements to display they just create additional rows of these.
  6. The individual boxes are bordered with rounded corners and clickable with whatever action.

With some research (google+adaptations to examples I found) I was able to do #6 like I wanted using divs and css. However, I had a really hard time doing 2-4. I got almost all the way there but I, for the life of me, could not get 2 and 3 to work. 2 was not that big a deal, but 3 was crucial. I was using floating divs for positioning and found an example site that mentioned using relative positioning and shift left etc to get it to center but it just did not work in my case (although the example works fine). In my case, instead of centering they would start at the center and go towards the right making it look comical so I don't know what I missed.

After struggling with this for a couple hours I did it with the following code in like 5 minutes:

<simplified code>
max_columns = 3; /* whatever */
html = "<table>";
for i = 1 to num_elements
  if i % max_columns eq 1 then
    html += "<tr>";
  endif
  html += "<td>";
  html += "<div>my element</div>"; /* This is a complex multi-leveled div */
                                   /* that makes boxes with rounded corners */
  html += "</td>";
  if i % max_columns eq 0 or i eq num_elements then
    html += "</tr>";
  endif
endfor
html += "</table>";
</simplified code>

This satisfies all my requirements except #4 which is a nice-to-have but not necessary.

Question: [How would you]/[Is there any way to] do it without using tables?

EDIT: I haven't had a chance to implement/test any of the potential solutions below, as I need to finish other aspects (in the next 5 days) before I come back to this problem. My current, lame (?) solution seems to work fine except for req #4, so this issue is not on my critical path right now. But I will test and post feedback on this soon-ish. So I'm going to keep this question open until then. I do appreciate the responses. Thanks.

EDIT #2: So I tried some of the solutions below and this is what I came up with:

<html>
    <head>
    <style>
  .box {
          width: 19%;
          height: 150px;
          border: 1px solid black;
          display: inline-block;
          margin: 3 0 0 0;
  }
  #center {
          text-align: center;
          border: 1px solid green;
          width: 80%;
          height: 85%;
          padding: 0 0 0 0;
          vertical-align: middle;
          margin: 0;
  }
  .footr{
          text-align: center;
          border: 1px solid green;
          width: 80%;
          height: 38px;
          padding: 0 0 0 0;
          vertical-align: middle;
          margin: 0;
  }
  </style>
    </head>
    <body style="margin-left:0; margin-top:0; margin-right:3;" >
    <div style="background-image: url(x); height: 60; width:131; float:left;"></div>
    <div style="background-image: url(x); background-repeat:repeat-x; height:60;"></div>
    <div class="clearer"></div>
    <center>
    <div id="center">
          <span class="box" style="float:left;">Blah</span>
          <span class="box">Blah</span>
          <span class="box">Blah</span>
          <span class="box">Blah</span>
          <span class="box" style="float:right;">Blah</span><br>
          <span class="box" style="float:left;">Blah</span>
          <span class="box">Blah</span>
          <span class="box">Blah</span>
          <span class="box">Blah</span>
          <span class="box" style="float:right;">Blah</span><br>
          <span class="box" style="float:left;">Blah</span>
          <span class="box">Blah</span>
          <span class="box">Blah</span>
    </div>
    <div class="footr">Footer</div>
    </center>
    </body
</html>

As you can probably tell, this solves almost all of my problems, except for 2/3 (not mentioned in my original Q:

  1. Vertical alignment of the whole block so its centered vertically
  2. On a horizontal row, I'd like the spans to stick to the outside edges of the outer div with the inner divs evenly spaced between them.
  3. On the row with less than n spans, I'd lie them to line up under the 1st 3 spans of the former row.

I'm doing this exercise solely for my personal "itch-i-just-have-to-scratch", and on my own time. For work, I already did most of this (sparingly) using tables to make stuff line up correctly.

P.S.: Please excuse some mal-formed HTML and the inline styles etc. Once I get a 100% working solution, I will clean it up.

A: 

Turn your div's into inline elements and give them a fixed width:

<html>
<head><title>Column test</title></head>
<style>
.box {
        width: 60px;
        height: 20px;
        border: 1px solid black;
        display: inline;
        margin: 3px;
}
.center {
        text-align: center;
        border: 1px solid green;
}
</style>
<body>
<div class="center">
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
</div>
</body>
</html>

This will flow them behind each other just like normal text. As soon as no more elements fit into the current line, they will start a new one.

You need the fixed width because the default width is 100%. It would be nice if it was possible to say "width to fit the contents" but that doesn't work since the content of the div needs the width of the div to layout itself (hen and egg problem).

Aaron Digulla
Interesting.. I'll kick myself if this works. I was in "float" and "clear" hell before I gave in to quick 'n easy.
Adnan
will the "inline" thing make them centered as well? Unfortunately, my VPN connection is down (its past midnight here in NZ) and i cant test this right now?
Adnan
Simplest way to center elements is to use <center></center> but I suspect it is frowned upon for not having semantic value.
Noel Walters
Yea, the <center> was already in place, but my divs for some reason refused to obey. I suspect nested divs to be the culprit there. I could have tried putting text-align:center on every "container" type div, but that just felt too nasty.
Adnan
Isn't a div with display:inline the same as a <span>?Furthermore, you could wrap the boxes in a container, and give that the text-align: center;
borisCallens
[hens](http://www.netstate.com/states/symb/birds/images/de_blue_hen_chicken.jpg) don't lay eggs ;)
borisCallens
@Adan: Unless I misunderstood what you want, my new code example works.
Aaron Digulla
@boris: Well ... mostly. I've seen browsers which will ignore "width" for span but not for div. So I prefer divs for this kind of trick.
Aaron Digulla
@Adnan: Tip: Get this to work in Firefox. After that, fix it for the other, broken browsers. This way, you know what works and what doesn't without wasting lots of time with browser bugs *in the beginning*.
Aaron Digulla
+1  A: 

Try if this fits you:

<html>
 <head>
  <title>box test</title>
  <link rel="stylesheet" href="style.css">
 </head>
 <body>
  <div id="boxContainer">
   <span class="box">1</span>
   <span class="box">2</span>
   <span class="box">3</span>
   <span class="box">4</span>
   <span class="box">5</span>
   <span class="box">6</span>
   <span class="box">7</span>
   <span class="box">8</span>
   <span class="box">9</span>
   <span class="box">a longer block</span>
   <span class="box">a longer multi <br/> lined<br/> block</span>
   <span class="box">12</span>
   <span class="box">13</span>
   <span class="box">14</span>
   <span class="box">15</span>
   <span class="box">16</span>
   <span class="box">17</span>
   <span class="box">18</span>
   <span class="box">19</span>
   <span class="box">20</span>   
  </div>
 </body>
</html>

#boxContainer{
 text-align: center;

 background-color: #D0DC16;
 border: 1px solid black;
 height: 100%;
 padding: 1em 0 1em 0;
 width: 100%;
}
.box{
 /*width: 24%;*/
 display: inline-block;

 padding: 2em;
 margin: 0.5em 0 0.5em 0;
 background-color: #DC6216;
 border: 1px solid black;
}

You would change the .box widht to (100/n)-1. I don't exactly know why the -1, but it probably has something to do with the margin and border not being counted towards width and the fact that a single pixel can't be devided into subpixels.

--EDIT--
Reviewed code after comments. Comment/decomment box width line for self-fitting / fixed number of boxes per line.
Added two irregular boxes.

borisCallens
This will always give him 4 boxes per row, regardless of the width of the browser window...
Traingamer
Hmm, maybe I misinterpreted req. nr 1?
borisCallens
If you leave out the width, the css will fit as many blocks as its content allows on one row.
borisCallens
The boxes need to have some kind of width, whether % or px or em or they'll default to 100% width
Traingamer
Only tested my code in Opera, but if I don't supply a width for the boxes, they shrinkwrap for the content..
borisCallens
Works fine in Safari as well.
Chuck
This almost works great.. I will post my test code with a few things I still haven't been able to work out. Thanks.
Adnan
+1  A: 

Question: [How would you]/[Is there any way to] do it without using tables?

Simply give them a fixed width and float them to the left. (If they do not have a set width this will not suffice).

Traingamer
How would you go about satisfying requirement nr 3?
borisCallens
I missed that one in my zeal to simply answer the dynamic, fluid, varying number requirements. :-(
Traingamer
I'm not sure this is possible, at least not without Javascript or proprietary CSS...
Ant P.
A: 

Well this is what I came up with. You may have to play with the widths to adjust for browser rounding errors/ white-space corrections but overall should work. Also IE may need some judicious use of overflow: hidden|auto.

<!DOCTYPE html>
<html><head><style type="text/css">

.container { width: 100%;text-align: center;}
.ib { min-width: 150px; display: -moz-inline-stack; display: inline-block; zoom: 1; *display: inline;}
.ib .styleMe { display: block; background: green; margin: 5px; text-align:left; border: 2px solid black; padding: 0 8px; }
.n1 .ib { width: 100%; } /* you could also use max-width here (if you can ignore IE6) and want jagged lines of items */
.n2 .ib { width: 50%; }
.n3 .ib { width: 33%; }
.n4 .ib { width: 25%; }
.n5 .ib { width: 20%; }
.n6 .ib { width: 16%; }
/*.nX .ib { max-width: 100/X; } may need to account for white-space/browser rounding errors too! */

</style></head>
<body>

<div class="container n4">
    <div class="ib"><div class="styleMe">
        <p>One</p>
        <p>Line 2</p>
    </div></div><!--
    kill whitespace (or adjusts widths above)
    --><div class="ib"><div class="styleMe">
        <p>Two</p>
        <p>Line 2</p>
    </div></div><!--
    kill whitespace (or adjusts widths above)
    --><div class="ib"><div class="styleMe">
        <p>Three</p>
        <p>Line 2</p>
    </div></div><!--
    kill whitespace (or adjusts widths above)
    --><div class="ib"><div class="styleMe">
        <p>Four</p>
        <p>Line 2</p>
    </div></div><!--
    kill whitespace (or adjusts widths above)
    --><div class="ib"><div class="styleMe">
        <p>Five</p>
        <p>Line 2</p>
    </div></div><!--
    kill whitespace (or adjusts widths above)
    --><div class="ib"><div class="styleMe">
        <p>Six</p>
        <p>Line 2</p>
    </div></div>
</div>
</body></html>
Josh
How do you determine which .n? class you're using in a resize?
Traingamer
Using the original question .n would come from max_columns (in his code it would be html = "<div class=""container n"+ max_columns +""">" everything else the CSS will handle. max_columns can be any value you need it to be aslong as it's defined in css
Josh
Note... if you use a fixed width instead of percentage width you don't really need the .nX classes. The display will adjust to best fit it's contianer adding/removing columns/rows as needed. This example uses a min-width (and could also use max-width) though that will change things for IE6 a bit!
Josh
Thanks, I will try this when I finish the rest of the page. Does look promising. I cannot ignore IE6 (although frustratingly, I _can_ ignore FF, etc. as my web app only supports IE), so I might have to play a bit with the widths as you suggest.
Adnan
For the widths above you can just change 50% to 49.9% and 25% to 24.9%; and it will work across the board for IE. The only thing in the above that won't work for IE6 is the min-width. You can get around that by just assign fixed widths in IE < 6 (make duplicate rules prefixed with "* html"
Josh
+1  A: 

You could try display:inline-block;. This would satisfy all conditions bar the expanding-contracting one.

The tutorial is here: display:inline-block; tutorial

The example usage is here: display:inline-block; example

Darko Z
Or here: http://stackoverflow.com/questions/618323/table-layout-equivalent-using-divs/618971#618971
borisCallens
+1  A: 

It's best if you use tables for tabular data. Be aware of the Divitis problem.

Update: If these "boxes" are links to somewhere else then simply implement them with anchors and style them appropriately. A technique called image replacement may help you. On how to lay out those links have a look at listamatic horizontal lists.

cherouvim
That was a great link. I'm not particularly fond of divs but I saw myself in some of those examples. Mostly due to HTML igno-bliss. Can't wait till my CSS/XHTML books arrive so I can read up on this shi-ite.
Adnan
Oh, by the way, in my original question, it is really not tabular data. They are some actually elaborate buttons (clickable divs, if you will) that need to be arranged on a home page of sorts. And my description is somewhat simplistic to not get bogged down by extraneous detail.
Adnan
@Adnan: see my update
cherouvim