The Death of Marat
Jacques-Louis David
Liberty Leading The People
Eugène Delacroix
Louis XVI, King of France and Navarre
Antoine-François Callet

Some of my layout articles, especially those that show items arranged with flexbox or CSS columns, have generated questions about the possibilities of reordering content. While flexbox has the ability to reorder items, the flexbox module is generally intended for changes to UI, rather than reordering based on arbitrary criteria.

In this article, I’ll show how you can easily arrange items alphabetically and chronologically on a page; in turn, you could use the same principles to order items using any kind of criteria.

It’s important to understand that this is true reordering of content, i.e. moving from ABCDE to CBADE. If you want to simply filter content – removing the B & D elements from ABCDE so that the user sees ACE – I’ve shown ways of doing that in previous articles.

Determining The Data Source

There are four sources of data that we could use to as a key to reorder elements:

  • Use the content: easiest to write HTML for, but slightly tricky to implement, and the hardest to keep consistent. Content keys are used in this example.
  • We could use data that already exists in the markup, such as required and their values. This may include information that has been added to the markup, such as data attributes.
  • Meta information, such as the file size of images.
  • We could also use data that exists outside the HTML page, such as a database, when ordering large information sets.

To start, I’ll create the markup, enhanced with a little microdata. For this example, I’m using paintings of the French Revolution:

<div id="rev-works">
	<figure itemscope itemtype="http://schema.org/Painting">
		<img src="the-death-of-marat.jpg" alt itemprop="image">
		<figcaption itemprop="name">The Death of Marat</figcaption>
		<div>
			<span itemprop="creator">Jacques-Louis David</span>
			<time itemprop="dateCreated">1793</time>
			</div>
	</figure>
…
</div>

Only one painting is shown here, as all the other works take the same pattern. You can see we have several possible content keys to use, including the date of creation and the artist’s name.

The CSS

While styling the content isn’t our focus here – there are plenty of ways to place the content side-by-side – this is the basic stylesheet I’m using:

#rev-works,
#rev-works figure div { 
	display: flex;
	align-items: flex-start; 
}
#rev-works figure {
	flex: 1;
	background: #fff;
	padding: 10px; 
}
#rev-works figure img {
	width: 100%;
	height: auto;
}
#rev-works figcaption {
	margin: 1rem auto;
	text-align: center;
	font-weight: 700; 
}
#rev-works figure div {
	justify-content: space-between; 
}

The Menu

We need a UI element to allow the user to sort the content. This could be practically anything, from to buttons, but in this case I’ll use a <select> drop-down:

<label for="orderby">Order by:</label>
<select name="orderby" id="orderby">
	<option value="theme">Theme</option>
	<option value="artist">Artist</option>
	<option value="date">Date</option>
</select>

(I’m assuming that the default order of the paintings corresponds to a theme.)

Ideally the <select> element would be injected into the page with JavaScript, since what we’re about to do requires JavaScript in any case: there’s no point in presenting an element to visitors that they can’t use.

The JavaScript

The basic script is placed at the end of the document:

var theme = document.getElementById("orderby"),
paintings = document.getElementById("rev-works"),
figures = paintings.querySelectorAll("figure"),
original = paintings.innerHTML;
function sortUs(parent, child, key) {
	var items = Array.prototype.slice.call(document.querySelectorAll(parent + " " + child)).sort(function(a, b) {
		var comparA = a.querySelector(key).innerHTML;
		var comparB = b.querySelector(key).innerHTML;
		return (comparA < comparB) ? -1 : (comparA > comparB) ? 1 : 0;
	});
	for (var i = 0; i < figures.length; i++) {
		paintings.appendChild(items[i]);
	}
}
theme.onchange = function(){ 
	if (theme.value == "theme") { 
		paintings.innerHTML = original;
	} else {
		if (theme.value == "artist") { 
			el = "span[itemprop=creator]"; 
		}
	if (theme.value == "date") {
		el = "time"; 
	}
	sortUs("#rev-works","figure",el);
}}

I’m not going to explain every aspect of the code here: that’s for future articles. For now, it’s enough to note the following:

  • The central function is the parametrized sortUs, which takes three arguments: the parent element, the children elements to reorder, and the key.
  • The original state of the layout is taken at the start of the script so that it can be returned to easily without a re-sort.
  • The innerHTML – that is, the content from a particular element inside each <figure> is taken as the key, and sequentially compared to each other to determine the order of the <figure> elements that contain them.

Adding Easing

The code works, but the UX is all a little sudden and brutal, with elements appearing and disappearing suddenly. With a little more work you could add a sequential fade-in effect; the code to do that is available in this article’s CodePen repo.

Responsiveness & Conclusion

One of the nice aspects of using is that it easily transfers to presenting the paintings in “portrait” mode when the browser window narrows: the script changes the physical order of elements, which flexbox respects.

Hopefully this small piece of code has shown that you don’t necessarily need JQuery or frameworks like IsoTope or TinySort to create dynamic sorted content on your pages.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/gDboB