21

Given markup like this:

<div class="a">A</div>
<div class="b">B</div>
<div class="a">A</div>
<div class="b">B</div>
<div class="a">A</div>
<div class="b">B</div>

Is it possible to style this document to look like this:

|-------|---------|
|       |         |
|   A   |    B    |
|       |         |
|-------|---------|
|       |         |
|   A   |    B    |
|       |         |
|-------|---------|
|       |         |
|   A   |    B    |
|       |         |
|-------|---------|

(and if the content in A or B is longer, its neighbor will grow to match its height)…

without any additional markup?

I understand that giving .a and .b a display value of table-cell would just make this one big row.

What’s the solution?

Alan H.
  • 16,219
  • 17
  • 80
  • 113
  • Sure. Markup + naïve css: http://jsfiddle.net/alanhogan/9VLgN/ – Alan H. Jun 04 '12 at 22:15
  • My bet is there is no solution – Mikael Jun 04 '12 at 22:20
  • it's not going to work without additional markup, you will need a wrapper or two in order float the elements how you want them – Keith Jun 04 '12 at 22:21
  • Looks like CSS Grid Layout might or might not work, but that only exists in the as-yet-unfinished IE10. – Alan H. Jun 04 '12 at 22:30
  • I’m not surprised this doesn’t seem to be possible, but I am certainly sad that CSS can’t do something that is fundamentally this simple yet. – Alan H. Jun 04 '12 at 22:39
  • One last thought: The reason I want to do this without extra markup is twofold. One, the wrappers would have no semantic meaning. And two, I am building a responsive site; perhaps at some sizes, A and B would be on succeeding rows; sometimes, one A/B pair per row; and sometimes, more than one A/B pair. – Alan H. Jun 04 '12 at 22:40
  • 2
    @Alan, as you write about *pairs*, it seems that the data actually is logically structured into pairs, so extra markup would be adequate. And using markup that groups the data into pairs, you could make the pairs inline block and let browsers handle the situation, without using table properties in CSS. – Jukka K. Korpela Jun 05 '12 at 03:44
  • An excellent comment, @JukkaK.Korpela. – Alan H. Jun 05 '12 at 08:44
  • @JukkaK.Korpela If you submitted your comment as an answer, I think I would accept it as it is the most insightful and direct answer here. (Everyone else’s input has also been accurate, kind, and helpful, as well!) – Alan H. Jun 05 '12 at 17:17

8 Answers8

4

Not without flexbox, which hasn’t landed in several major browsers yet, seems to be the consensus.

Alan H.
  • 16,219
  • 17
  • 80
  • 113
3

No, I think it's not possible "without any additional markup". It needs:

  • div wrapper with display: table-row; containing A anb B "cells"
  • JavaScript with listeners on divs, which will determine max of A anb B's heights in each pair and set it to smaller one

Solution for second one:

CSS:

.a, .b {
    padding: 0.5em;
    float: left;
}
.a:nth-child(n+1) {
    clear: both;
}

jQuery:

$(function() {
    var max_width_a = 0, max_width_b = 0;
    $("div.a").each(function() {
        var elem_a = $(this),
            elem_b = elem_a.next("div.b"),
            height_a = elem_a.height(),
            height_b = elem_b.height(),
            pair = [elem_a, elem_b];

        max_width_a = Math.max(max_width_a, elem_a.width());    
        max_width_b = Math.max(max_width_b, elem_b.width());  
        $(pair).height(Math.max(height_a, height_b));
    }).width(max_width_a);
    $("div.b").width(max_width_b);
});

I've updated your Fiddle. Works on document ready, you have to customize it if you want to determine dynamic height changes.

Let me know if I have to explain how it works. Of course you can coerce div.a and div.b's width in CSS and don't check for max width with jQuery (then you will have to determine only max height in each pair).

Wirone
  • 3,304
  • 1
  • 29
  • 48
  • Thank you. I would rather not use jQuery or any JS for this, though. It’s really not something that should require JS. CSS is better suited for it, as browsers know when to redraw things, and JS doesn’t know when it’s needed to recalculate things. (E.g.: The contents of one of those divs change. Now this function needs called.) – Alan H. Jun 05 '12 at 08:46
  • Of course you're right, it should be doable with pure CSS, but when it's not, I wrote script for it. I've been testing CSS solutions, but couldn't achieve what you want. – Wirone Jun 05 '12 at 08:58
1

no, this is not possible without giving each row a wrapper.

here's a pure css solution with a single containing wrapper (per row)

.row { 
    background-color: #ccc;
    float: left;
    margin: 10px 0;
    padding: 1em;
    width: 50%;
    }
.a {
    float: left;
    width: 45%;
    }
.b {
    float: right;
    width: 45%;
    }

and the html

<div class="row">
    <div class="a">Mofo break yo neck, yaonna chung fizzle, bizzle dizzle dang dolor boom shackalack augue. Fizzle izzle away sit its fo riz</div>
    <div class="b">Lorizzle hizzle dolizzle sit amet, brizzle adipiscing elizzle. Nullam i saw beyonces tizzles and my pizzle went crizzle shizzlin dizzle, fo shizzle mah nizzle fo rizzle, mah home g-dizzle sizzle, suscipit quizzle, i saw beyonces tizzles and my pizzle went crizzle vizzle, arcu. Break yo neck, yall shiznit tortor. Sed eros. Ma nizzle izzle you son of a bizzle dapibizzle turpishizzle. Integizzle check out this velit sed fo.</div>
</div>
<div class="row">
    <div class="a">Sizzle ullamcorpizzle. Shut the shizzle up sagittizzle dizzle a shiznit. Vestibulizzle you son of a bizzle ipsizzle primis izzle faucibus orci pot et ultrices posuere dope Fo shizzle; Crazy vestibulizzle. Fo shizzle my nizzle habitant morbi tristique senectus ma nizzle netizzle izzle tellivizzle famizzle turpis hizzle. Donizzle tempor hendrerit hizzle. Aliquizzle erat volutpat. </div>
    <div class="b">Vivamizzle sagittis.rttitizzle izzle, boom shackalack hizzle, orci. Integizzle hizzle funky fresh lectus. </div>
</div>
<div class="row">
    <div class="a">Donizzle tempor hendrerit hizzle. Aliquizzle erat volutpat. Things crunk fizzle, scelerisque bizzle, daahng dawg the bizzle, crackalackin shiznit, arcu. Daahng dawg elizzle. Crazy fermentizzle, est izzlot purizzle , bibendizzle sizzle amizzle, fo shizzle vehicula, fo shizzle my nizzle malesuada, shizznit. Aenizzle fizzle ipsizzle izzle est ullamcorpizzle tincidunt. Cool quizzle. Mauris ligula urna, tempizzle shut th</div>
    <div class="b">Mofo break yo neck, yall fo shizzle izzle leo bibendum crackalackin. Vivamizzle shit tortor vizzle away. Brizzle malesuada fo magna. Dang commodo, nisl nizzle go to hizzle egestizzle, magna dolor w</div>
</div>

if you want a and b to have different background colors you will have to use height:100%

kristina childs
  • 2,190
  • 1
  • 20
  • 19
  • With `float: left;` you will not get same height for each pair `.a` and `.b` since those element would have each own height based on content. `height:100%` possibly would fix it, but Alan wants `.a`s to be same width like in `table`, see his ascii-artworked-example ;) He should then coerce `width: ...;` but he didn't say he want it to be fixed. Also, with floating `div`s wrapper should have `height: auto; overflow: auto;`. Wrapper should have `display: table-row;` and child `div`s must be `display: table-cell;` and this should work, but he don't want additional markup... – Wirone Jun 05 '12 at 09:01
  • if you take the code and put it into an html doc you'll see that this gives the illusion of a table. also, the question was if it is possible without any additional markup and the answer is no. that said, this is the way to do it with as little extra markup as possible. – kristina childs Jun 05 '12 at 16:30
  • of course floats don't dictate height. that's why you need to put .a and .b in a wrapper. by doing so, the largest content in each cell will push the entire wrapper down, making the next row in the grid align correctly. this works perfectly if the background color of each row is the same (why there is a #ccc so it's easily seen), however if the background colors of .a and .b are to be different, there would need to be 100% height added to one or more classes. you don't need display: table-row or table-cell to do this, though – kristina childs Jun 05 '12 at 16:30
  • display: block wouldn't be a bad idea depending on how you want to style it. like i said, this is the best way to do it with as little markup possible. – kristina childs Jun 05 '12 at 16:30
  • You didn't understand my entire comment, I think. In your solution each `.a` and each `.b` must have `width: ...px;` because only then it will look like table. And you don't know if fixed width is needed here. If not, `display: table-cell;` is the solution, because the elements will automatically fit to each other. `float: right;` for `.b` is unnecessary too, it can be `float: left;` like `.a`. If every child of the wrapper is floating, wrapper's calculated height would be 0px, that's I wrote `height: auto; overflow: auto;` for. Nevermind - it's not the solution for Alan. – Wirone Jun 05 '12 at 16:53
  • Thanks for the suggestion. That does work well. (But if you want A and B to be the same height, `height: 100%` doesn’t work – I don’t think floats can be given percentage heights. [Here’s a demo.](http://jsfiddle.net/alanhogan/P9tEV/1/)) – Alan H. Jun 05 '12 at 17:13
  • height: 100% is a tricky beast. because of it's nature, the browser needs to know the height of it's containing div... and the containing div before that... and before that. if done incorrectly you'll also end up with strange cut-offs of the page on window resizing and mobile devices. but the question is are you trying to get a div 100% of the page or divs equal to each other? if you want them equal, here is how you do it: http://kristinachilds.com/testes.html you have to use relative positioning and overflow:hidden to trick the browser into creating a window for your content – kristina childs Jun 05 '12 at 19:27
1

i put this in a comment, but should probably just make it an answer. here's how you do the same thing (using rows) forcing a 100% height as well

.row {
    background-color: #ccc;
    float: left;
    margin: 10px 0;
    padding: 1em;
    width: 50%;
}

#container_a {
    background-color: blue;
    float: left;
    position: relative;
    right: 50%;
    width: 100%;
}
#container_b {
    background-color: red;
    float: left;
    overflow: hidden;
    position: relative;
    right: 0;
    width: 100%;
}
#column_a {
    float: left;
    left: 50%;
    overflow: hidden;
    position: relative;
    width: 46%;
}
#column_b {
    float: left;
    left: 56%;
    overflow: hidden;
    position: relative;
    width: 46%;
}

and the html

<div class="row">
    <div id="container_b">
        <div id="container_a">
            <div id="column_a">Lorizzle hizzle dolizzle sit amet, brizzle adipiscing elizzle. Nullam i saw beyonces tizzles and my pizzle went crizzle shizzlin dizzle, fo shizzle mah nizzle fo rizzle, mah home g-dizzle sizzle, suscipit quizzle, i saw beyonces tizzles and my pizzle went crizzle vizzle, arcu. Break yo neck, yall shiznit tortor. Sed eros. Ma nizzle izzle you son of a bizzle dapibizzle turpishizzle. Integizzle check out this velit sed fo.</div>
            <div id="column_b">Vivamizzle sagittis.rttitizzle izzle, boom shackalack hizzle, orci. Integizzle hizzle funky fresh lectus. </div>
        </div>
    </div>
</div>
<div class="row">
    <div id="container_b">
        <div id="container_a">
            <div id="column_a">Lorizzle hizzle dolizzle sit amet, brizzle adipiscing elizzle. Nullam i saw beyonces tizzles and my pizzle went crizzle shizzlin dizzle, fo shizzle mah nizzle fo rizzle, mah home g-dizzle sizzle, suscipit quizzle, i saw beyonces tizzles and my pizzle went crizzle vizzle, arcu. Break yo neck, yall shiznit tortor. Sed eros. Ma nizzle izzle you son of a bizzle dapibizzle turpishizzle. Integizzle check out this velit sed fo.</div>
            <div id="column_b">Vivamizzle sagittis.rttitizzle izzle, boom shackalack hizzle, orci. Integizzle hizzle funky fresh lectus. </div>
        </div>
    </div>
</div>
<div class="row">
    <div id="container_b">
        <div id="container_a">
            <div id="column_a">Lorizzle hizzle dolizzle sit amet, brizzle adipiscing elizzle. Nullam i saw beyonces tizzles and my pizzle went crizzle shizzlin dizzle, fo shizzle mah nizzle fo rizzle, mah home g-dizzle sizzle, suscipit quizzle, i saw beyonces tizzles and my pizzle went crizzle vizzle, arcu. Break yo neck, yall shiznit tortor. Sed eros. Ma nizzle izzle you son of a bizzle dapibizzle turpishizzle. Integizzle check out this velit sed fo.</div>
            <div id="column_b">Vivamizzle sagittis.rttitizzle izzle, boom shackalack hizzle, orci. Integizzle hizzle funky fresh lectus. </div>
        </div>
    </div>
</div>
kristina childs
  • 2,190
  • 1
  • 20
  • 19
  • [JSFiddle here](http://jsfiddle.net/alanhogan/v4ffB/). Okay, I’ll give you that some sort of row wrapper seems necessary – but *three* wrappers for each row? – Alan H. Jun 06 '12 at 06:03
  • if you want each containing div to be equal height WITHOUT any scripting, yes. i've gotten around this in the past by making a sectioned background image (say a 800x1px strip) that gives the illusion of background colors and borders. if you want a div that's 100% height of the whole window, all you need to make sure body has a 100% height in it as well as the divs within it. but this won't work for ROWS of divs, only columns 100% height of the entire browser window – kristina childs Jun 06 '12 at 16:23
  • like this: http://kristinachilds.com/example/testes2.html of course, this won't work for fluid layouts and is overall a pretty antiquated way of doing things – kristina childs Jun 06 '12 at 17:00
  • My original question mentions CSS `display: table-*` properties. Undoubtedly using them would mean you really only need one wrapper per row. (Of course, you lose IE7 compatibility without some kind of shim/polyfill.) – Alan H. Jun 06 '12 at 22:20
  • 1
    yeah, that's the point. table-cell isn't bulletproof. if you dont' need to support IE6 or 7 then table-cell is the easier way to go (with the single wrapper of course) – kristina childs Jun 14 '12 at 20:55
1

I've written a jQuery plugin that accomplishes this. It converts a float layout into a display:table layout and handles the rows for you. Cell widths are handled automatically without script (much faster). It's responsive too. It's called WRECKER!! Hahahaha!.

svachon.com/blog/wrecker-responsive-equal-height-columns-and-rows/

Steven Vachon
  • 3,814
  • 1
  • 30
  • 30
  • Nice documentation page. Love the how-it-works section; it answers what questions I’d have. – Alan H. Feb 04 '13 at 17:44
  • I liked this, and used it. I've added some functionality to your plugin: Users can set `excludeLastSingleItem` (default false) to exclude the last item from the wrecker table if there is an odd number of items. This allows for the last item to appear as if it's in the last row of the table but spanning multiple columns (using CSS). See https://github.com/stevenvachon/jquery.wrecker/pull/3 or https://github.com/avjaarsveld/jquery.wrecker I used it with a 2 column table where the last item is centred in something that looks like the last row. – avjaarsveld Dec 20 '16 at 15:50
1

You CAN do this without flexbox, but you'll need to use nth-child(). Have a look at this answer: https://stackoverflow.com/a/12589629/740836

Community
  • 1
  • 1
Nick Budden
  • 621
  • 9
  • 20
0

I can't think of a way without a row wrapper, but if you know the exact width of the outer div will never change, it is possible to achieve. My workaround for this is relying on the wrapper size to fit a and b on same row and then the next a and b will automatically be pushed so that it wraps around to the next row. However, if the width of the wrapper shrinks, a and b won't go on the same line.

<div id="wrapper">
  <div class="a">A</div>
  <div class="b">B</div>
  <div class="a">A</div>
  <div class="b">B</div>
</div>

<style>
#wrapper { width: 500px;}
.a { background-color:green; float:left; width:50%;}
 .b {background-color:red;float:left;width:50%;}
</style>

http://jsfiddle.net/Lf0arzkn/1/

eternal
  • 99
  • 1
  • 4
  • 13
0

Yes, it is possible with display: contents if you limit yourself to one child (like a text node or an element, but actually in CSS box terms) in the cell just before (or after if you adapt my solution accordingly) the break.

<style>
#d1::after {
    content: "";
    display: table-row;
}
</style>
<div style="display: table">
<div id="d1" style="display: contents">aaaaaaaaaa</div>
<div style="display: table-cell">bbbbbbbbb</div>
</div>
ByteEater
  • 885
  • 4
  • 13