Viewport and media query based preloading
I recently encountered a bug that I couldn’t find any information about elsewhere so I’m documented it to hopefully save someone time in the future.
This only applies if you are using the media
attribute on a <link rel="preload">
tag and you are using a media query to only target certain viewports.
The markup
<head>
...
<link rel="preload" media="(min-width: 600px)" href="desktop.jpg" as="image">
<link rel="preload" media="(max-width: 599px)" href="mobile.jpg" as="image">
<meta name"viewport" content="width=device-width, initial-scale=1">
...
</head>
Using this, let’s see what gets loaded on different screen sizes.
So far so good. If you are like me, at this point you might assume that the media query is working correctly. But let’s take a look at what happens when we switch to the chrome emulator.
The issue
Well this is confusing. The desktop image is being preloaded on chrome’s mobile emulator (at a device width of 375px). To double check the media query is correct, you can run in the console window.matchMedia('(max-width: 599px)').matches
which returns true. What is happening here?!
The solution
It turns out that the browser’s preload scanner will not wait for other meta tags to be parsed before evaluating the media query in <link>
tags, so in this case the viewport has not been configured when the media query is evaluated.
Since the viewport has not been set, it works correctly for a shrunken screen size browser, but does not work on a device with smaller device width.
The solution is simple— reorder the <meta name"viewport" content=”width=device-width, initial-scale=1">
tag to be above the preload tags.To entirely avoid running into this sort of behavior, I’d recommend putting the viewport
media query right at the top of the entire<head>
tag, just below the <title>
.
Happy hacking!