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="http://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="http://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.

Have you published a response to this? :

Responses

Jason Gross

After a quick glace at the behavior of the dConstruct site my thought was to use background-image as your trigger.

I agree with you that looking for the presence of floating just seems like a &quot;meh&quot; solution. It locks in the design and puts your JavaScript at the mercy of a layout decision. The quick foo: bar; example hints at what I would consider to be a more reliable solution in that it uses an attribute from the body or html elements. However, I also get a bad gut feeling on the idea of applying an attribute to one of these elements just for the sake of having a JavaScript hook.

It would appear to me that the dConstruct site is set to only load the body background image once the screen width reaches 45em, the same breakpoint for the header floating. So in this situation why not just look for the presence of a background-image on the body element? If we have determined that we are at a resolution that supports the background image we can serve up the better header images and the videos as well.

Of course this also does not provide us with a solution that we can quickly apply to other designs as a tool for determining when to conditionally load media. For that, we will need someone smarter than I.

David Bushell

Here’s a crazy idea/hack. In your widescreen media query you could redefine (and parse in JavaScript) the font-family property with a stack like: “’Helvetica Neue’, Arial, Helvetica, sans-serif, show-videos”. Assuming the last value is never reached by the browser render…

Tantek

» If you can think of a suitable CSS property that could be used in this way, leave a comment.

There’s one that starts with ‘v’ and ends with ‘y’ I’ve found handy for unconventional use in the past.

# Posted by Tantek on Tuesday, April 24th, 2012 at 2:10pm

Tantek

Or if ‘voice-family’ doesn’t work for DOM access, then perhaps try using ‘font-family’ on a harmless invisible empty element like meta[charset] which you already put in every document so there’s no extra markup. Optionally use an ‘id’ attribute on that meta for slightly faster selector matching, and IE6(-) support.

# Posted by Tantek on Tuesday, April 24th, 2012 at 2:28pm

Stephanie Hobson

If you’re looking for a CSS property to change on the body that won’t affect the display of the page z-index might be your best bet.

I think you could also accomplish this by creating a virtual DOM element to test without actually attaching it to the document in which case you could use whatever property you want. Maybe a little colour coding?

Lee Griffin

I suppose the argument against adding an element to the DOM on resize/load, testing whether it has media query affected attributes or not, before removing it is in the same “performance related issue” realm as the polyfill method you mention?

If zoom works with javascript cross browser I’d be comfortable using it, especially as it is something that can be used without much fear of it screwing up designs if used with the basic “1” value.

With zoom being grey-area as it is though, assuming it was off the table, and given I too wouldn’t see the “float” solution as sustainable as a method, I’d probably lean towards using z-index in a relative way. The only issue with this is the increased workload that’d be necessary on sites that rely heavily on z-index AND would change those elements at different stages with a complex set of media-query rules.

In theory you could see if a z-index value was above a threshold, which would be fine as long as no elements on the page on the same level as elements that are being tested also had z-indexes. But then we could combine it with a flag method too.

For example, if you have three divs, classed “one”, “two” and “three” laid out in such a way that two and three have z-indexes. You’d set the z-index of .two to 5 and .three to 15. One media query then changes the styling of .two and so its z-index goes to 1010, while .three goes to 1015.

This would enable you to do two things. First, the threshold of 1000 is reached, so you can say that the element is changed for media-queries. Secondly you could take the final number of the z-index and test it to check if it is 0 or 5 (these are arbitrary numbers), if it’s 0 then it is both affected by media queries and wants to be involved in an progressive loading/alteration. If it’s 5, it does not.

A second media query would see .three join the “wants to be involved” club, so it’s z-index would change to 1020.

Obviously the disadvantage here is that you need to maintain elements across multiple media-queries, unlike zoom or float where you just switch it to being “on” or “off”.

The same effect could probably be more simply achieved on simple sites by setting up “levels” of media query interaction and applying z-index to the body tag. If you know the route with which your media-queries might affect elements then a suitable flag test can be used. If there are multiple routes that can somehow happen independently of each other, this falls down.

I think on balance, I’d have to be convinced that either zoom wasn’t “standard” enough to use, or that the adding of an element to perform a check on was too costly, before exploring the z-index idea…but I would definitely avoid float as a flag attribute.

Really interesting stuff though, currently redeveloping the company website and so optimisation and appropriate responsive behaviour is right at the forefront of my “things to consider” right now!

Faruk

I use a smartphone media query compound test with Modernizr.mq, which is a matchMedia test (sans polyfill), to detect and consequently alter the DOM and conditionally run / not run certain JS, on http://thepit.ch/ — easy and intuitive to work with. Would that not work for your needs?

# Posted by Faruk on Tuesday, April 24th, 2012 at 3:11pm

sarble

Use font-family?

# Posted by sarble on Tuesday, April 24th, 2012 at 3:47pm

Michaël Guitton

@adactio: “What I really need is some kind of otherwise-useless CSS declaration just so that I can test for it in JavaScript. […]”

My 2 cents: https://developer.mozilla.org/en/CSS/clip; clip: auto;?

# Posted by Michaël Guitton on Tuesday, April 24th, 2012 at 4:35pm

sil

@media screen and (min-width: 45em) { html { page-break-after: always; } body { page-break-after: auto; } }

perhaps?

# Posted by sil on Tuesday, April 24th, 2012 at 4:56pm

Collin

Not sure it’s valid, but how about content?

@media screen and (min-width: 45em) { body { content: ‘wide’; } }

You could then use any number of values, like ‘small,’ ‘hd,’ etc.

# Posted by Collin on Tuesday, April 24th, 2012 at 6:54pm

CW Petersen

Picky, I know, but there’s a little typo. &quot;180/240 = 56.25%&quot; where you’re talking about 180/320.

Alan Dalton

Maybe we don’t “need a way of testing media query rules in JavaScript”. Maybe we just need to start using JavaScript instead of CSS for media queries.

elwin schmitz

In Foresight.js they (ab)use the ‘font-family’ property, so thats a possibility.

But what about the ‘content’-property? It can also contain plain text, and is defined in CSS only, it is presentational after all.

I’ve put a little test at: http://160584.nl/tests/define-breakpoints-once.html Take a look at the Console.log for the javascript-output.

Haven’t tested it in a lot of browsers yet, so maybe its use is limited.

If anyone wants to improve my test: https://gist.github.com/2488173 :)

Aaron T. Grogg

May not be all that pretty, but just a quick thought…

#test::after {
    content: '&amp;lt; 700';
    display: none;
}
@media all and (min-width: 700px) and (max-width: 799px) {
    #test::after {
        content: '700 - 799';
    }
}
@media all and (min-width: 800px) {
    #test::after {
        content: '&amp;gt; 800';
    }
}

var content = window.getComputedStyle(document.getElementById('test'),':after').content;
alert(content);

Obvious issues with IE6/7…

Otherwise, an empty div with width incrementally updated by media query?

Good luck, Atg

Sean Hogan

I’m not sure what’s weird about testing for float. It’s pretty close to what I would imagine is the natural solution - style div.embed with display:none normally, and display: block in the @media section of the stylesheet. Then execute getComputedStyle on the div.embed. I assume that would work with your use-case.

Looking at the bigger picture…

  1. You imply that a matchMedia polyfill would be an unnecessary performance hit, but rich web-sites are heavy by nature (the dconstruct home page seems to weigh in at 220KB including 8KB of js). It would be interesting to hear your guidelines / rules for when some script becomes wasteful.

  2. My favorite color is blue. No wait, it’s red. Ahhh… Seriously, I was thinking the iframe could be included in the page markup with:

    <div class="embed">
       <script type="text/html">
            <iframe src="http://player.vimeo.com/video/33692624" 
                width="320" height="180" frameborder="0">
            </iframe>
        </script>
    </div>

  3. It seems like a lot of the responsive web-design stuff is proven and I guess data-attributes seem to me like something for the prototyping stage. It would be nice if some common microformats evolved. Are any being seriously discussed anywhere?

regards

# Posted by Sean Hogan on Wednesday, April 25th, 2012 at 10:59am

Dave McDermid

You could add a style rule such as:

@media screen and (min-width: 45em) { body::before {content: ‘wide’; display:none;}}

And with JS:

if(getComputedStyle(document.body, ‘:before’).content == "wide") { ; }

This would allow a variety values to be used, or even the min-width value.