scottjehl/Respond — pull request #211 by mexon (2013-08-06 02:54:33+00:00)
Title: Iterate over styles in reverse order of media.

This isn't a real pull request, more pointing out what looks to me a bug, or at least weird design. Apologies for the too long explanation: it's because I tried and failed to understand how this works and what the correct solution is.

My page includes two stylesheets. First is style.css, a general-purpose style, which has no "@media" line and thus effectively has media=all. Next is responsive.css, with all the "@media" lines for different screen widths. The @media lines all specify "screen".

responsive.css has a lot of definitions intended to override style.css. I was finding that your script was working, but when the CSS selector in the two stylesheets was the same, style.css was winning instead of responsive.css. To properly imitate what happens in a browser that supports @media lines, later scripts should win.

Digging into this, I see two weird effects going on in your script.

First, it inserts style blocks like this:

head.insertBefore( ss, lastLink.nextSibling );

It iterates over a list of style blocks [A, B, C], and inserts each one before node X's next sibling. Start with nodes [X, Y], then you get [X, A, Y], then [X, B, A, Y] and finally [X, C, B, A, Y]. This is the opposite order from what I would expect. My patch reverses this order, and it so happens that my page now works.

But that doesn't mean [X, A, B, C, Y] is correct, and here I'm seriously confused. I would expect to see [X, , Y]. But in fact the script doesn't appear to be making any effort to preserve the order in which the stylesheets were specified in the original tags. It's sorting by media type. So with my patch, what I get is [X, <@media all stuff>, <@media screen stuff>, Y]. And I assume that since styleBlocks is an associative array, there's actually no particular guarantee which order the loop will produce. My patch works only because I'm lucky. Or rather, it works because the keys of styleBlocks are created in the same order as the corresponding tags, and as long as the number of keys is small the hash table returns them in creation order.

Buy why group by @media type at all? I would guess that there's an extra level of CSS specificity, which says that a definition inside a "@media " overrides "@media all". That would argue for always putting the "@media all" block first. But I haven't been able to find any spec suggesting what order should be used.

The other possibility is that @media has no effect on specificity. In that case it doesn't seem correct to group by @media type, and that whole loop is pointless. Instead, the style blocks should be created inside the mediastyles loop, preserving the order from the original DOM tree.

For now I'm going to go with my naive and almost surely broken patch because it works. But if you agree there's a problem here, I'll try to implement a robust solution.