153

Short Story

Let's say my HTML is already set in stone:

<div id="blockA">Block A</div>
<div id="blockB">Block B</div>
<div id="blockC">Block C</div>

It will look like this:

------------
| Block A  |
------------
| Block B  |
------------
| Block C  |
------------

Now I want to switch the order of the blocks. How can I do that with only CSS?

------------
| Block C  |
------------
| Block A  |
------------
| Block B  |
------------

I'm aware there's hacky solutions such as using position:absolute, but this doesn't preserve the effective use of the display:block property. That is, blocks push other blocks downward when they grow in size.

Long Story

When user uses a computer to view my webpage, the blocks are displayed in this order:

  1. General info.
  2. Event schedule.
  3. iPhone app advertisement

The iPhone app advertisement is placed last because it's not terribly important to computer users. A small percentage of computer users will whip out their phone and install the app.

If a mobile user comes to this site, the iPhone app advertisement should be the most important thing on the page. Therefore, it should be moved to the top:

  1. iPhone app advertisement
  2. General info.
  3. Event schedule.

I would like iPhone and computer users to share the same HTML, but have a CSS media query switch the order of the blocks.

@media only screen and (max-device-width: 480px) {
   #blockC {
      /* magic order switching */
   }
}
Community
  • 1
  • 1
JoJo
  • 19,587
  • 34
  • 106
  • 162
  • Why would you need to do this with *only* CSS, if you mind me asking? – Blender Sep 15 '11 at 03:53
  • 1
    `position: absolute` doesn't change `display: block`. – alex Sep 15 '11 at 03:53
  • Is there a reason you want to do this in CSS and not JavaScript? Are you worried about users with JavaScript turned off? – Ray Toal Sep 15 '11 at 03:54
  • 2
    `position:absolute` doesn't preserve the stacking and pushing nature of `display:block`, which is the whole point of having a block element. – JoJo Sep 15 '11 at 04:30
  • It's not possible through efficient CSS, though some jQuery could do the magic, I could show you if you want. – Starx Sep 15 '11 at 04:48
  • 13
    I just like to have distinct roles for HTML, CSS, and JS. I like HTML to only deal with **information**, CSS to only deal with **appearance**, and JS to only deal with user **interaction**. I'm OCD about model-view-controller. If I have to change my HTML to change the appearance, then a little piece of my heart dies. – JoJo Sep 15 '11 at 04:48
  • 2
    I don't think it's unreasonable to want to do this through CSS - I would rather put it behind a media query and let that be the single source of truth for how the site's layout will respond than splitting that logic across css and js. – Nick Heiner Dec 07 '14 at 23:20
  • Anyone who wants to achieve similar effect with Bootstrap3 columns, [here is one tutorial](https://web3canvas.com/re-order-columns-in-mobile-devices-using-bootstrap-3/). – matiss Feb 02 '19 at 10:41
  • If you just use css, you can use flex: `div { display: flex; align-items: center; justify-content: center; flex-direction: row-reverse; //revert horizontally //flex-direction: column-reverse; revert vertically }` – al-bulat Dec 18 '19 at 09:26
  • Bruh your short story is a long as your long story XD – Shreemaan Abhishek Jun 17 '21 at 16:29

11 Answers11

148

Here is a "simple as possible" example, for changing the order of div-elements (when resizing the browser window):

<!DOCTYPE html>
<html>
  <head>
    <title>foobar</title>
    <style>
      @media screen and (max-width:300px){
        #parent{
          display:flex;
          flex-flow: column;
        }
        #a{order:2;}
        #c{order:1;}
        #b{order:3;}
      }
    </style>
  </head>
  <body>
    <div id="parent">
      <div id="a">one</div>
      <div id="b">two</div>
      <div id="c">three</div>
    </div>
  </body>
</html>

Example: http://jsfiddle.net/devnull/qyroxexv/ (change window-width to see the effect of changing the order of the divs)

devnull
  • 2,740
  • 3
  • 14
  • 11
120

As has already been suggested, Flexbox is the answer - particularly because you only need to support a single modern browser: Mobile Safari.

See: http://jsfiddle.net/thirtydot/hLUHL/

You can remove the -moz- prefixed properties if you like, I just left them in for future readers.

    #blockContainer {
        display: -webkit-box;
        display: -moz-box;
        display: box;
        
        -webkit-box-orient: vertical;
        -moz-box-orient: vertical;
        box-orient: vertical;
    }
    #blockA {
        -webkit-box-ordinal-group: 2;
        -moz-box-ordinal-group: 2;
        box-ordinal-group: 2;
    }
    #blockB {
        -webkit-box-ordinal-group: 3;
        -moz-box-ordinal-group: 3;
        box-ordinal-group: 3;
    }
    <div id="blockContainer">
        <div id="blockA">Block A</div>
        <div id="blockB">Block B</div>
        <div id="blockC">Block C</div>
    </div>
Nicolás Alarcón Rapela
  • 2,714
  • 1
  • 18
  • 29
thirtydot
  • 224,678
  • 48
  • 389
  • 349
  • 13
    Let's just hope Windows 7 phone with its freaking IE never becomes mainstream so we can continue using cool CSS like this. – JoJo Sep 17 '11 at 06:27
  • Brilliant but could be very slow on complex pages – Christopher Tokar Feb 11 '13 at 15:44
  • 4
    The flexbox specification changed, see here for examples of the new implementation: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes – Daniel Ristic Sep 11 '13 at 16:13
  • For this solution, you would have to hope that the underlying implementation uses IDs or some other mechanism to unique identify each div inside the blockContainer. – Doug Jan 16 '14 at 01:20
  • @Doug how about using `nth-child` to target the elements? – Brett Ryan Mar 02 '14 at 16:01
  • I just added the `-ms-` prefix to get it to work on mobile IE. – Justin Jun 27 '14 at 20:42
  • Hm, it uses the `display` property that's probably sometimes cannot be modified. For example, is it possible to exchange two cells in a table? – Lyubomyr Shaydariv Jul 14 '14 at 13:04
  • @LyubomyrShaydariv: [This might help.](http://stackoverflow.com/questions/10414478/make-a-div-display-under-another-using-css-in-a-totally-fluid-layout/10415503#10415503) – thirtydot Jul 14 '14 at 13:37
  • @thirtydot yep. I really think that the concept of orderable boxes is amazing, but I thought it could be implemented using another CSS property not breaking the "original" displays. Bad luck. Anyway, thanks! – Lyubomyr Shaydariv Jul 14 '14 at 13:41
  • Here is a fork of this fiddle that adds support for IE10: http://jsfiddle.net/15Ljpn2q/2/ – Willster May 19 '15 at 10:43
  • This does not work with IE 9 or older. Use display:flex and order:[number] instead it works with IE 10 and newer. – vee Feb 28 '16 at 09:20
  • Nice. Now how would you change that so that the output will be in 1 line with the preceding and following text (like: "Blocks: Block C Block A Block B are available"). Changing the "vertical" to "horizontal" works half way. The 3 blocks are in 1 line, but they are on a line of their own, not together with the other text. – Gik May 15 '16 at 15:22
  • It is a okay solution but it's not working on IE11. The better and newer solution, and also works on IE11: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Ordering_Flex_Items – Duka Aug 21 '20 at 13:42
  • I didn't see anyone refer to Jamie Wong's beautiful code that uses CSS to move elements vertically and horizontally. Visit http://jamie-wong.com/2011/10/16/fifteen-puzzle-algorithm/ and open Dev Tools to watch the pieces of the puzzle move visually but not in their HTML positions. – Tom Elam Oct 26 '21 at 20:56
65

Update: Two lightweight CSS solutions:

Using flex, flex-flow and order:

Example1: Demo Fiddle

    body{
        display:flex;
        flex-flow: column;
    }
    #blockA{
        order:4;
    }
    #blockB{
        order:3;
    }
    #blockC{
        order:2;
    }

Alternatively, reverse the Y scale:

Example2: Demo Fiddle

body{
    -webkit-transform: scaleY(-1);
    transform: scaleY(-1);
}
div{
    -webkit-transform: scaleY(-1);
    transform: scaleY(-1);
}
SW4
  • 69,876
  • 20
  • 132
  • 137
39

I known this is old, but I found a easier solution and it works on ie10, firefox and chrome:

<div id="wrapper">
  <div id="one">One</div>
  <div id="two">Two</div>
  <div id="three">Three</div>
</div> 

This is the css:

#wrapper {display:table;}
#one {display:table-footer-group;}
#three {display:table-header-group;}

And the result:

"Three"
"Two"
"One"

I found it here.

Sertage
  • 3,123
  • 1
  • 19
  • 26
12

This method worked for me without flexbox:

#blockA,
#blockB,
#blockC {
    border: 1px solid black;
    padding: 20px;
}


.reverseOrder,
#blockA,
#blockB,
#blockC {
    -webkit-transform: rotate(180deg);
       -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
         -o-transform: rotate(180deg);
            transform: rotate(180deg);
}
<div class="reverseOrder">
    <div id="blockA">Block A</div>
    <div id="blockB">Block B</div>
    <div id="blockC">Block C</div>
</div>
ValentinVoilean
  • 1,365
  • 1
  • 13
  • 22
3

HTML:

<div id="blockC second-order">Block C</div>
<div id="blockA">Block A</div>
<div id="blockB">Block B</div>
<div id="blockC first-order">Block C</div>

CSS

.second-order {
     display: none;
}

@media only screen and (max-device-width: 480px) {
     .first-order: {
         display: none;
     }

     .second-order: {
         display: block;
     }
}

I think this is non-stupid solution becouse repeating content is no problem in the most of cases and in your case if it is advertisment you would repeat not a lot of content.

I've answers on this question althought one year passed, becouse I was searching for solution, I read this and got this idea.

kspacja
  • 4,648
  • 11
  • 38
  • 41
  • 1
    Interesting "thinking outside the box" approach. However, I would only recommend it if the duplicated content is inserted automatically or duplicated via some include mechanism. If it means cut/paste a block of content, that would be a maintenance nightmare. (Someone will come along later, and only make a fix in one place, test it on one screen width, and not realize they didn't fix it in all cases.) – ToolmakerSteve Aug 08 '14 at 01:31
  • I agreed, but I think if we use some include mechanism there shouldn't be a problem with coping/pasting, but without it my solution is not the best - I know. – kspacja Aug 08 '14 at 11:08
2

I managed to do it with CSS display: table-*. I haven't tested with more than 3 blocks though.

fiddle

yoda
  • 10,834
  • 19
  • 64
  • 92
  • Good to see a solution that doesn't require CSS3 flex. Not generalizable to more than 3 top-level boxes -- but often that is enough. – ToolmakerSteve Aug 08 '14 at 01:20
2
<div id="container">
    <div id="a">Block A</div>
    <div id="b">Block B</div>
    <div id="c">Block C</div>
</div>

lets say the height of a block is 100px

#container     {position:relative; height: 300px;}
#a, #b, #c     {position:absolute; height: 100px}
#c             {top: 0px;}
#b             {top: 100px;}
#a             {top: 200px;}
fceruti
  • 2,405
  • 1
  • 19
  • 28
  • 4
    I don't want to hardcode the heights. The elements inside are dynamic, so the height is not known. – JoJo Sep 15 '11 at 04:27
1

You could mess with the margins: http://jsfiddle.net/zV2p4/

But you would probably be better off using position: absolute. This does not change display: block, but it will make the width auto. To fix this, make the divs width: 100%

Will
  • 19,661
  • 7
  • 47
  • 48
  • What if I wanted to preserve the block property? That is, if the top block all of a sudden has more text added, the bottom blocks would be pushed downward. With your hardcoding of margins, the blocks would collide. – JoJo Sep 15 '11 at 04:43
  • You should probably use JavaScript then. There may be a CSS3 property that will accomplish this. But, CSS3 is not supported by all browsers. – Will Sep 15 '11 at 04:50
0

Hows this for low tech...

put the ad at the top and bottom and use media queries to display:none as appropriate.

If the ad wasn't too big, it wouldn't add too much size to the download, you could even customise where the ad sent you for iPhone/pc.

0

Possible in CSS3: http://www.w3.org/TR/css3-writing-modes/#writing-mode

Why not change the orders of the tags? Your HTML page isn't made out of stone, are they?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
kazinix
  • 28,987
  • 33
  • 107
  • 157