Switching the order of block elements with CSS [duplicate]
Asked Answered
T

11

157

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 */
   }
}
Titian answered 15/9, 2011 at 3:51 Comment(10)
Why would you need to do this with only CSS, if you mind me asking?Zwinglian
position: absolute doesn't change display: block.Medius
Is there a reason you want to do this in CSS and not JavaScript? Are you worried about users with JavaScript turned off?Freytag
position:absolute doesn't preserve the stacking and pushing nature of display:block, which is the whole point of having a block element.Titian
It's not possible through efficient CSS, though some jQuery could do the magic, I could show you if you want.Rapport
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.Titian
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.Niobous
Anyone who wants to achieve similar effect with Bootstrap3 columns, here is one tutorial.Manipular
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 }Zachariah
Bruh your short story is a long as your long story XDOnerous
T
122

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>
Tailor answered 15/9, 2011 at 11:52 Comment(14)
Let's just hope Windows 7 phone with its freaking IE never becomes mainstream so we can continue using cool CSS like this.Titian
Brilliant but could be very slow on complex pagesBoraginaceous
The flexbox specification changed, see here for examples of the new implementation: developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxesDjebel
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.Backrest
@Backrest how about using nth-child to target the elements?Infra
I just added the -ms- prefix to get it to work on mobile IE.Wolff
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?Acantho
@LyubomyrShaydariv: This might help.Tailor
@Tailor 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!Acantho
Here is a fork of this fiddle that adds support for IE10: jsfiddle.net/15Ljpn2q/2Estafette
This does not work with IE 9 or older. Use display:flex and order:[number] instead it works with IE 10 and newer.Bournemouth
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.Overliberal
It is a okay solution but it's not working on IE11. The better and newer solution, and also works on IE11: developer.mozilla.org/en-US/docs/Web/CSS/…Executant
I didn't see anyone refer to Jamie Wong's beautiful code that uses CSS to move elements vertically and horizontally. Visit 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.Displant
M
151

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)

Mashhad answered 1/8, 2014 at 9:43 Comment(0)
T
122

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>
Tailor answered 15/9, 2011 at 11:52 Comment(14)
Let's just hope Windows 7 phone with its freaking IE never becomes mainstream so we can continue using cool CSS like this.Titian
Brilliant but could be very slow on complex pagesBoraginaceous
The flexbox specification changed, see here for examples of the new implementation: developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxesDjebel
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.Backrest
@Backrest how about using nth-child to target the elements?Infra
I just added the -ms- prefix to get it to work on mobile IE.Wolff
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?Acantho
@LyubomyrShaydariv: This might help.Tailor
@Tailor 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!Acantho
Here is a fork of this fiddle that adds support for IE10: jsfiddle.net/15Ljpn2q/2Estafette
This does not work with IE 9 or older. Use display:flex and order:[number] instead it works with IE 10 and newer.Bournemouth
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.Overliberal
It is a okay solution but it's not working on IE11. The better and newer solution, and also works on IE11: developer.mozilla.org/en-US/docs/Web/CSS/…Executant
I didn't see anyone refer to Jamie Wong's beautiful code that uses CSS to move elements vertically and horizontally. Visit 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.Displant
L
67

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);
}
Lignify answered 2/12, 2014 at 8:36 Comment(6)
omg, example2 is fantastic)Phenylamine
That second example is pretty interesting. Thanks for the share!Denver
example 2 is magic!Holcman
I can't say "wow" enough for the example 2! But I think it doesn't work for a horizontal list of items.Alfaro
This works best for horizontally listed elements i.e. mobile ViewKrucik
Example #2 is a neat trick - but should NOT be used in production because it breaks everything from text-selection, to TTS (screen-readers for blind users), and many other things. Wow.Poundfoolish
G
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.

Gerhardt answered 14/8, 2014 at 13:32 Comment(1)
Very helpful for when using a table!Burlington
F
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>
Fright answered 4/6, 2015 at 14:2 Comment(1)
OP did not want to reverse order, but change the order: C-A-B, not C-B-A.Subir
G
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.

Grison answered 20/1, 2013 at 19:17 Comment(2)
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.)Cristophercristy
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.Grison
C
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;}
Clarendon answered 15/9, 2011 at 3:57 Comment(1)
I don't want to hardcode the heights. The elements inside are dynamic, so the height is not known.Titian
I
2

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

fiddle

Impassible answered 25/4, 2014 at 18:56 Comment(1)
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.Cristophercristy
L
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%

Leniency answered 15/9, 2011 at 3:59 Comment(2)
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.Titian
You should probably use JavaScript then. There may be a CSS3 property that will accomplish this. But, CSS3 is not supported by all browsers.Leniency
S
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?

Schweiz answered 15/9, 2011 at 4:2 Comment(2)
Layout flow is deprecated... Use writing mode insteadLeniency
@Leniency - Thanks, I was looking at the wrong resource.Schweiz
U
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.

Uria answered 22/5, 2013 at 7:38 Comment(1)
BTW, this is what @kspacja's answer does, and it has code details.Cristophercristy

© 2022 - 2024 — McMap. All rights reserved.