-
-
Save nathansmith/905220 to your computer and use it in GitHub Desktop.
/* | |
Adapt.js licensed under GPL and MIT. | |
Read more here: http://adapt.960.gs | |
*/ | |
// Closure. | |
(function(w, d, config, undefined) { | |
// If no config, exit. | |
if (!config) { | |
return; | |
} | |
// Empty vars to use later. | |
var url, url_old, timer; | |
// Alias config values. | |
var path = config.path; | |
var range = config.range; | |
var range_len = range.length; | |
// Create empty link tag: | |
// <link rel="stylesheet" /> | |
var css = d.createElement('link'); | |
css.rel = 'stylesheet'; | |
// Adapt to width. | |
function adapt() { | |
// This clearInterval is for IE. | |
// Really it belongs in react(), | |
// but doesn't do any harm here. | |
clearInterval(timer); | |
// Parse browser width. | |
var x = w.innerWidth || d.documentElement.clientWidth || d.body.clientWidth || 0; | |
// While loop vars. | |
var arr, arr_0, val_1, val_2, is_range, file; | |
// How many ranges? | |
var i = range_len; | |
var last = range_len - 1; | |
while (i--) { | |
// Turn string into array. | |
arr = range[i].split('='); | |
// Width is to the left of "=". | |
arr_0 = arr[0]; | |
// File name is to the right of "=". | |
// Presuppoes a file with no spaces. | |
file = arr[1].replace(/\s/g, ''); | |
// Assume min/max if "to" isn't present. | |
is_range = arr_0.match('to'); | |
// If it's a range, split left/right sides of "to", | |
// and then convert each one into numerical values. | |
// If it's not a range, turn min/max into a number. | |
val_1 = is_range ? parseInt(arr_0.split('to')[0], 10) : parseInt(arr_0, 10); | |
val_2 = is_range ? parseInt(arr_0.split('to')[1], 10) : undefined; | |
// Check for: minimum, maxiumum, range. | |
if ((!val_2 && i === 0 && x <= val_1) || (!val_2 && range_len > 1 && i === last && x > val_1) || (x > val_1 && x <= val_2)) { | |
// Built full URL to CSS file. | |
url = path + file; | |
break; | |
} else { | |
// Blank if no conditions met. | |
url = ''; | |
} | |
} | |
// Was it created yet? | |
if (url_old && url_old !== url) { | |
// If so, just set the URL. | |
css.href = url; | |
url_old = url; | |
} else { | |
// If not, set URL and append to DOM. | |
css.href = url; | |
url_old = url; | |
// Use faster document.head if possible. | |
(d.head || d.getElementsByTagName('head')[0]).appendChild(css); | |
} | |
} | |
// Fire off once. | |
adapt(); | |
// Slight delay. | |
function react() { | |
// Clear interval as window resize fires, | |
// so that it only calls adapt() when the | |
// user has finished resizing the window. | |
clearInterval(timer); | |
timer = setInterval(adapt, 100); | |
} | |
// Do we want to watch for | |
// resize and device tilt? | |
if (config.dynamic) { | |
// Event listener for window resize, | |
// also triggered by phone rotation. | |
if (w.addEventListener) { | |
// Good browsers. | |
w.addEventListener('resize', react, false); | |
} else if (w.attachEvent) { | |
// Legacy IE support. | |
w.attachEvent('onresize', react); | |
} else { | |
// Old-school fallback. | |
w.onresize = react; | |
} | |
} | |
// Pass in window, document, config, undefined. | |
})(this, this.document, ADAPT_CONFIG); |
Tiff:
Good call. Less attribute retrieval, and can immediately be turned into a JS object. Think on this further, I will. #Yoda
May I suggest using document.head
when it’s available? It’s as simple as changing line 43 into:
(d.head || d.getElementsByTagName('head')[0]).appendChild(css);
Typo: the example in the comment on line 9 should read id="ADAPT_CSS"
instead of id="adaptive_css"
.
Changing line 43, as you suggested, would cause it not to work in browsers that understand document.head. A browser that understands document.head would never evaluate past the "or" conditional "||".
d.head || d.getElementsByTagName('head')[0].appendChild(css);
Instead, it would need to be...
d.head ? d.head.appendChild(css) : d.getElementsByTagName('head')[0].appendChild(css);
But that's extra code, when I'm only ever referencing <head>
once. I agree that making a shortcut to it, as that article suggests, is a good idea if you're going to be using that convention repeatedly throughout your code. But for this one-liner, I don't think it really matters, because I would still have to add the lengthy selector for old browsers. By doing it only via getElementsByTagName, it works everywhere, and is less code overall.
Sorry, I made a mistake in my last comment. I’ve edited it now, but here is the proper solution again:
(d.head || d.getElementsByTagName('head')[0]).appendChild(css);
And yeah, it’s like 10 more bytes, but IMHO the speed gain in modern browsers is worth it.
Boom, updated! :)
I thought I would note that I made this its own project:
I'll leave this gist here for posterity, but won't be updating it any more.
Gotcha. I usually hesitate to have anything watching document resize/scroll events. Hence trying out my little model, since media-queries serve that purpose. But that has little to do with your solution, more me exploring whether browsers observe JS-inserted media attributes. Which they do, so that's cool (and I have some clean-up to do).
I like the implications of your
data-*
attributes on that script tag. I wonder if something like this would work, given those args:<script src="assets/js-adapt.js" data-path="assets/css/" data-range="{"480":"480.css","480-960":"800.css","960-1280":"960.css","1280-1600":"1280.css","1600":"1280.css"}"></script>
or another JS object structure to collect the ranges.