Content Jumping (and How To Avoid It)

The following is a guest post by Brandon Smith (CodePen). Brandon takes a look at one of the more eye-twinging behaviors of web sites and some possibilities for avoiding it.

Few things are as annoying on the web as having the page layout unexpectedly change or shift while you're trying to view or interact with it. Whether you're attempting to read an article as it wriggles around in front of you, or you try to click a link only to have another one push it out of the way and take you off to somewhere unexpected, it's always frustrating.

This layout shifting is especially frustrating on mobile devices, where a big enough change can push all familiar content off screen and cause a visitor to completely lose her bearings. I would argue that shifting the page layout after the initial render (without relevant user interaction) may be the single most unpleasant user experience a site can create. I'm surprised to see it happen even on really big-name sites that are otherwise very well made. The good news is that it's fixable.

Luckily, modern browsers don't perform initial render until the style sheets have finished loading, which means that with some clever CSS we can do a lot toward fixing this problem.

What causes layout shifting?

Most commonly, it's the result of loading Ajax partials. Ajax is asynchronous, meaning nothing is waiting for it to finish. But when it does finish, and the results are plopped into the document, it can cause relayout and push other elements around.

It's especially common with ads, but can happen with any layout-affecting content that loads after initial render, including images or even fonts.

I've ran into this problem on sites I work on myself

The first time I encountered this problem actually wasn't for ads or images, but for rendered templates from our own server. I was working at Broadleaf Commerce, on a preexisting Admin interface that we were adding features to. We didn't want to rebuild the whole thing using something like Angular, but we did want some of the content to be able to update without a page refresh. We created Ajax containers bound to server routes that would re-render and send back just a section of the page. It worked quite well, but in some cases we had multiple of these sections going down the page with static headers in-between. A few seconds after initial visit the headings (and sometimes other static content) would all jump down - exactly the jarring layout shifting experience we're trying to avoid.

Fixing with min-height

To fix it, I used the browser's DevTools to measure the height of the resulting content and hard coded it as a min-height for the container in the CSS.

Imagine this widget was Ajax'd in. We could measure the size of it here and set min-height: 363px on a placeholder element.

This meant that the surrounding layout began in more or less the state that it would end up, with the only change being the interior of the content boxes. I also set a background color for the containers to suggest that something would go there. These simple additions made the interface much more concrete and predictable. One could debate whether hardcoding the minimum height is wise, given that it decouples the layout from the content. This is why we used min-height instead of height. If the content ends up making the element taller, it can still do that, which isn't great, but it's slightly better than doing nothing.

Here's an example of what this would look like (rerun to see delayed ad loading):

See the Pen Avoiding Content Jumps by CSS-Tricks (@css-tricks) on CodePen.

Another Example: A Third-Party Widget

Another time, I was working on a client site who wanted to use the Braintree drop-in UI (https://www.braintreepayments.com/products-and-features/drop-in-ui) for their checkout page.

It's a great little widget, but you'll notice that even on Braintree's own site the content gets pushed around when the widget expands. To Braintree's credit it does so with a smooth transition which helps maintain some continuity, but the page I worked on still benefitted from a wrapper element with a min-height, keeping something as fundamental as the checkout button from moving around unexpectedly.

Fixing with Transitions

Using smooth transitions can make an unexpected content change a lot less jarring.

.ad-wrapper {
  height: 0;
  overflow: hidden;
  transition: height 0.66s ease-out;
}
.ad-wrapper.loaded {
  height: 100px;
}

See the Pen Avoiding Content Jumps by CSS-Tricks (@css-tricks) on CodePen.

The downside is that they only work when you set dimensions explicitly; min- properties and content-derived layout changes won't transition. Still, transitions can be used in cases where you have unpredictably sized content and you don't mind using JavaScript to query its size and smoothly resize the container accordingly.

Another Example: Font Loading Shifts

In a more unusual case, I was once working on a client site that prominently used a horizontally condensed font. The problem was that it had to be loaded in, so after a few seconds nearly all of the site content would suddenly change size. It wasn't as drastic of a shift as the above examples, but since it affected nearly everything on the page it was much more of an eyesore. In the end I found this really great article that supplies a verbose but effective font-family statement that covers web safe condensed fonts for all major platforms. But before finding that, min-height was once again my friend.

Examples From Around The Web

The Verge (mobile)

I notice on The Verge that

  1. Their ad at the top is sometimes a small banner and sometimes a larger ad
  2. They seem to already be using a min-height technique for the banner-sized ad

It's possible that their ad API doesn't tell them what size the ad will be. If it does, they should adapt the min-height accordingly. If this isn't possible, they might already be doing all they can with pure CSS. Although even then, I would argue that from a UX perspective, a really large ad should go further down among the content instead of at the very top, and the banner would be more appropriate at the top. Then again, showing the user a nearly full-screen ad as soon as they visit the main page is probably a lucrative source of revenue.

Kotaku (mobile)

Worth noting on Kotaku is that there's no ad at the very top of the main page, so when you first visit it there isn't a jump. However, if the page ever loads while already scrolled down a ways (such as after pressing the back button when you've finished reading an article), there is some massive, disorienting jumping caused by large ads peppered throughout the post stream. They all seem to be the same size, so this would be a simple fix.

Summary

One of the most important things to keep in mind when designing any user experience is expected behavior. We expect that clicking a blue, underlined segment of text will take us to another page. If it instead opens a modal, or does nothing at all, we have a confusing and frustrating user experience. The same is true of navigating the page we're already on. When we see an article appear in front of us, we expect it to sit still unless we scroll it. When we see a link or a button, we expect it to still be there when we reach to tap on that spot. Violations of these basic assumptions are at best unpleasant, at worst frustrating or even disorienting.