Vertical reordering of blocks with CSS

Summary
Cross-browser method for full-fledged vertical reordering of blocks of arbitrary height with CSS.

Modern browsers — Flexbox

Arbitrary order

In modern browsers (including Internet Explorer 11 and Edge), visual order of elements can be controlled using the order CSS-property that is a part of the Flexbox layout mechanism. Blocks follow according to the order-property values in increasing order. Value of the order property must be an integer number (both positive and negative values are allowed). Default value is 0.

HTML:
<div class="example">
    <div class="a">First</div>
    <div class="b">Second</div>
    <div class="c">Third</div>
</div>
CSS:
.example {
    display: flex;
    flex-direction: column;
}

.example > .a {order: 3; } /* Will be displayed third  */
.example > .b {order: 2; } /* Will be displayed second */
.example > .c {order: 1; } /* Will be displayed first  */

The display: flex declaration enables Flexbox for the container element. The flex-direction: column declaration makes child elements to be under each other instead of horizontal output used in Flexbox by default.

Vertical positions of the reordered blocks are interdependent: vertical enlargement of any block results in that visually next blocks are automatically shifted vertically, including cases when a block’s height is changed dynamically, for example, as a result of increasing font size via browser settings.

Reverse order

If all you need is just to show elements in reverse order, you can use the flex-direction: column-reverse declaration for the container that contains them, with no need to specify order for each of elements explicitly:

.example {
    display: flex;
    flex-direction: column-reverse;
}

Old browsers — display: table

In browsers that do not support Flexbox (IE 10 and older), it is possible to change vertical order of blocks on an HTML page using CSS table presentation via properties of the display: table family. Regardless of block order in HTML code, header (display: table-header-group) of such table is displayed at the top of it, footer (table-footer-group) — at the bottom, and table body (table-row-group) — between the header and the footer.

.example {
    display: table;
    width: 100%;
}

.example > .a {display: table-footer-group; } /* Will be displayed at the bottom of the pseudo-table */
.example > .b {display: table-row-group;    } /* Will be displayed in the middle */
.example > .c {display: table-header-group; } /* Will be displayed at the top */

With this technique, it is possible to change order of up to three sibling blocks. Additionally, it’s also possible to utilize display: table-caption (displaying a block as table caption) in conjunction with caption-side CSS-property with the value of top or bottom.

The technique works in most of popular browsers, including Internet Explorer 9+ (using the technique in IE8 is limited, see below).

Internet Explorer 6, 7, and 8 — DOM

Old browsers IE6 and IE7 do not support CSS properties of display: table family.

Also, IE8 has a dynamic rendering bug that appears in some cases: if a block to move contains preudo-table elements (display: table*) (this is the only buggy case noticed currently), some pseudo-table cells (such cells as well as cell count are different each time the page is reloaded) may disappear randomly when the page is initially rendered.

So for IE8 and lower, we can override CSS rules that make blocks tably, and additionally move blocks to required positions in DOM tree of HTML document with JavaScript instead of CSS:

/**
 * Reorders sibling elements in DOM tree according to specified order.
 * @param {Array} elems Sibling elements in desired block order.
 */
function reorderElements(elems) {
    // http://tanalin.com/en/articles/css-block-order/
    var count = elems.length;

    if (!count) {
        return;
    }

    var parent = elems[0].parentNode;

    for (var i = count - 1; i >= 0; i--) {
        parent.insertBefore(elems[i], parent.firstChild);
    }
}

Detection of browser features

To use different styles for modern and old browsers, JavaScript-based feature detection can be used.

To detect whether a browser supports Flexbox, test whether the order property of the object available via the style property of the root element of the document (<html></html>) — document.documentElement — is available.

Internet Explorer version can be detected by checking existence of the nonstandard document.all object available in IE 10 and older, in conjunction with existence or absence of one of standard objects.

if ('order' in document.documentElement.style) {
    // Flexbox-compatible browser.
    // Using `order` or `flex-direction: column-reverse`.
}
else if (document.all && !document.addEventListener) {
    // IE8 or older.
    // Changing the real order of blocks in the DOM tree via JS.
}
else {
    // Browser with no Flexbox support, including IE 9/10.
    // Using `display: table`.
}