ShreevatsaR
ShreevatsaR

Reputation: 39113

CSS: two columns

I can't figure out how to achieve the following layout with CSS (probably because I don't actually know CSS).

I have a bunch of divs like this:

<div class="right"> <p>1</p> </div>
<div class="left">  <p>2</p> </div>
<div class="left">  <p>3</p> </div>
<div class="left">  <p>4</p> </div>
<div class="right"> <p>5</p> </div>
<div class="right"> <p>6</p> </div>

(not the real contents)

Now I want the layout to look like two equal columns of divs, with the "right" ones on the right, and the "left" ones on the left, thus:

2 1
3 5
4 6

[Edit: In a previous version of this question I had textareas inside the divs, and the divs all had different names like "one" and "xyz".] I tried something like

div.right { width:50%; float:right; clear:right; }
div.left { width:50%; float:left; clear:left;}

but it doesn't quite work: It produces something like:

2 1
3 
4 5
  6

(without the "clear"s, it blithely produces

2 1
3 4
6 5

which is not what is wanted).

It is apparent that it can be made to work if the divs are ordered differently, but I'd like not to do that (because these divs are generated dynamically if the browser has Javascript, and I don't want to change the actual order that is displayed in the absence of Javascript, for semantic reasons). Is it still possible to achieve the layout I want?

[For what it's worth, I'm willing to have it not work on IE or older versions of other browsers, so if there is a solution that works only on standards-compliant browsers, that's okay :-)]

Upvotes: 3

Views: 3971

Answers (11)

dave mankoff
dave mankoff

Reputation: 17789

The following works for me in Firefox 3:

div {
  width: 198px;
  border: 1px solid black;
}
div.onediv, div.tendiv, div.xyzdiv { float: right; }
div.twodiv, div.abcdiv, div.pqrdiv { float: left; }
<div style="width: 400px;">
  <div class="onediv"><p>One name</p> <textarea id="one"></textarea></div>
  <div class="twodiv"><p>Two name</p> <textarea id="two"></textarea></div>
  <div class="tendiv"><p>Ten name</p> <textarea id="ten"></textarea></div>
  <div class="abcdiv"><p>Abc name</p> <textarea id="abc"></textarea></div>
  <div class="xyzdiv"><p>Xyz name</p> <textarea id="xyz"></textarea></div>
  <div class="pqrdiv"><p>Pqr name</p> <textarea id="pqr"></textarea></div>
</div>

Just a quick note: putting div inside your class name (i.e. abcdiv) is a little redundant and weird. Without going into a huge diatribe about proper class usage, I would leave it out.

You can actually simplify your code if you just say "col1" and "col2". That way you can even swap the columns later if you want:

div {
  width: 198px;
  border: 1px solid black;
}
div.col1 { float: right; }
div.col2 { float: left; }
<div style="width: 400px;">
  <div class="col1"><p>One name</p> <textarea id="one"></textarea></div>
  <div class="col2"><p>Two name</p> <textarea id="two"></textarea></div>
  <div class="col1"><p>Ten name</p> <textarea id="ten"></textarea></div>
  <div class="col2"><p>Abc name</p> <textarea id="abc"></textarea></div>
  <div class="col1"><p>Xyz name</p> <textarea id="xyz"></textarea></div>
  <div class="col2"><p>Pqr name</p> <textarea id="pqr"></textarea></div>
</div>

Upvotes: 3

Steve Perks
Steve Perks

Reputation: 5530

I felt guilty that the pure css method didn't work, but if you don't mind using JavaScript to get what you're after, then here's some jQuery that'll work (someone else will probably be able to clean this up for you if you don't like the bloat).

<style>
  #container { width: 500px; border: 1px grey solid; overflow: hidden; }
  #container .rightSide { width: 250px; float: right; }
  #container .left { width: 250px; background: red; padding: 20px 0; overflow: hidden; text-align: center; }
  #container .right { width: 250px; background: green; padding: 20px 0; overflow: hidden; text-align: center; }
</style>
<script type="text/javascript">
$(document).ready(function() {
  $('#container').prepend('<div class="rightSide"></div>');
  $('#container div.right').each(function() {
    var $rightContent = $(this).remove().html();
    $('.rightSide').append('<div class="right">' + $rightContent + '</div>');
  });
});
</script>
<div id="container" >
  <div class="right"> <p>1</p> </div>
  <div class="left">  <p>2</p> </div>
  <div class="left">  <p>3</p> </div>
  <div class="left">  <p>4</p> </div>
  <div class="right"> <p>5</p> </div>
  <div class="right"> <p>6</p> </div>
</div>

Upvotes: 0

Anders
Anders

Reputation: 12570

Check out the 960 Grid System. I have used this on projects in the past and I have to say it works pretty well. Not only that, it is easy to use and is consistent across browsers :D

Using this framework:

<html>
    <head>
        <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" />
        <link rel="stylesheet" type="text/css" media="all" href="css/text.css" />
        <link rel="stylesheet" type="text/css" media="all" href="css/960.css" />
     </head>
     <body>
     <div class="container_16">
         <div class="grid_1 alpha" style="text-align:center;">
            2
         </div>
         <div class="grid_1 omega" style="text-align:center;">
            1
         </div>
         <div class="clear"></div>
         <div class="grid_1 alpha" style="text-align:center;">
            4
         </div>
         <div class="grid_1 omega" style="text-align:center;">
            3
         </div>
         <div class="clear"></div>
         <div class="grid_1 alpha" style="text-align:center;">
            5
         </div>
         <div class="grid_1 omega" style="text-align:center;">
            4
         </div>
    </div>
</body>
</html>

alt text http://img40.imageshack.us/img40/6889/cd3cc920a04b80e06b8efef.png

The bar on top of the text is from my browser, not part of the CSS or layout.

Upvotes: 0

ilya n.
ilya n.

Reputation: 18826

Solution 1

If you can afford to duplicate HTML:

.left-column .right, .right-column .left {
    display: none;
}

Solution 2

Wait a little until browsers support CSS3:

.right {
    position: flow(side); 
}

Solution 3

If your divs have the same height, use the indirect adjacent sibling combinator. Even though it belongs to CSS Level 3, it's already well-supported:

.container {
    position: relative;
}

.left, .right {
    position: absolute;
    top : 0;
    width: 50%;
} 

.left {
    left: 0;
}

.right {
    left: 50%;
}

.left ~ .left, .right ~ .right {
    top : 100px;
}

.left ~ .left ~ .left, .right ~ .right ~ .right {
    top : 200px;
}

.left ~ .left ~ .left ~ .left, .right ~ .right ~ .right ~ .right {
    top : 300px;
}

... /* you need to include as many rules as the maximum possible height */

Upvotes: 2

Steve Perks
Steve Perks

Reputation: 5530

I'll leave the rest of this answer in place so that someone else can see what my initial thoughts were, but this approach actually fails and requires that there are always more floating elements than none floating.

I think this is your solution (not tested).

.left { width: 51%; float: left; }
.right { width: 49%; }

The 51% stops them from floating next to each other and then just let all the .right content wrap up around those floated blocks.

Thinking less often gives you more with HTML/css, and again - NO to using tables for layout.

===[edit]===

I ran a test on this and it does work with a couple of tweaks AND if you know the first item is floating left (or right to reverse the behavior).

<style>
  div div { text-align: center; padding: 20px 0; overflow: hidden; }
  .left { width: 251px; float: left; background: red; }
  .right { width: 249px; background: green; }
</style>
<div style="width: 500px;" >
  <div class="left"> <p>1</p> </div>
  <div class="right">  <p>2</p> </div>
  <div class="left">  <p>3</p> </div>
  <div class="left">  <p>4</p> </div>
  <div class="right"> <p>5</p> </div>
  <div class="right"> <p>6</p> </div>
</div>

Upvotes: 2

Nathan Long
Nathan Long

Reputation: 126122

You have Javascript - why not use it?

You said this:

These divs are generated dynamically if the browser has Javascript, and I don't want to change the actual order that is displayed in the absence of Javascript, for semantic reasons...

You're only displaying these <div>s if the user has Javascript - why not use Javascript to rearrange them? If you move #4 to be after #5, it looks fine with your current CSS.

So (with jQuery):

$("div:eq(3)").remove().insertAfter("div:eq(4)");

Upvotes: 1

Vilx-
Vilx-

Reputation: 107052

Perhaps you can go with absolute positioning? It depends of course on the rest of your page's structure, but often this is quite viable.

Upvotes: 0

buti-oxa
buti-oxa

Reputation: 11431

You try to separate your input stream into two independent streams, and I don't think CSS allows you to do it. Using left and right floats is a clever idea, but it will not always work. CSS spec says in 9.5.1 rule 5:

The outer top of a floating box may not be higher than the outer top of any block or floated box generated by an element earlier in the source document.

So, your floats can jump left, right and down, but not up.

You can achive everything with absolute positioning, but there is nothing automatic about it.

EDIT: I said in comments that I don't expect CSS3 to support a feature like that because it violates separation of content and layout. Later, I remembered that CSS3 plans to have footnotes. So, if you mark some of your divs with "float: note", the selected divs will flow into a separate, "footnote" area of the page. I guess you can place them on the right of the rest of the layout.

Upvotes: 2

Andy Webb
Andy Webb

Reputation: 1710

Tables are a perfectly acceptable solution for this IMHO.

The backlash against tables was directed towards using them for the entire site layout. But if you need to display your data in rows and columns...... that's what tables were made for :)

If you still are interested in a viable cross browser CSS solution I would look at using a framework. It will save you hours of time in trying to get your page to look right.

http://www.blueprintcss.org/ is an excellent framework IMHO.

Upvotes: 0

LeJeune
LeJeune

Reputation: 950

When I hit that kind of problem I usually go with Tables.

<table>
    <tr>
        <td>
            <p>One name</p> <textarea id="one"></textarea>
        </td>
       <td>
            <p>XYZ name</p> <textarea id="xyz"></textarea>
        </td>
    </tr>
   ....
 </table>

Alignement works perfectly with all browsers.

Upvotes: 1

user42092
user42092

Reputation:

If you get rid of the clears it works fine. It looks like there's some overlap in the middle for whatever reason (rounding error?).

Upvotes: 0

Related Questions