How to dynamically change the order of HTML elements

Let's say you're working on a page that has two main components - Element A and Element B.

Suppose you had to implement a responsive layout with the following requirements:

  1. Desktop: Element A should be positioned on top of Element B
  2. Mobile: Element B should be positioned on top of Element A

In this tutorial, we'll look at two different approaches to solve these requirements.

Here's what we'll be setting to achieve:

html elements order change based on layout

Before we begin

It's important to clarify some pre-conditions and set the right expectations as to what the end result will exactly be.

We assume that both elements are siblings in the DOM - i.e., both are direct descendants of the same parent element:

<section class="parent">
  <div id="element-a" class="element">
    <h2>HTML Element A</h2>
  </div>
  <div id="element-b" class="element">
    <h2>HTML Element B</h2>
  </div>
</section>

We'll look at two different solutions. Both accomplish the requirements visually. However, there are some nuances that can make you decide for one or the other.

Here are some quick remarks for each solution, so you can jump right away to the one that better fits your use case.

Solution 1:

Solution 2:

Solution 1: flex-direction

As you may have guessed from the title, we'll be using flexbox. The parent element has to be a flex container. This is how we make it one:

.parent {
  display: flex;
  flex-direction: column;
}

We've also used flex-direction to set the main axis and define how child elements are placed inside the parent container.

By default, flex items are displayed in the same order as they appear in the document.

Now, for the mobile layout we want to reverse the order in which the elements are displayed. The value column-reverse does precisely this. Let's go ahead and add a media query to reverse the flex-direction whenever the display area has a maximum width of 767 pixels:

@media (max-width: 767px) {
  .parent {
    flex-direction: column-reverse;
  }
}

And we're done, that's all we need!

It is important to bear in mind that this solution creates a disconnect between the visual presentation and the DOM order.

"This will adversely affect users experiencing low vision navigating with the aid of assistive technology such as a screen reader. If the visual (CSS) order is important, then screen reader users will not have access to the correct reading order." - MDN web docs

If you have more than two child elements and would like to have more control over their individual display order, checkout the order property.

Solution 2: DOM manipulation

We'll be using JavaScript - via HTML DOM API - to access the document and its elements.

The strategy is to select the element that should be on top - based on the maximum width of the display area, and insert it as the first child of the parent element.

First, let's create a variable to represent our media query:

const mobile = "(max-width: 767px)";

The Window interface provides the method matchMedia() - which returns a media query list, matching a specified media query string.

We can then check whether our variable mobile matches with the current state of the document.

So let’s define a function that conditionally sets Element A or Element B as first child of the parent element:

function setFirstChild() {
  const matched = window.matchMedia(mobile).matches;

  const topElement = matched
    ? document.getElementById("element-b")
    : document.getElementById("element-a");

  const parent = topElement.parentNode;
  parent.insertBefore(topElement, parent.firstChild);
}

Notice that there was no need to explicitly remove it from the DOM, store it in a temporary variable, and append it again.

Since it already exists in the document, insertBefore() moves it from its current position to the new position.

Now, we attach the function we've just created to the load event, so it runs when the document is loaded:

window.onload = setFirstChild;

Finally, we need to watch for media query changes, so that if the user resizes the browser the order updates accordingly:

window.matchMedia(mobile).addEventListener("change", setFirstChild);

You can now test that everything is working as expected by reloading and resizing the browser.

Wrapping up

We've seen two ways to dynamically change the order of HTML elements based on the display width. Neither is necessarily better.

Solution 1 is arguably more practical. However, it breaks keyboard navigation.

Solution 2 manipulates the DOM, which is operationally more intensive than just using media queries.

You can reference the code I used to create this tutorial, which includes examples for both solutions.

Acknowledgements

Thanks to Hemlock for mentioning a way to move a DOM element without having to remove and append it again to the DOM.