Sometimes it is helpful to entirely change chunks of markup when a certain CSS media query is triggered. This could because a certain layout will not work on smaller screen sizes or because it refers to media that you would not want a mobile to download.

Recently I had this exact problem when dealing with a site that included a grid based layout in a carousel/slider. When the browser was sized down from desktop it needed to change the number of columns in the grid. If a column is suddenly removed from the grid then where does the content residing in it go?

Well in this case it would slip to the next slide in the carousel, which then means that every slide in the carousel changes as all the items are reshuffled in the respective grids. This re-gridding of the content could not realistically be performed by JavaScript on the fly.

To workaround this I would need to have a way to have three different versions (one for each supported responsive break point) of the carousel in the page and only show the one relevant to the current media query. Before you run off screaming, no, I did not fire up three carousels and have them waiting for instruction!

You can have hidden HTML/content inside your document that you then conditionally load in at the right point in time using JavaScript. Firstly, to be able to match against media query break points you will want to use window.matchMedia and if you are stuck support <=IE9 you can use Paul Irish’s polyfill.

Next up we need to hide the content that we will conditionally load. Now there are a few ways to do this, but I settled on so called JavaScript templates. They are really simple just involve one script tag surrounding the content:

<script type="text/conditional-html" id="tablet-a-unique-reference">
  <p>I am a hidden block of HTML.</p>

You should note that this script tag is a little different to the ones you are probably used to writing. Instead of having a type of text/javascript they have a type of text/conditional-html. This is what causes the content to be hidden and not evaluated by JavaScript - it serves no other purpose.

Now let us actually go ahead and replace the content. For this demonstration I have assumed that we are moving from a desktop responsive breakpoint to a tablet one. The intial HTML loaded with the page is that of the desktop experience.

var div = document.getElementById("a-unique-reference"),
  desktop_html = div.innerHTML,
  tablet_html = document.getElementById("tablet-a-unique-reference").innerHTML;

The above code just grabs the HTML from the currently displayed <div> and also the content of the JavaScript template we setup above.

var checkAndChangeHtml = function () {
  if (matchMedia("screen and (max-width : 62.000em)").matches) {
    div.innerHTML = tablet_html;
  } else {
    div.innerHTML = desktop_html;
window.addEventListener("resize", checkAndChangeHtml);
checkAndChangeHtml(); // call it once on document load incase it
// initially loads at a lower width/on a mobile device

Now this is where the action takes place. The code adds a listener to resize event so that we can run our code when the user resizes their browser. If the width of the browser falls into our tablet media query range then the div has it’s content set to the tablet HTML.

You should wrap all the of the above in the following to ensure that the DOM is ready (supports >=IE8):

document.onreadystatechange = function () {
  if (document.readyState == "interactive") {
    // code would go here

Obviously if you prefer you can use jQuery to abstract away these event bindings.

So there you have it. A very simple way of conditionally loading content on the client side for use in responsive designs.


After writing this I discovered that someone had already released something called ResponsiveComments that is similar. It however uses more mark up as it wraps the hidden content in a <div> and then HTML comments (<!-- -->). I do not like this approach as it inserts block level items into your source code unlike the script tag example given here. Divs have layout and script tags do not.