Journal tags: loading

5

sparkline

Lost in calculation

As well as her personal site, wordridden.com, Jessica also has a professional site, lostintranslation.com.

Both have been online for a very long time. Jessica’s professional site pre-dates the Sofia Coppola film of the same name, which explains how she was able to get that domain name.

Thanks to the internet archive, you can see what lostintranslation.com looked like more than twenty years ago. The current iteration of the site still shares some of that original design DNA.

The most recent addition to the site is a collection of images on the front page: the covers of books that Jessica has translated during her illustrious career. It’s quite an impressive spread!

I used a combination of CSS grid and responsive images to keep the site extremely performant. That meant using a combination of the picture element, source elements, srcset attributes, and the sizes attribute.

That last part always feels weird. I have to tell the browser what sizes the images will displayed at, which can change depending on the viewport width. But I’ve already given that information in the CSS! It feels weird to have to repeat that information in the HTML.

It’s not just about the theoretical niceties of DRY: Don’t Repeat Yourself. There’s the very practical knock-on effects of having to update the same information in two places. If I update the CSS, I need to remember to update the HTML too. Those concerns no longer feel all that separate.

But I get it. Browsers use a look-ahead parser to start downloading images as soon as possible, so I understand why I need to explicitly state what size the images will be displayed at. Still, it feels like something that a computer should be calculating, not something for a human to list out by hand.

But wait! Most of the images on that page also have a loading attribute with a value of “lazy”. That tells browsers they don’t have to download the images immediately. That sort of negates the look-ahead parser.

That’s why the HTML spec now includes a value for the sizes attribute of “auto”. It’s only supposed to be used in conjunction with loading="lazy" (otherwise it means 100vw).

Browser makers are on board with this. You can track the implementation progress for Chromium, WebKit, and Firefox.

I would very much like to see this become a reality!

Conditional CSS

I got some great comments on my post about conditionally loading content.

Just to recap, I was looking for a way of detecting from JavaScript whether media queries have been executed in CSS without duplicating my breakpoints. That bit is important: I’m not looking for MatchMedia, which involves making media queries in JavaScript. Instead I’m looking for some otherwise-useless CSS property that I can use to pass information to JavaScript.

Tantek initially suggested using good ol’ voice-family, which he has used for hacks in the past. But, alas, that unsupported property isn’t readable from JavaScript.

Then Tantek suggested that, whatever property I end up using, I could apply it to an element that’s never rendered: meta or perhaps head. I like that idea.

A number of people suggested using font-family, citing Foresight.js as prior art. I tried combining that idea with Tantek’s suggestion of using an invisible element:

@media screen and (min-width: 45em) {
    head {
        font-family: widescreen;
    }
}

Then I can read that in JavaScript:

window.getComputedStyle(document.head,null).getPropertyValue('font-family')

It works! …except in Opera. Where every other browser returns whatever string has been provided in the font-family declaration, Opera returns the font that ends up actually getting used (Times New Roman by default).

I guess I could just wait a little while for Opera to copy whatever Webkit browsers do. (Ooh! Controversial!)

Back to the drawing board.

Stephanie suggested using z-index. I wouldn’t to do that in the body of my document for fear of screwing up any complex positioning I’ve got going on, but I could apply that idea to the head or a meta element:

@media screen and (min-width: 45em) {
    head {
        z-index: 2;
    }
}

Alas, that doesn’t seem to work in Webkit; I just get back a value of auto. Curses! It works fine if it’s applied to an element in the body but like I said, I’d rather not screw around with the z-indexing of page elements. Ironically, it works fine in Opera

A number of people suggested using generated content! “But how’s that supposed to work?” I thought. “I won’t be able to reference the generated DOM node from my JavaScript, will I?”

It turns out that I’m an idiot. That second argument in the getComputedStyle method, which I always just blindly set to null, is there precisely so that you can access pseudo-elements like generated content.

Dave McDermid, Aaron T. Grogg, Colin Olan, Elwin Schmitz, Emil, and Andy Rossi arrived at the solution roundabout the same time.

Here’s Andy’s write-up and code. His version uses transition events to fire the getComputedStyle check: probably overkill for what I want to do, but very smart thinking.

Here’s Emil’s code. I was initially worried about putting unnecessary generated content into the DOM but the display:none he includes should make sure that it’s never seen (or read by screenreaders).

I could just generate the content on the body element:

@media all and (min-width: 45em) {
    body:after {
        content: 'widescreen';
        display: none;
    }
}

It isn’t visible, but it is readable from JavaScript:

var size = window.getComputedStyle(document.body,':after').getPropertyValue('content');

And with that, I can choose whether or not to load some secondary content into the page depending on the value returned:

if (size == 'widescreen') {
    // Load some more content.
}

Nice!

As to whether it’s an improvement over what I’m currently doing (testing for whether columns are floated or not) …probably. It certainly seems to maintain a nice separation between style and behaviour while at the same time allowing a variable in CSS to be read in JavaScript.

Thanks to everyone who responded. Much appreciated.

Update: If you’re finding that some browsers are including the quotes in the returned :after value, try changing if (size == 'widescreen') to if (size.indexOf("widescreen") !=-1). Thanks to multiple people who pointed this out.

Conditionally loading content

Bevan did a great job on the dConstruct website. I tried to help him out along the way, which mostly involved me doing a swoop’n’poop code review every now and then.

I was particularly concerned about performance so I suggested base-64 encoding background images wherever possible and squeezing inline images through ImageOptim. I also spotted an opportunity to do a bit of conditional loading.

Apart from the home page, just about every other page on the site features a fairly hefty image in the header …if you view it in a wide enough browser window. If you’re visiting with a narrow viewport, an image like this would just interfere with the linearised layout and be an annoying thing to scroll past.

So instead of putting the image in the markup, I put a data-img attribute on the body element:

<body data-img="/img/conference.png">

Then in a JavaScript file that’s executed after the DOM has loaded, I check to see if the we’re dealing with a wide-screen viewport or not. Now, the way I’m doing this is to check to see if the header is linearised or if it’s being floated at all—if it’s being floated, that means the layout styles within my media queries are being executed, ergo this is a wide-screen view, and so I inject the image into the header.

I could’ve just put the image in the markup and used display: none in the CSS to hide it on narrow screens but:

  1. that won’t work for small-screen devices that don’t support media queries, and
  2. the image would still be downloaded by everyone even if it isn’t displayed (a big performance no-no).

I’m doing something similar with videos.

If you look at a speaker page you’ll see that in the descriptions I’ve written, I link to a video of the speaker at a previous conference. So that content is available to everyone—it’s just a click away. On large viewports, I decided to pull in that content and display it in the page, kind of like I’m doing with the image in the header.

This time, instead of adding a data- attribute to the body, I’ve put in a div (with a class of “embed” and a data-src attribute) at the point in the document where I want to the video to potentially show up:

<div class="embed" data-src="//www.youtube.com/embed/-2ZTmuX3cog"></div>

There are multiple video providers—YouTube, Vimeo, Blip—but their embed codes all work in much the same way: an iframe with a src attribute. That src attribute is what I’ve put in the data-src attribute of the embed div.

<div class="embed" data-src="//player.vimeo.com/video/33692624"></div>

Once again, I use JavaScript after the DOM has loaded to see if the wide-screen media queries are being applied. This time I’m testing to see if the parent of the embed div is being floated at all. If it is, we must be viewing a widescreen layout rather than the linearised content. In that case, I generate the iframe and insert it into the div:

(function(win){
    var doc=win.document;
    if (doc.getElementsByClassName && win.getComputedStyle) {
        var embed = doc.getElementsByClassName('embed')[0];
        if (embed) {
            var floating = win.getComputedStyle(embed.parentNode,null).getPropertyValue('float');
            if (floating != 'none') {
                var src = embed.getAttribute('data-src');
                embed.innerHTML = '<iframe src="'+src+'" width="320" height="180" frameborder="0"></iframe>';
            }
        }
    }
})(this);

In my CSS, I’m then using Thierry Koblentz’s excellent technique for creating intrinsic ratios for video to make sure the video scales nicely within its flexible container. The initial video proportion of 320x180 is maintained as a percentage: 180/320 = 0.5625 = 56.25%:

.embed {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
}
.embed iframe {
    position: absolute;
    top:  0;
    left: 0;
    width: 100%;
    height: 100%;
}

The conditional loading is working fine for the header images and the embedded videos, but I still feel a bit weird about testing for the presence of floating.

I could use matchMedia instead but then I’d probably have to use a polyfill (another performance hit), and I’d still end up maintaining my breakpoints in two places: once in CSS, and again in JavaScript. Likewise, if I just used documentElement.clientWidth, I’d have to declare my breakpoints twice.

Paul Hayes wrote about this issue a while back:

We need a way of testing media query rules in JavaScript, and a way of generating events when a new rule matches.

He came up with the ingenious solution of using transitionEnd events that are fired by media queries. The resulting matchMedia polyfill he made is very clever, but probably overkill for what I’m trying to do—I don’t really need to check for resize events for what I’m doing.

What I really need is some kind of otherwise-useless CSS declaration just so that I can test for it in JavaScript. Suppose there were a CSS foo declaration that I could use inside a media query:

@media screen and (min-width: 45em) {
    body {
        foo: bar;
    }
}

…then in JavaScript I could test its value:

var foovalue = window.getComputedStyle(document.body,null).getPropertyValue('foo');
if (foovalue == 'bar') {
    // do something
}

Of course that won’t work because foo wouldn’t be recognised by the browser so it wouldn’t be available to interrogate in JavaScript.

Any ideas? Maybe something like zoom:1 ? If you can think of a suitable CSS property that could be used in this way, leave a comment.

Of course, now that I’m offering you a textarea to fill in with your comments, you’re probably just going to use it to tell me what’s wrong with the JavaScript I’ve written …those “comments” might mysteriously vanish.

Clean conditional loading

It’s December. That means it’s time for the geek advent calendars to get revved up again:

For every day until Christmas Eve, you can find a tasty geek treat on each of those sites.

Today’s offering on 24 Ways is a little something I wrote called Conditional Loading for Responsive Designs. It expands on the technique I’m using on Huffduffer to conditionally load inessential content into a sidebar with Ajax where the layout is wide enough to accommodate it:

if (document.documentElement.clientWidth > 640) {
// Use Ajax to retrieve content here.
}

In that example, the Ajax only kicks in if the viewport is wider than 640 pixels. Assuming I’ve got a media query that also kicks in at 640 pixels, everything is hunky-dory.

But …it doesn’t feel very to have that 640 pixel number repeated in two places: once in the CSS and again in the JavaScript. It feels particularly icky if I’m using ems for my media query breakpoints (as I often do) while using pixels in JavaScript.

At my recent responsive enhancement workshop in Düsseldorf, Andreas Nebiker pointed out an elegant solution: instead of testing the width of the viewport in JavaScript, why not check for a style change that would have been executed within a media query instead?

So, say for example I’ve got some CSS like this:

@media all and (min-width: 640px) {
    [role="complementary"] {
        width: 30%;
        float: right;
    }
}

Then in my JavaScript I could test to see if that element has the wide-screen layout or not:

var sidebar = document.querySelector('[role="complementary"]'),
floating = window.getComputedStyle(sidebar,null).getPropertyValue('float');
if (floating == 'right') {
// Use Ajax to retrieve content here.
}

Or something like that. The breakpoint is only ever specified once so I ever change it from 640 pixels to something else (like 40 ems) then I only have to make that change in one place. Feel free to grab the example and play around with it.

By the way, you’ll notice that in the original 24 Ways article and also in this updated example, I’m only testing the layout on page load, not on page resize. It would be fairly easy to add in an onResize test as well, but I’m increasingly coming to the conclusion that—apart from the legitimate case of orientation change on tablets—the only people resizing their browser windows after the page loads are web designers testing responsive designs. Still, it would be nice to get some more data to test that hypothesis.

Lazy loading on Huffduffer

If you look at my profile page on Huffduffer, this is what you’ll see:

  • my details,
  • what I’ve huffduffed,
  • links to subscribe to my podcast and
  • my tag cloud.

That’s the core information for that page, preceded by a header with site navigation and followed by a footer with some additional links.

Because I’ve provided a URL with my details, there’s some extra information displayed in the sidebar:

It’s a similar situation if you look at a piece of audio I’ve huffduffed. The core information is:

  • all the details about the audio (title, description, tags),
  • who else has huffduffed this,
  • possibly-related items and
  • links to share and embed the audio.

In addition, because I’ve used a machine tagbook:author=cory doctorow—the sidebar contains:

  • related articles from The Guardian,
  • sales information from The New York Times,
  • books on Amazon.

In both cases, this supporting information isn’t essential; it’s just nice to have.

There’s a potential performance problem though. Because this extra information is coming from third-party services—and despite the fact that I’m doing some caching—it could delay the display of the whole page. So I took some time on the weekend to adjust the architecture a little bit. Now the extra information is requested with Ajax once the core information has already loaded. This is .

Now I’ve introduced a dependency on JavaScript, which is far from ideal, but because this is just “nice to have” information, I think it’s okay if it isn’t seen by a subset of visitors.

In fact, because this extra lazy-loaded stuff takes up valuable screen real estate, I think it might be acceptable to only serve it up to visitors who have the screen real estate to accommodate it:

if ($(document).width() > 640) {
// do lazy loading here
}

So if you load my profile on a small screen, you won’t see my latest tweets or my Last.fm recommendations. Likewise if you look at something I’ve huffduffed that’s tagged with music:artist=radiohead you won’t see information from Last.fm, pictures from Flickr or albums on Amazon unless you load the page with a wide enough viewport.

Now it could be that the real issue here isn’t viewport size, but connection speed …in which case I’m making the classic error of conflating small screen size with limited bandwidth. A script like Boomerang, which attempts to measure a user’s connection speed, could be very handy in this situation.

Lazy loading is the new fold

I was chatting with James about the implications that lazy loading could have for earlier phases of the design process: wireframing, page description diagrams, and so on.

Traditionally, you’ve got only two choices when judging what content to include: either something is on the page or it isn’t. You can use hierarchy, position and contrast to emphasise or de-emphasise the content but fundamentally, it’s a binary choice. But with conditional lazy-loading there’s a third option: include some content if the user’s circumstances warrant it.

Once again, Luke’s Mobile First approach is a useful thought experiment. It can help prioritise which elements are core content and which could be lazy-loaded:

Mobile devices require software development teams to focus on only the most important data and actions in an application. There simply isn’t room in a 320 by 480 pixel screen for extraneous, unnecessary elements. You have to prioritize.

So when a team designs mobile first, the end result is an experience focused on the key tasks users want to accomplish without the extraneous detours and general interface debris that litter today’s desktop-accessed Web sites. That’s good user experience and good for business.

Sometimes there are political reasons for wanting the “extraneous detours and general interface debris.” Lazy loading for large-screen users could be the least worst option in that situation. Semantically speaking, the kind of content that might be marked up in an aside element might be a good candidate for lazy loading …if the viewport is large enough.

I have a feeling that we’re going to be seeing a lot more of lazy loading as the responsive web design revolution rolls on. Used judiciously, it could provide plenty of performance benefits. But if it’s taken too far, lazy-loading could be disastrous, resulting in sites that rely on JavaScript to load their core content—I’m looking at you, Twitter.