leaverou.me @leaverou me@leaverou.me
A polyfill is a piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively.
Remy Sharp
A shim that mimics a future API providing fallback functionality to older browsers.
Paul Irish
if (!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g,'');
};
}
' JSConf EU '.trim(); // 'JSConf EU'
var supports = {
localStorage: 'localStorage' in window,
geolocation: 'geolocation' in navigator,
querySelector: 'querySelector' in document,
HistoryAPI: 'pushState' in history,
/* etc */
};
supports.property = function(property){
var dummy = document.createElement('_');
return property in dummy.style;
};
// Test it:
supports.property('textShadow');
var prefixes = ['', '-moz-', '-ms-', '-o-', '-webkit-']; supports.property = function(property) {
for (var i=0; i<prefixes.length; i++) { var prefixed = prefixes[i] + property;
dummy.style.cssText = prefixed + ':inherit;'; if (dummy.style.getPropertyValue(prefixed)) return property;
}};
supports.RGBA = (function(){
var elem = document.createElement('_'), style = elem.style;
try { style.color = 'rgba(0,0,0,.5)'; }
catch(e) { return false; } return style.color.indexOf('rgba') > -1;
})();
supports.selector = function(selector) { var style = document.createElement('style');
style.innerHTML = selector + '{}'; document.body.appendChild(style);
var ret = !!style.sheet.cssRules[0];
document.body.removeChild(style); return ret;
};
Optimistic thought:
supports.element = function(tag) {
var el = document.createElement(tag);
return el instanceof HTMLUnknownElement;
};
More pragmatic:
var supports = {
canvas: 'getContext' in document.createElement('canvas'),
video: 'play' in document.createElement('video'),
progress: 'position' in document.createElement('progress')
};
var input = document.createElement('input');
supports.placeholder = 'placeholder' in input;
input.type = 'file';
supports.multipleUpload = 'multiple' in input;
var input = document.createElement('input');
input.setAttribute('type', 'number');
supports.spinner = input.type === 'number';
supports.mouseenter = (function() {
var el = document.createElement('_');
el.setAttribute('onmouseenter', 'return;');
return typeof el.onmouseenter === 'function';
})();
http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
A polyfill for document.head:
if(!document.head) {
document.head = document.getElementsByTagName('head')[0];
}
And then…
myIframe.contentDocument.head.appendChild(script);
TypeError: Cannot call method 'appendChild' of undefined
Don’t:
Unless… You really have to
Code like this:
if(!('open' in document.createElement('details')) {
var details = document.getElementsByTagName('details');
for(var i=details.length; i--;) {
Details.init(details[i]);
}
}
belongs to your polyfill, NOT the user’s code
In cases like that… Don't break!
Our only tools:
They bubble, so we can use event delegation!
document.addEventListener('DOMAttrModified', function(e) {
var node = e.target, attr = e.attrName;
if(/^input$/i.test(node.nodeName) &&
(attr === 'placeholder' || attr === 'value'))
Placeholder.update(node);
}, false);
onpropertychange
for IE < 7Object#watch()
for FirefoxObject.defineProperty(obj, 'placeholder', {
get: function() { return obj.getAttribute('placeholder') },
set: function(value) { Placeholder.update(obj, value); },
enumerable: true,
configurable: true
});
Support: IE8, FF4, Chrome, Safari 5, Opera 12
obj.__defineGetter__('placeholder', function() {
return obj.getAttribute('placeholder')
});
obj.__defineSetter__('placeholder', function(value) {
Placeholder.update(obj, value);
});
Support: FF3.5, Chrome, Safari 4, Opera 10.10
Combine them: github.com/eligrey/Xccessors
IE < 9 thinks that attributes are properties, so:
the previous getter would result in infinite recursion
When enumerable: true
IE8 throws an exception. Catch it with:
try { /* ... */ } catch(e) {
if(e.number === -0x7FF5EC54) {
/* ... */
}
}
Element.prototype
is not evil in this caseDOMNodeInserted
to cater for new elementswindow.matchMedia || (window.matchMedia = (function(){
var div = document.createElement('div');
div.id = 'mq-test-1'; div.style.cssText = 'position:absolute;top:-100em';
return function(q){
div.innerHTML = '<style media="'+q+'">#mq-test-1{ width:42px; }</style>'; document.body.appendChild(div);
var ret = div.offsetWidth == 42;
document.body.removeChild(div); return { matches: ret, media: q };
}; })());
Simplified version of https://github.com/paulirish/matchMedia.js
if(!('outerHTML' in document.createElement('_')) Object.defineProperty(Element.prototype, 'outerHTML', {get: function(){
if (doc.xmlVersion) { return (new XMLSerializer).serializeToString(node); }
else { var x = document.createElement('_'); x.appendChild(this.cloneNode(false)); return x.innerHTML.replace('><', '>' + this.innerHTML + '<'); }
}, enumerable: true});
Simplified version of https://gist.github.com/1044128 by Eli Grey
tabindex="0"
Everything is hard!
Help yourself
Help everyone
those ____s!