JavaScript 代码片段
第一部分【Common】
Polyfills
第二部分【Fast】
第三部分【Util】
本文档使用 MrDoc 发布
-
+
首页
Polyfills
A polyfill is [a term coined by Remy Sharp](https://remysharp.com/2010/10/08/what-is-a-polyfill) for a snippet of code that adds support for a feature to browsers that don’t offer it natively. They let you provide deeper backwards compatibility and browser support without having to use a clunky preprocessor or command line tool. ## How to use a polyfill Drop polyfills from the list below into your codebase, ideally before any code that uses the JavaScript methods or browser APIs you’re polyfilling. Alternatively, you can automatically polyfill your site with a service like [polyfill.io](https://polyfill.io/). ## Array.every() Pushes support back to at least IE6. ```js /** * Array.every() polyfill */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every if (!Array.prototype.every) { Array.prototype.every = function(callbackfn, thisArg) { 'use strict'; var T, k; if (this == null) { throw new TypeError('this is null or not defined'); } // 1. Let O be the result of calling ToObject passing the this // value as the argument. var O = Object(this); // 2. Let lenValue be the result of calling the Get internal method // of O with the argument "length". // 3. Let len be ToUint32(lenValue). var len = O.length >>> 0; // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. if (typeof callbackfn !== 'function') { throw new TypeError(); } // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. if (arguments.length > 1) { T = thisArg; } // 6. Let k be 0. k = 0; // 7. Repeat, while k < len while (k < len) { var kValue; // a. Let Pk be ToString(k). // This is implicit for LHS operands of the in operator // b. Let kPresent be the result of calling the HasProperty internal // method of O with argument Pk. // This step can be combined with c // c. If kPresent is true, then if (k in O) { // i. Let kValue be the result of calling the Get internal method // of O with argument Pk. kValue = O[k]; // ii. Let testResult be the result of calling the Call internal method // of callbackfn with T as the this value and argument list // containing kValue, k, and O. var testResult = callbackfn.call(T, kValue, k, O); // iii. If ToBoolean(testResult) is false, return false. if (!testResult) { return false; } } k++; } return true; }; } ``` ## Array.fill() Pushes support back to IE9. ```js /** * Array.prototype.fill() polyfill * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill#polyfill * @license MIT */ if (!Array.prototype.fill) { Object.defineProperty(Array.prototype, 'fill', { value: function(value) { // Steps 1-2. if (this == null) { throw new TypeError('this is null or not defined'); } var O = Object(this); // Steps 3-5. var len = O.length >>> 0; // Steps 6-7. var start = arguments[1]; var relativeStart = start >> 0; // Step 8. var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); // Steps 9-10. var end = arguments[2]; var relativeEnd = end === undefined ? len : end >> 0; // Step 11. var finalValue = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); // Step 12. while (k < finalValue) { O[k] = value; k++; } // Step 13. return O; } } } ``` ## Array.filter() Pushes support back to at least IE6. ```js /** * Array.filter() polyfill */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter if (!Array.prototype.filter) Array.prototype.filter = function(func, thisArg) { 'use strict'; if ( ! ((typeof func === 'Function' || typeof func === 'function') && this) ) throw new TypeError(); var len = this.length >>> 0, res = new Array(len), // preallocate array t = this, c = 0, i = -1; if (thisArg === undefined) while (++i !== len) // checks to see if the key was set if (i in this) if (func(t[i], i, t)) res[c++] = t[i]; else while (++i !== len) // checks to see if the key was set if (i in this) if (func.call(thisArg, t[i], i, t)) res[c++] = t[i]; res.length = c; // shrink down array to proper size return res; }; ``` ## Array.find() Pushes support back to at least IE6. ```js /** * Array.prototype.find() polyfill * Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find * @author Chris Ferdinandi * @license MIT */ if (!Array.prototype.find) { Array.prototype.find = function (callback) { // 1. Let O be ? ToObject(this value). if (this == null) { throw new TypeError('"this" is null or not defined'); } var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If IsCallable(callback) is false, throw a TypeError exception. if (typeof callback !== 'function') { throw new TypeError('callback must be a function'); } // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. var thisArg = arguments[1]; // 5. Let k be 0. var k = 0; // 6. Repeat, while k < len while (k < len) { // a. Let Pk be ! ToString(k). // b. Let kValue be ? Get(O, Pk). // c. Let testResult be ToBoolean(? Call(callback, T, « kValue, k, O »)). // d. If testResult is true, return kValue. var kValue = o[k]; if (callback.call(thisArg, kValue, k, o)) { return kValue; } // e. Increase k by 1. k++; } // 7. Return undefined. return undefined; } } ``` ## Array.findIndex() Pushes support back to at least IE6. ```js /** * Array.prototype.findIndex() polyfill * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex * @license MIT */ if (!Array.prototype.findIndex) { Object.defineProperty(Array.prototype, 'findIndex', { value: function(predicate) { if (this == null) { throw new TypeError('"this" is null or not defined'); } // 1. Let O be ? ToObject(this value). var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If IsCallable(predicate) is false, throw a TypeError exception. if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. var thisArg = arguments[1]; // 5. Let k be 0. var k = 0; // 6. Repeat, while k < len while (k < len) { // a. Let Pk be ! ToString(k). // b. Let kValue be ? Get(O, Pk). // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). // d. If testResult is true, return k. var kValue = o[k]; if (predicate.call(thisArg, kValue, k, o)) { return k; } // e. Increase k by 1. k++; } // 7. Return -1. return -1; }, configurable: true, writable: true }); } ``` ## Array.flat() Pushes support back to IE9. ```js /** * Array.flat() polyfill * Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat#reduce_concat_isArray_recursivity */ if (!Array.prototype.flat) { Array.prototype.flat = function(depth) { 'use strict'; // If no depth is specified, default to 1 if (depth === undefined) { depth = 1; } // Recursively reduce sub-arrays to the specified depth var flatten = function (arr, depth) { // If depth is 0, return the array as-is if (depth < 1) { return arr.slice(); } // Otherwise, concatenate into the parent array return arr.reduce(function (acc, val) { return acc.concat(Array.isArray(val) ? flatten(val, depth - 1) : val); }, []); }; return flatten(this, depth); }; } ``` ## Array.forEach() Pushes support back to at least IE6. ```js /** * Array.prototype.forEach() polyfill * @author Chris Ferdinandi * @license MIT */ if (!Array.prototype.forEach) { Array.prototype.forEach = function (callback, thisArg) { thisArg = thisArg || window; for (var i = 0; i < this.length; i++) { callback.call(thisArg, this[i], i, this); } }; } ``` ## Array.from() Pushes support back to at least IE6. ```js /** * Array.from() polyfill */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from // Production steps of ECMA-262, Edition 6, 22.1.2.1 if (!Array.from) { Array.from = (function () { var toStr = Object.prototype.toString; var isCallable = function (fn) { return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; }; var toInteger = function (value) { var number = Number(value); if (isNaN(number)) { return 0; } if (number === 0 || !isFinite(number)) { return number; } return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); }; var maxSafeInteger = Math.pow(2, 53) - 1; var toLength = function (value) { var len = toInteger(value); return Math.min(Math.max(len, 0), maxSafeInteger); }; // The length property of the from method is 1. return function from(arrayLike/*, mapFn, thisArg */) { // 1. Let C be the this value. var C = this; // 2. Let items be ToObject(arrayLike). var items = Object(arrayLike); // 3. ReturnIfAbrupt(items). if (arrayLike == null) { throw new TypeError('Array.from requires an array-like object - not null or undefined'); } // 4. If mapfn is undefined, then let mapping be false. var mapFn = arguments.length > 1 ? arguments[1] : void undefined; var T; if (typeof mapFn !== 'undefined') { // 5. else // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. if (!isCallable(mapFn)) { throw new TypeError('Array.from: when provided, the second argument must be a function'); } // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. if (arguments.length > 2) { T = arguments[2]; } } // 10. Let lenValue be Get(items, "length"). // 11. Let len be ToLength(lenValue). var len = toLength(items.length); // 13. If IsConstructor(C) is true, then // 13. a. Let A be the result of calling the [[Construct]] internal method // of C with an argument list containing the single item len. // 14. a. Else, Let A be ArrayCreate(len). var A = isCallable(C) ? Object(new C(len)) : new Array(len); // 16. Let k be 0. var k = 0; // 17. Repeat, while k < len… (also steps a - h) var kValue; while (k < len) { kValue = items[k]; if (mapFn) { A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); } else { A[k] = kValue; } k += 1; } // 18. Let putStatus be Put(A, "length", len, true). A.length = len; // 20. Return A. return A; }; }()); } ``` ## Array.includes() Pushes support back to IE9. ```js /** * Array.prototype.includes() polyfill * @author Chris Ferdinandi * @license MIT */ if (!Array.prototype.includes) { Array.prototype.includes = function(search, start) { 'use strict'; if (search instanceof RegExp) { throw TypeError('first argument must not be a RegExp'); } if (start === undefined) { start = 0; } return this.indexOf(search, start) !== -1; }; } ``` ## Array.isArray() Pushes support back to at least IE6. ```js /** * Array.isArray() polyfill */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; } ``` ## Array.map() Pushes support back to at least IE6. ```js /** * Array.map() polyfill */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map // Production steps of ECMA-262, Edition 5, 15.4.4.19 // Reference: http://es5.github.io/#x15.4.4.19 if (!Array.prototype.map) { Array.prototype.map = function(callback/*, thisArg*/) { var T, A, k; if (this == null) { throw new TypeError('this is null or not defined'); } // 1. Let O be the result of calling ToObject passing the |this| // value as the argument. var O = Object(this); // 2. Let lenValue be the result of calling the Get internal // method of O with the argument "length". // 3. Let len be ToUint32(lenValue). var len = O.length >>> 0; // 4. If IsCallable(callback) is false, throw a TypeError exception. // See: http://es5.github.com/#x9.11 if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. if (arguments.length > 1) { T = arguments[1]; } // 6. Let A be a new array created as if by the expression new Array(len) // where Array is the standard built-in constructor with that name and // len is the value of len. A = new Array(len); // 7. Let k be 0 k = 0; // 8. Repeat, while k < len while (k < len) { var kValue, mappedValue; // a. Let Pk be ToString(k). // This is implicit for LHS operands of the in operator // b. Let kPresent be the result of calling the HasProperty internal // method of O with argument Pk. // This step can be combined with c // c. If kPresent is true, then if (k in O) { // i. Let kValue be the result of calling the Get internal // method of O with argument Pk. kValue = O[k]; // ii. Let mappedValue be the result of calling the Call internal // method of callback with T as the this value and argument // list containing kValue, k, and O. mappedValue = callback.call(T, kValue, k, O); // iii. Call the DefineOwnProperty internal method of A with arguments // Pk, Property Descriptor // { Value: mappedValue, // Writable: true, // Enumerable: true, // Configurable: true }, // and false. // In browsers that support Object.defineProperty, use the following: // Object.defineProperty(A, k, { // value: mappedValue, // writable: true, // enumerable: true, // configurable: true // }); // For best browser support, use the following: A[k] = mappedValue; } // d. Increase k by 1. k++; } // 9. return A return A; }; } ``` ## Array.reduce() Pushes support back to at least IE6. ```js /** * Array.prototype.reduce() polyfill * Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce * @author Chris Ferdinandi * @license MIT */ if (!Array.prototype.reduce) { Array.prototype.reduce = function (callback) { if (this === null) { throw new TypeError('Array.prototype.reduce called on null or undefined'); } if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } // 1. Let O be ? ToObject(this value). var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // Steps 3, 4, 5, 6, 7 var k = 0; var value; if (arguments.length >= 2) { value = arguments[1]; } else { while (k < len && !(k in o)) { k++; } // 3. If len is 0 and initialValue is not present, // throw a TypeError exception. if (k >= len) { throw new TypeError( 'Reduce of empty array ' + 'with no initial value' ); } value = o[k++]; } // 8. Repeat, while k < len while (k < len) { // a. Let Pk be ! ToString(k). // b. Let kPresent be ? HasProperty(O, Pk). // c. If kPresent is true, then // i. Let kValue be ? Get(O, Pk). // ii. Let accumulator be ? Call( // callbackfn, undefined, // « accumulator, kValue, k, O »). if (k in o) { value = callback(value, o[k], k, o); } // d. Increase k by 1. k++; } // 9. Return accumulator. return value; } } ``` ## Array.some() Pushes support back to at least IE6. ```js /** * Array.some() polyfill */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some // Production steps of ECMA-262, Edition 5, 15.4.4.17 // Reference: http://es5.github.io/#x15.4.4.17 if (!Array.prototype.some) { Array.prototype.some = function(fun/*, thisArg*/) { 'use strict'; if (this == null) { throw new TypeError('Array.prototype.some called on null or undefined'); } if (typeof fun !== 'function') { throw new TypeError(); } var t = Object(this); var len = t.length >>> 0; var thisArg = arguments.length >= 2 ? arguments[1] : void 0; for (var i = 0; i < len; i++) { if (i in t && fun.call(thisArg, t[i], i, t)) { return true; } } return false; }; } ``` ## CSS.escape() Pushes support back to at least IE6. ```js /*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */ ;(function(root, factory) { // https://github.com/umdjs/umd/blob/master/returnExports.js if (typeof exports == 'object') { // For Node.js. module.exports = factory(root); } else if (typeof define == 'function' && define.amd) { // For AMD. Register as an anonymous module. define([], factory.bind(root, root)); } else { // For browser globals (not exposing the function separately). factory(root); } }(typeof global != 'undefined' ? global : this, function(root) { if (root.CSS && root.CSS.escape) { return root.CSS.escape; } // https://drafts.csswg.org/cssom/#serialize-an-identifier var cssEscape = function(value) { if (arguments.length == 0) { throw new TypeError('`CSS.escape` requires an argument.'); } var string = String(value); var length = string.length; var index = -1; var codeUnit; var result = ''; var firstCodeUnit = string.charCodeAt(0); while (++index < length) { codeUnit = string.charCodeAt(index); // Note: there’s no need to special-case astral symbols, surrogate // pairs, or lone surrogates. // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER // (U+FFFD). if (codeUnit == 0x0000) { result += '\uFFFD'; continue; } if ( // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is // U+007F, […] (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F || // If the character is the first character and is in the range [0-9] // (U+0030 to U+0039), […] (index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || // If the character is the second character and is in the range [0-9] // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] ( index == 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && firstCodeUnit == 0x002D ) ) { // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point result += '\\' + codeUnit.toString(16) + ' '; continue; } if ( // If the character is the first character and is a `-` (U+002D), and // there is no second character, […] index == 0 && length == 1 && codeUnit == 0x002D ) { result += '\\' + string.charAt(index); continue; } // If the character is not handled by one of the above rules and is // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to // U+005A), or [a-z] (U+0061 to U+007A), […] if ( codeUnit >= 0x0080 || codeUnit == 0x002D || codeUnit == 0x005F || codeUnit >= 0x0030 && codeUnit <= 0x0039 || codeUnit >= 0x0041 && codeUnit <= 0x005A || codeUnit >= 0x0061 && codeUnit <= 0x007A ) { // the character itself result += string.charAt(index); continue; } // Otherwise, the escaped character. // https://drafts.csswg.org/cssom/#escape-a-character result += '\\' + string.charAt(index); } return result; }; if (!root.CSS) { root.CSS = {}; } root.CSS.escape = cssEscape; return cssEscape; })); ``` ## Element.addEventListener() Pushes support back to at least IE6. ```js /** * Mashup from https://github.com/nbouvrette/eventListenerPolyfill * * @license addEventListener polyfill 1.0 / Eirik Backer / MIT Licence * https://gist.github.com/2864711/946225eb3822c203e8d6218095d888aac5e1748e * * sounisi5011 version: * http://qiita.com/sounisi5011/items/a8fc80e075e4f767b79a#11 */ (function (window, document, listeners_prop_name) { if ((!window.addEventListener || !window.removeEventListener) && window.attachEvent && window.detachEvent) { /** * @param {*} value * @return {boolean} */ var is_callable = function (value) { return typeof value === 'function'; }; /** * @param {!Window|HTMLDocument|Node} self * @param {EventListener|function(!Event):(boolean|undefined)} listener * @return {!function(Event)|undefined} */ var listener_get = function (self, listener) { var listeners = listener[listeners_prop_name]; if (listeners) { var lis; var i = listeners.length; while (i--) { lis = listeners[i]; if (lis[0] === self) { return lis[1]; } } } }; /** * @param {!Window|HTMLDocument|Node} self * @param {EventListener|function(!Event):(boolean|undefined)} listener * @param {!function(Event)} callback * @return {!function(Event)} */ var listener_set = function (self, listener, callback) { var listeners = listener[listeners_prop_name] || (listener[listeners_prop_name] = []); return listener_get(self, listener) || (listeners[listeners.length] = [self, callback], callback); }; /** * @param {string} methodName */ var docHijack = function (methodName) { var old = document[methodName]; document[methodName] = function (v) { return addListen(old(v)); }; }; /** * @this {!Window|HTMLDocument|Node} * @param {string} type * @param {EventListener|function(!Event):(boolean|undefined)} listener * @param {boolean=} useCapture */ var addEvent = function (type, listener, useCapture) { if (is_callable(listener)) { var self = this; self.attachEvent( 'on' + type, listener_set(self, listener, function (e) { e = e || window.event; e.preventDefault = e.preventDefault || function () { e.returnValue = false }; e.stopPropagation = e.stopPropagation || function () { e.cancelBubble = true }; e.target = e.target || e.srcElement || document.documentElement; e.currentTarget = e.currentTarget || self; e.timeStamp = e.timeStamp || (new Date()).getTime(); listener.call(self, e); }) ); } }; /** * @this {!Window|HTMLDocument|Node} * @param {string} type * @param {EventListener|function(!Event):(boolean|undefined)} listener * @param {boolean=} useCapture */ var removeEvent = function (type, listener, useCapture) { if (is_callable(listener)) { var self = this; var lis = listener_get(self, listener); if (lis) { self.detachEvent('on' + type, lis); } } }; /** * @param {!Node|NodeList|Array} obj * @return {!Node|NodeList|Array} */ var addListen = function (obj) { var i = obj.length; if (i) { while (i--) { obj[i].addEventListener = addEvent; obj[i].removeEventListener = removeEvent; } } else { obj.addEventListener = addEvent; obj.removeEventListener = removeEvent; } return obj; }; addListen([document, window]); if ('Element' in window) { /** * IE8 */ var element = window.Element; element.prototype.addEventListener = addEvent; element.prototype.removeEventListener = removeEvent; } else { /** * IE < 8 */ //Make sure we also init at domReady document.attachEvent('onreadystatechange', function () { addListen(document.all) }); docHijack('getElementsByTagName'); docHijack('getElementById'); docHijack('createElement'); addListen(document.all); } } })(window, document, 'x-ms-event-listeners'); ``` ## Element.after() Pushes support back to at least IE6. ```js /** * ChildNode.after() polyfill * Adapted from https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/after()/after().md * @author Chris Ferdinandi * @license MIT */ (function (elem) { // Check if element is a node // https://github.com/Financial-Times/polyfill-service var isNode = function (object) { // DOM, Level2 if (typeof Node === 'function') { return object instanceof Node; } // Older browsers, check if it looks like a Node instance) return object && typeof object === "object" && object.nodeName && object.nodeType >= 1 && object.nodeType <= 12; }; // Add after() method to prototype for (var i = 0; i < elem.length; i++) { if (!window[elem[i]] || 'after' in window[elem[i]].prototype) continue; window[elem[i]].prototype.after = function () { var argArr = Array.prototype.slice.call(arguments); var docFrag = document.createDocumentFragment(); for (var n = 0; n < argArr.length; n++) { docFrag.appendChild(isNode(argArr[n]) ? argArr[n] : document.createTextNode(String(argArr[n]))); } this.parentNode.insertBefore(docFrag, this.nextSibling); }; } })(['Element', 'CharacterData', 'DocumentType']); ``` ## Element.append() Pushes support back to at IE9. ```js /** * ChildNode.append() polyfill * https://gomakethings.com/adding-an-element-to-the-end-of-a-set-of-elements-with-vanilla-javascript/ * @author Chris Ferdinandi * @license MIT */ (function (elem) { // Check if element is a node // https://github.com/Financial-Times/polyfill-service var isNode = function (object) { // DOM, Level2 if (typeof Node === 'function') { return object instanceof Node; } // Older browsers, check if it looks like a Node instance) return object && typeof object === "object" && object.nodeName && object.nodeType >= 1 && object.nodeType <= 12; }; // Add append() method to prototype for (var i = 0; i < elem.length; i++) { if (!window[elem[i]] || 'append' in window[elem[i]].prototype) continue; window[elem[i]].prototype.append = function () { var argArr = Array.prototype.slice.call(arguments); var docFrag = document.createDocumentFragment(); for (var n = 0; n < argArr.length; n++) { docFrag.appendChild(isNode(argArr[n]) ? argArr[n] : document.createTextNode(String(argArr[n]))); } this.appendChild(docFrag); }; } })(['Element', 'CharacterData', 'DocumentType']); ``` ## Element.before() Pushes support back to at least IE6. ```js /** * ChildNode.before() polyfill * https://gomakethings.com/how-to-insert-an-element-before-another-one-in-the-dom-with-vanilla-javascript/ * @author Chris Ferdinandi * @license MIT */ (function (elem) { // Check if element is a node // https://github.com/Financial-Times/polyfill-service var isNode = function (object) { // DOM, Level2 if (typeof Node === 'function') { return object instanceof Node; } // Older browsers, check if it looks like a Node instance) return object && typeof object === "object" && object.nodeName && object.nodeType >= 1 && object.nodeType <= 12; }; // Add before() method to prototype for (var i = 0; i < elem.length; i++) { if (!window[elem[i]] || 'before' in window[elem[i]].prototype) continue; window[elem[i]].prototype.before = function () { var argArr = Array.prototype.slice.call(arguments); var docFrag = document.createDocumentFragment(); for (var n = 0; n < argArr.length; n++) { docFrag.appendChild(isNode(argArr[n]) ? argArr[n] : document.createTextNode(String(argArr[n]))); } this.parentNode.insertBefore(docFrag, this); }; } })(['Element', 'CharacterData', 'DocumentType']); ``` ## Element.classList Pushes support back to IE8. ```js /* * classList.js: Cross-browser full element.classList implementation. * 1.1.20170427 * * By Eli Grey, http://eligrey.com * License: Dedicated to the public domain. * See https://github.com/eligrey/classList.js/blob/master/LICENSE.md */ /*global self, document, DOMException */ /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */ if ("document" in self) { // Full polyfill for browsers with no classList support // Including IE < Edge missing SVGElement.classList if (!("classList" in document.createElement("_")) || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg", "g"))) { (function (view) { "use strict"; if (!('Element' in view)) return; var classListProp = "classList" , protoProp = "prototype" , elemCtrProto = view.Element[protoProp] , objCtr = Object , strTrim = String[protoProp].trim || function () { return this.replace(/^\s+|\s+$/g, ""); } , arrIndexOf = Array[protoProp].indexOf || function (item) { var i = 0 , len = this.length ; for (; i < len; i++) { if (i in this && this[i] === item) { return i; } } return -1; } // Vendors: please allow content code to instantiate DOMExceptions , DOMEx = function (type, message) { this.name = type; this.code = DOMException[type]; this.message = message; } , checkTokenAndGetIndex = function (classList, token) { if (token === "") { throw new DOMEx( "SYNTAX_ERR" , "An invalid or illegal string was specified" ); } if (/\s/.test(token)) { throw new DOMEx( "INVALID_CHARACTER_ERR" , "String contains an invalid character" ); } return arrIndexOf.call(classList, token); } , ClassList = function (elem) { var trimmedClasses = strTrim.call(elem.getAttribute("class") || "") , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] , i = 0 , len = classes.length ; for (; i < len; i++) { this.push(classes[i]); } this._updateClassName = function () { elem.setAttribute("class", this.toString()); }; } , classListProto = ClassList[protoProp] = [] , classListGetter = function () { return new ClassList(this); } ; // Most DOMException implementations don't allow calling DOMException's toString() // on non-DOMExceptions. Error's toString() is sufficient here. DOMEx[protoProp] = Error[protoProp]; classListProto.item = function (i) { return this[i] || null; }; classListProto.contains = function (token) { token += ""; return checkTokenAndGetIndex(this, token) !== -1; }; classListProto.add = function () { var tokens = arguments , i = 0 , l = tokens.length , token , updated = false ; do { token = tokens[i] + ""; if (checkTokenAndGetIndex(this, token) === -1) { this.push(token); updated = true; } } while (++i < l); if (updated) { this._updateClassName(); } }; classListProto.remove = function () { var tokens = arguments , i = 0 , l = tokens.length , token , updated = false , index ; do { token = tokens[i] + ""; index = checkTokenAndGetIndex(this, token); while (index !== -1) { this.splice(index, 1); updated = true; index = checkTokenAndGetIndex(this, token); } } while (++i < l); if (updated) { this._updateClassName(); } }; classListProto.toggle = function (token, force) { token += ""; var result = this.contains(token) , method = result ? force !== true && "remove" : force !== false && "add" ; if (method) { this[method](token); } if (force === true || force === false) { return force; } else { return !result; } }; classListProto.toString = function () { return this.join(" "); }; if (objCtr.defineProperty) { var classListPropDesc = { get: classListGetter , enumerable: true , configurable: true }; try { objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); } catch (ex) { // IE 8 doesn't support enumerable:true // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected if (ex.number === undefined || ex.number === -0x7FF5EC54) { classListPropDesc.enumerable = false; objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); } } } else if (objCtr[protoProp].__defineGetter__) { elemCtrProto.__defineGetter__(classListProp, classListGetter); } }(self)); } // There is full or partial native classList support, so just check if we need // to normalize the add/remove and toggle APIs. (function () { "use strict"; var testElement = document.createElement("_"); testElement.classList.add("c1", "c2"); // Polyfill for IE 10/11 and Firefox <26, where classList.add and // classList.remove exist but support only one argument at a time. if (!testElement.classList.contains("c2")) { var createMethod = function (method) { var original = DOMTokenList.prototype[method]; DOMTokenList.prototype[method] = function (token) { var i, len = arguments.length; for (i = 0; i < len; i++) { token = arguments[i]; original.call(this, token); } }; }; createMethod('add'); createMethod('remove'); } testElement.classList.toggle("c3", false); // Polyfill for IE 10 and Firefox <24, where classList.toggle does not // support the second argument. if (testElement.classList.contains("c3")) { var _toggle = DOMTokenList.prototype.toggle; DOMTokenList.prototype.toggle = function (token, force) { if (1 in arguments && !this.contains(token) === !force) { return force; } else { return _toggle.call(this, token); } }; } testElement = null; }()); } ``` ## Element.closest() Pushes support back to IE9. ```js /** * Element.closest() polyfill * https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill */ if (!Element.prototype.closest) { if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; } Element.prototype.closest = function (s) { var el = this; var ancestor = this; if (!document.documentElement.contains(el)) return null; do { if (ancestor.matches(s)) return ancestor; ancestor = ancestor.parentElement; } while (ancestor !== null); return null; }; } ``` ## Element.matches() Pushes support back to IE9. ```js /** * Element.matches() polyfill (simple version) * https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill */ if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; } ``` ## Element.matches() (Back to IE8) This version pushes is support back to IE8, but [the IE9 and up version](https://vanillajstoolkit.com/polyfills/matches/) is much better for performance. ```js /** * Element.matches() polyfill (IE8 support) * https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill */ if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function (s) { var matches = (this.document || this.ownerDocument).querySelectorAll(s), i = matches.length; while (--i >= 0 && matches.item(i) !== this) { } return i > -1; }; } ``` ## Element.prepend() Pushes support back to IE9. ```js /** * ChildNode.prepend() polyfill * https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md */ (function (arr) { arr.forEach(function (item) { if (item.hasOwnProperty('append')) { return; } Object.defineProperty(item, 'append', { configurable: true, enumerable: true, writable: true, value: function append() { var argArr = Array.prototype.slice.call(arguments), docFrag = document.createDocumentFragment(); argArr.forEach(function (argItem) { var isNode = argItem instanceof Node; docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem))); }); this.appendChild(docFrag); } }); }); })([Element.prototype, Document.prototype, DocumentFragment.prototype]); ``` ## Element.remove() Pushes support back to IE6. ```js /** * ChildNode.remove() polyfill * https://gomakethings.com/removing-an-element-from-the-dom-the-es6-way/ * @author Chris Ferdinandi * @license MIT */ (function (elem) { for (var i = 0; i < elem.length; i++) { if (!window[elem[i]] || 'remove' in window[elem[i]].prototype) continue; window[elem[i]].prototype.remove = function () { this.parentNode.removeChild(this); }; } })(['Element', 'CharacterData', 'DocumentType']); ``` ## Element.requestFullscreen() Pushes support back to IE11. ```js /** * Element.requestFullScreen() polyfill * @author Chris Ferdinandi * @license MIT */ if (!Element.prototype.requestFullscreen) { Element.prototype.requestFullscreen = Element.prototype.mozRequestFullscreen || Element.prototype.webkitRequestFullscreen || Element.prototype.msRequestFullscreen; } ``` ## NodeList.forEach() Pushes support back to at least IE6. ```js /** * NodeList.prototype.forEach() polyfill * https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill */ if (window.NodeList && !NodeList.prototype.forEach) { NodeList.prototype.forEach = function (callback, thisArg) { thisArg = thisArg || window; for (var i = 0; i < this.length; i++) { callback.call(thisArg, this[i], i, this); } }; } ``` ## Object.assign() Pushes support back to IE9. ```js /** * Object.assign() polyfill */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign if (typeof Object.assign != 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, "assign", { value: function assign(target, varArgs) { // .length of function is 2 'use strict'; if (target == null) { // TypeError if undefined or null throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource != null) { // Skip over if undefined or null for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true }); } ``` ## Object.entries() Pushes support back to at least IE6. ```js /** * Object.entries() polyfill * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries */ if (!Object.entries) { Object.entries = function (obj){ var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array while (i--) resArray[i] = [ownProps[i], obj[ownProps[i]]]; return resArray; }; } ``` ## Object.fromEntries() Adds support to all modern browsers that support the `for...of` loop. ```js /** * Object.entriesFrom() polyfill * @author Chris Ferdinandi * @license MIT */ if (!Object.fromEntries) { Object.fromEntries = function (entries){ if (!entries || !entries[Symbol.iterator]) { throw new Error('Object.fromEntries() requires a single iterable argument'); } let obj = {}; for (let [key, value] of entries) { obj[key] = value; } return obj; }; } ``` ## Object.keys() Pushes support back to at least IE7. ```js /** * Object.keys() polyfill */ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys if (!Object.keys) { Object.keys = (function () { 'use strict'; var hasOwnProperty = Object.prototype.hasOwnProperty, hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), dontEnums = [ 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor' ], dontEnumsLength = dontEnums.length; return function (obj) { if (typeof obj !== 'function' && (typeof obj !== 'object' || obj === null)) { throw new TypeError('Object.keys called on non-object'); } var result = [], prop, i; for (prop in obj) { if (hasOwnProperty.call(obj, prop)) { result.push(prop); } } if (hasDontEnumBug) { for (i = 0; i < dontEnumsLength; i++) { if (hasOwnProperty.call(obj, dontEnums[i])) { result.push(dontEnums[i]); } } } return result; }; }()); } ``` ## Promise() Pushes support back to IE9. ```js /** * Promise Polyfill * https://github.com/taylorhakes/promise-polyfill * (c) Taylor Hakes, MIT License */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory() : typeof define === 'function' && define.amd ? define(factory) : (factory()); }(this, (function () { 'use strict'; /** * @this {Promise} */ function finallyConstructor(callback) { var constructor = this.constructor; return this.then( function(value) { // @ts-ignore return constructor.resolve(callback()).then(function() { return value; }); }, function(reason) { // @ts-ignore return constructor.resolve(callback()).then(function() { // @ts-ignore return constructor.reject(reason); }); } ); } // Store setTimeout reference so promise-polyfill will be unaffected by // other code modifying setTimeout (like sinon.useFakeTimers()) var setTimeoutFunc = setTimeout; function isArray(x) { return Boolean(x && typeof x.length !== 'undefined'); } function noop() {} // Polyfill for Function.prototype.bind function bind(fn, thisArg) { return function() { fn.apply(thisArg, arguments); }; } /** * @constructor * @param {Function} fn */ function Promise(fn) { if (!(this instanceof Promise)) throw new TypeError('Promises must be constructed via new'); if (typeof fn !== 'function') throw new TypeError('not a function'); /** @type {!number} */ this._state = 0; /** @type {!boolean} */ this._handled = false; /** @type {Promise|undefined} */ this._value = undefined; /** @type {!Array<!Function>} */ this._deferreds = []; doResolve(fn, this); } function handle(self, deferred) { while (self._state === 3) { self = self._value; } if (self._state === 0) { self._deferreds.push(deferred); return; } self._handled = true; Promise._immediateFn(function() { var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { (self._state === 1 ? resolve : reject)(deferred.promise, self._value); return; } var ret; try { ret = cb(self._value); } catch (e) { reject(deferred.promise, e); return; } resolve(deferred.promise, ret); }); } function resolve(self, newValue) { try { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.'); if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { var then = newValue.then; if (newValue instanceof Promise) { self._state = 3; self._value = newValue; finale(self); return; } else if (typeof then === 'function') { doResolve(bind(then, newValue), self); return; } } self._state = 1; self._value = newValue; finale(self); } catch (e) { reject(self, e); } } function reject(self, newValue) { self._state = 2; self._value = newValue; finale(self); } function finale(self) { if (self._state === 2 && self._deferreds.length === 0) { Promise._immediateFn(function() { if (!self._handled) { Promise._unhandledRejectionFn(self._value); } }); } for (var i = 0, len = self._deferreds.length; i < len; i++) { handle(self, self._deferreds[i]); } self._deferreds = null; } /** * @constructor */ function Handler(onFulfilled, onRejected, promise) { this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.promise = promise; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, self) { var done = false; try { fn( function(value) { if (done) return; done = true; resolve(self, value); }, function(reason) { if (done) return; done = true; reject(self, reason); } ); } catch (ex) { if (done) return; done = true; reject(self, ex); } } Promise.prototype['catch'] = function(onRejected) { return this.then(null, onRejected); }; Promise.prototype.then = function(onFulfilled, onRejected) { // @ts-ignore var prom = new this.constructor(noop); handle(this, new Handler(onFulfilled, onRejected, prom)); return prom; }; Promise.prototype['finally'] = finallyConstructor; Promise.all = function(arr) { return new Promise(function(resolve, reject) { if (!isArray(arr)) { return reject(new TypeError('Promise.all accepts an array')); } var args = Array.prototype.slice.call(arr); if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { try { if (val && (typeof val === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call( val, function(val) { res(i, val); }, reject ); return; } } args[i] = val; if (--remaining === 0) { resolve(args); } } catch (ex) { reject(ex); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }; Promise.resolve = function(value) { if (value && typeof value === 'object' && value.constructor === Promise) { return value; } return new Promise(function(resolve) { resolve(value); }); }; Promise.reject = function(value) { return new Promise(function(resolve, reject) { reject(value); }); }; Promise.race = function(arr) { return new Promise(function(resolve, reject) { if (!isArray(arr)) { return reject(new TypeError('Promise.race accepts an array')); } for (var i = 0, len = arr.length; i < len; i++) { Promise.resolve(arr[i]).then(resolve, reject); } }); }; // Use polyfill for setImmediate for performance gains Promise._immediateFn = // @ts-ignore (typeof setImmediate === 'function' && function(fn) { // @ts-ignore setImmediate(fn); }) || function(fn) { setTimeoutFunc(fn, 0); }; Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { if (typeof console !== 'undefined' && console) { console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console } }; /** @suppress {undefinedVars} */ var globalNS = (function() { // the only reliable means to get the global object is // `Function('return this')()` // However, this causes CSP violations in Chrome apps. if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } throw new Error('unable to locate global object'); })(); if (!('Promise' in globalNS)) { globalNS['Promise'] = Promise; } else if (!globalNS.Promise.prototype['finally']) { globalNS.Promise.prototype['finally'] = finallyConstructor; } }))); ``` ## String.endsWith() Pushes support back to at least IE6. ```js /** * String.prototype.endsWith() polyfill * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith#Polyfill */ if (!String.prototype.endsWith) { String.prototype.endsWith = function(searchStr, Position) { // This works much better than >= because // it compensates for NaN: if (!(Position < this.length)) { Position = this.length; } else { Position |= 0; // round position } return this.substr(Position - searchStr.length, searchStr.length) === searchStr; }; } ``` ## String.includes() Pushes support back to at least IE6. ```js /** * String.prototype.includes() polyfill * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill */ if (!String.prototype.includes) { String.prototype.includes = function(search, start) { 'use strict'; if (search instanceof RegExp) { throw TypeError('first argument must not be a RegExp'); } if (start === undefined) { start = 0; } return this.indexOf(search, start) !== -1; }; } ``` ## String.padEnd() Pushes support back to at least IE6. ```js /** * String.prototype.padStart() polyfill * https://github.com/uxitten/polyfill/blob/master/string.polyfill.js * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd */ if (!String.prototype.padEnd) { String.prototype.padEnd = function padEnd(targetLength,padString) { targetLength = targetLength>>0; //floor if number or convert non-number to 0; padString = String((typeof padString !== 'undefined' ? padString : ' ')); if (this.length > targetLength) { return String(this); } else { targetLength = targetLength-this.length; if (targetLength > padString.length) { padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed } return String(this) + padString.slice(0,targetLength); } }; } ``` ## String.padStart() Pushes support back to at least IE6. ```js /** * String.prototype.padStart() polyfill * https://github.com/uxitten/polyfill/blob/master/string.polyfill.js * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart */ if (!String.prototype.padStart) { String.prototype.padStart = function padStart(targetLength,padString) { targetLength = targetLength>>0; //truncate if number or convert non-number to 0; padString = String((typeof padString !== 'undefined' ? padString : ' ')); if (this.length > targetLength) { return String(this); } else { targetLength = targetLength-this.length; if (targetLength > padString.length) { padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed } return padString.slice(0,targetLength) + String(this); } }; } ``` ## String.repeat() Pushes support back to IE9. ```js /** * String.prototype.repeat() polyfill https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat#Polyfill */ if (!String.prototype.repeat) { String.prototype.repeat = function(count) { 'use strict'; if (this == null) throw new TypeError('can\'t convert ' + this + ' to object'); var str = '' + this; // To convert string to integer. count = +count; // Check NaN if (count != count) count = 0; if (count < 0) throw new RangeError('repeat count must be non-negative'); if (count == Infinity) throw new RangeError('repeat count must be less than infinity'); count = Math.floor(count); if (str.length == 0 || count == 0) return ''; // Ensuring count is a 31-bit integer allows us to heavily optimize the // main part. But anyway, most current (August 2014) browsers can't handle // strings 1 << 28 chars or longer, so: if (str.length * count >= 1 << 28) throw new RangeError('repeat count must not overflow maximum string size'); var maxCount = str.length * count; count = Math.floor(Math.log(count) / Math.log(2)); while (count) { str += str; count--; } str += str.substring(0, maxCount - str.length); return str; } } ``` ## String.startsWith() Pushes support back to at least IE6. ```js /** * String.prototype.startsWith() polyfill */ if (!String.prototype.startsWith) { String.prototype.startsWith = function(search, pos){ return this.slice(pos || 0, search.length) === search; }; } ``` ## String.trim() Pushes support back to at least IE6. ```js /** * String.prototype.trim() polyfill * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill */ if (!String.prototype.trim) { String.prototype.trim = function () { return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); }; } ``` ## String.trimEnd() Pushes support back to IE9. ```js /** * String.prototype.trimEnd() polyfill * Adapted from polyfill.io */ if (!String.prototype.trimEnd) { String.prototype.trimEnd = function () { return this.replace(new RegExp(/[\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF]+/.source + '$', 'g'), ''); }; } ``` ## String.trimStart() Pushes support back to IE9. ```js /** * String.prototype.trimStart() polyfill * Adapted from polyfill.io */ if (!String.prototype.trimStart) { String.prototype.trimStart = function () { return this.replace(new RegExp('^' + /[\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF]+/.source, 'g'), ''); }; } ``` ## URL Pushes support back to IE9. ```js /** * URL Polyfill * Draft specification: https://url.spec.whatwg.org * https://polyfill.io/ */ (function (global) { 'use strict'; function isSequence(o) { if (!o) return false; if ('Symbol' in global && 'iterator' in global.Symbol && typeof o[Symbol.iterator] === 'function') return true; if (Array.isArray(o)) return true; return false; } function toArray(iter) { return ('from' in Array) ? Array.from(iter) : Array.prototype.slice.call(iter); } (function() { // Browsers may have: // * No global URL object // * URL with static methods only - may have a dummy constructor // * URL with members except searchParams // * Full URL API support var origURL = global.URL; var nativeURL; try { if (origURL) { nativeURL = new global.URL('http://example.com'); if ('searchParams' in nativeURL) { var url = new URL('http://example.com'); url.search = 'a=1&b=2'; if (url.href === 'http://example.com/?a=1&b=2') { url.search = ''; if (url.href === 'http://example.com/') { return; } } } if (!('href' in nativeURL)) { nativeURL = undefined; } nativeURL = undefined; } // eslint-disable-next-line no-empty } catch (_) {} // NOTE: Doesn't do the encoding/decoding dance function urlencoded_serialize(pairs) { var output = '', first = true; pairs.forEach(function (pair) { var name = encodeURIComponent(pair.name); var value = encodeURIComponent(pair.value); if (!first) output += '&'; output += name + '=' + value; first = false; }); return output.replace(/%20/g, '+'); } // NOTE: Doesn't do the encoding/decoding dance function urlencoded_parse(input, isindex) { var sequences = input.split('&'); if (isindex && sequences[0].indexOf('=') === -1) sequences[0] = '=' + sequences[0]; var pairs = []; sequences.forEach(function (bytes) { if (bytes.length === 0) return; var index = bytes.indexOf('='); if (index !== -1) { var name = bytes.substring(0, index); var value = bytes.substring(index + 1); } else { name = bytes; value = ''; } name = name.replace(/\+/g, ' '); value = value.replace(/\+/g, ' '); pairs.push({ name: name, value: value }); }); var output = []; pairs.forEach(function (pair) { output.push({ name: decodeURIComponent(pair.name), value: decodeURIComponent(pair.value) }); }); return output; } function URLUtils(url) { if (nativeURL) return new origURL(url); var anchor = document.createElement('a'); anchor.href = url; return anchor; } function URLSearchParams(init) { var $this = this; this._list = []; if (init === undefined || init === null) { // no-op } else if (init instanceof URLSearchParams) { // In ES6 init would be a sequence, but special case for ES5. this._list = urlencoded_parse(String(init)); } else if (typeof init === 'object' && isSequence(init)) { toArray(init).forEach(function(e) { if (!isSequence(e)) throw TypeError(); var nv = toArray(e); if (nv.length !== 2) throw TypeError(); $this._list.push({name: String(nv[0]), value: String(nv[1])}); }); } else if (typeof init === 'object' && init) { Object.keys(init).forEach(function(key) { $this._list.push({name: String(key), value: String(init[key])}); }); } else { init = String(init); if (init.substring(0, 1) === '?') init = init.substring(1); this._list = urlencoded_parse(init); } this._url_object = null; this._setList = function (list) { if (!updating) $this._list = list; }; var updating = false; this._update_steps = function() { if (updating) return; updating = true; if (!$this._url_object) return; // Partial workaround for IE issue with 'about:' if ($this._url_object.protocol === 'about:' && $this._url_object.pathname.indexOf('?') !== -1) { $this._url_object.pathname = $this._url_object.pathname.split('?')[0]; } $this._url_object.search = urlencoded_serialize($this._list); updating = false; }; } Object.defineProperties(URLSearchParams.prototype, { append: { value: function (name, value) { this._list.push({ name: name, value: value }); this._update_steps(); }, writable: true, enumerable: true, configurable: true }, 'delete': { value: function (name) { for (var i = 0; i < this._list.length;) { if (this._list[i].name === name) this._list.splice(i, 1); else ++i; } this._update_steps(); }, writable: true, enumerable: true, configurable: true }, get: { value: function (name) { for (var i = 0; i < this._list.length; ++i) { if (this._list[i].name === name) return this._list[i].value; } return null; }, writable: true, enumerable: true, configurable: true }, getAll: { value: function (name) { var result = []; for (var i = 0; i < this._list.length; ++i) { if (this._list[i].name === name) result.push(this._list[i].value); } return result; }, writable: true, enumerable: true, configurable: true }, has: { value: function (name) { for (var i = 0; i < this._list.length; ++i) { if (this._list[i].name === name) return true; } return false; }, writable: true, enumerable: true, configurable: true }, set: { value: function (name, value) { var found = false; for (var i = 0; i < this._list.length;) { if (this._list[i].name === name) { if (!found) { this._list[i].value = value; found = true; ++i; } else { this._list.splice(i, 1); } } else { ++i; } } if (!found) this._list.push({ name: name, value: value }); this._update_steps(); }, writable: true, enumerable: true, configurable: true }, entries: { value: function() { return new Iterator(this._list, 'key+value'); }, writable: true, enumerable: true, configurable: true }, keys: { value: function() { return new Iterator(this._list, 'key'); }, writable: true, enumerable: true, configurable: true }, values: { value: function() { return new Iterator(this._list, 'value'); }, writable: true, enumerable: true, configurable: true }, forEach: { value: function(callback) { var thisArg = (arguments.length > 1) ? arguments[1] : undefined; this._list.forEach(function(pair) { callback.call(thisArg, pair.value, pair.name); }); }, writable: true, enumerable: true, configurable: true }, toString: { value: function () { return urlencoded_serialize(this._list); }, writable: true, enumerable: false, configurable: true } }); function Iterator(source, kind) { var index = 0; this.next = function() { if (index >= source.length) return {done: true, value: undefined}; var pair = source[index++]; return {done: false, value: kind === 'key' ? pair.name : kind === 'value' ? pair.value : [pair.name, pair.value]}; }; } if ('Symbol' in global && 'iterator' in global.Symbol) { Object.defineProperty(URLSearchParams.prototype, global.Symbol.iterator, { value: URLSearchParams.prototype.entries, writable: true, enumerable: true, configurable: true}); Object.defineProperty(Iterator.prototype, global.Symbol.iterator, { value: function() { return this; }, writable: true, enumerable: true, configurable: true}); } function URL(url, base) { if (!(this instanceof global.URL)) throw new TypeError("Failed to construct 'URL': Please use the 'new' operator."); if (base) { url = (function () { if (nativeURL) return new origURL(url, base).href; var iframe; try { var doc; // Use another document/base tag/anchor for relative URL resolution, if possible if (Object.prototype.toString.call(window.operamini) === "[object OperaMini]") { iframe = document.createElement('iframe'); iframe.style.display = 'none'; document.documentElement.appendChild(iframe); doc = iframe.contentWindow.document; } else if (document.implementation && document.implementation.createHTMLDocument) { doc = document.implementation.createHTMLDocument(''); } else if (document.implementation && document.implementation.createDocument) { doc = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null); doc.documentElement.appendChild(doc.createElement('head')); doc.documentElement.appendChild(doc.createElement('body')); } else if (window.ActiveXObject) { doc = new window.ActiveXObject('htmlfile'); doc.write('<head></head><body></body>'); doc.close(); } if (!doc) throw Error('base not supported'); var baseTag = doc.createElement('base'); baseTag.href = base; doc.getElementsByTagName('head')[0].appendChild(baseTag); var anchor = doc.createElement('a'); anchor.href = url; return anchor.href; } finally { if (iframe) iframe.parentNode.removeChild(iframe); } }()); } // An inner object implementing URLUtils (either a native URL // object or an HTMLAnchorElement instance) is used to perform the // URL algorithms. With full ES5 getter/setter support, return a // regular object For IE8's limited getter/setter support, a // different HTMLAnchorElement is returned with properties // overridden var instance = URLUtils(url || ''); // Detect for ES5 getter/setter support // (an Object.defineProperties polyfill that doesn't support getters/setters may throw) var ES5_GET_SET = (function() { if (!('defineProperties' in Object)) return false; try { var obj = {}; Object.defineProperties(obj, { prop: { get: function () { return true; } } }); return obj.prop; } catch (_) { return false; } }()); var self = ES5_GET_SET ? this : document.createElement('a'); var query_object = new URLSearchParams( instance.search ? instance.search.substring(1) : null); query_object._url_object = self; Object.defineProperties(self, { href: { get: function () { return instance.href; }, set: function (v) { instance.href = v; tidy_instance(); update_steps(); }, enumerable: true, configurable: true }, origin: { get: function () { if ('origin' in instance) return instance.origin; return this.protocol + '//' + this.host; }, enumerable: true, configurable: true }, protocol: { get: function () { return instance.protocol; }, set: function (v) { instance.protocol = v; }, enumerable: true, configurable: true }, username: { get: function () { return instance.username; }, set: function (v) { instance.username = v; }, enumerable: true, configurable: true }, password: { get: function () { return instance.password; }, set: function (v) { instance.password = v; }, enumerable: true, configurable: true }, host: { get: function () { // IE returns default port in |host| var re = {'http:': /:80$/, 'https:': /:443$/, 'ftp:': /:21$/}[instance.protocol]; return re ? instance.host.replace(re, '') : instance.host; }, set: function (v) { instance.host = v; }, enumerable: true, configurable: true }, hostname: { get: function () { return instance.hostname; }, set: function (v) { instance.hostname = v; }, enumerable: true, configurable: true }, port: { get: function () { return instance.port; }, set: function (v) { instance.port = v; }, enumerable: true, configurable: true }, pathname: { get: function () { // IE does not include leading '/' in |pathname| if (instance.pathname.charAt(0) !== '/') return '/' + instance.pathname; return instance.pathname; }, set: function (v) { instance.pathname = v; }, enumerable: true, configurable: true }, search: { get: function () { return instance.search; }, set: function (v) { if (instance.search === v) return; instance.search = v; tidy_instance(); update_steps(); }, enumerable: true, configurable: true }, searchParams: { get: function () { return query_object; }, enumerable: true, configurable: true }, hash: { get: function () { return instance.hash; }, set: function (v) { instance.hash = v; tidy_instance(); }, enumerable: true, configurable: true }, toString: { value: function() { return instance.toString(); }, enumerable: false, configurable: true }, valueOf: { value: function() { return instance.valueOf(); }, enumerable: false, configurable: true } }); function tidy_instance() { var href = instance.href.replace(/#$|\?$|\?(?=#)/g, ''); if (instance.href !== href) instance.href = href; } function update_steps() { query_object._setList(instance.search ? urlencoded_parse(instance.search.substring(1)) : []); query_object._update_steps(); } return self; } if (origURL) { for (var i in origURL) { if (Object.prototype.hasOwnProperty.call(origURL, i) && typeof origURL[i] === 'function') URL[i] = origURL[i]; } } global.URL = URL; global.URLSearchParams = URLSearchParams; }()); // Patch native URLSearchParams constructor to handle sequences/records // if necessary. (function() { if (new global.URLSearchParams([['a', 1]]).get('a') === '1' && new global.URLSearchParams({a: 1}).get('a') === '1') return; var orig = global.URLSearchParams; global.URLSearchParams = function(init) { if (init && typeof init === 'object' && isSequence(init)) { var o = new orig(); toArray(init).forEach(function(e) { if (!isSequence(e)) throw TypeError(); var nv = toArray(e); if (nv.length !== 2) throw TypeError(); o.append(nv[0], nv[1]); }); return o; } else if (init && typeof init === 'object') { o = new orig(); Object.keys(init).forEach(function(key) { o.set(key, init[key]); }); return o; } else { return new orig(init); } }; }()); }(self)); ``` ## document.exitFullscreen() Pushes support back to IE11. ```js /** * document.exitFullScreen() polyfill * @author Chris Ferdinandi * @license MIT */ if (!document.exitFullscreen) { document.exitFullscreen = document.mozExitFullscreen || document.webkitExitFullscreen || document.msExitFullscreen; } ``` ## document.fullscreenElement Pushes support back to IE11. ```js /** * document.fullscreenElement polyfill * Adapted from https://shaka-player-demo.appspot.com/docs/api/lib_polyfill_fullscreen.js.html * @author Chris Ferdinandi * @license MIT */ if (!document.fullscreenElement) { Object.defineProperty(document, 'fullscreenElement', { get: function() { return document.mozFullScreenElement || document.msFullscreenElement || document.webkitFullscreenElement; } }); Object.defineProperty(document, 'fullscreenEnabled', { get: function() { return document.mozFullScreenEnabled || document.msFullscreenEnabled || document.webkitFullscreenEnabled; } }); } ``` ## fetch() Pushes support back to IE10. ```js /** * Fetch Polyfill * https://github.com/github/fetch * (c) GitHub, Inc., MIT License */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.WHATWGFetch = {}))); }(this, (function (exports) { 'use strict'; var support = { searchParams: 'URLSearchParams' in self, iterable: 'Symbol' in self && 'iterator' in Symbol, blob: 'FileReader' in self && 'Blob' in self && (function() { try { new Blob(); return true } catch (e) { return false } })(), formData: 'FormData' in self, arrayBuffer: 'ArrayBuffer' in self }; function isDataView(obj) { return obj && DataView.prototype.isPrototypeOf(obj) } if (support.arrayBuffer) { var viewClasses = [ '[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]' ]; var isArrayBufferView = ArrayBuffer.isView || function(obj) { return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 }; } function normalizeName(name) { if (typeof name !== 'string') { name = String(name); } if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) { throw new TypeError('Invalid character in header field name') } return name.toLowerCase() } function normalizeValue(value) { if (typeof value !== 'string') { value = String(value); } return value } // Build a destructive iterator for the value list function iteratorFor(items) { var iterator = { next: function() { var value = items.shift(); return {done: value === undefined, value: value} } }; if (support.iterable) { iterator[Symbol.iterator] = function() { return iterator }; } return iterator } function Headers(headers) { this.map = {}; if (headers instanceof Headers) { headers.forEach(function(value, name) { this.append(name, value); }, this); } else if (Array.isArray(headers)) { headers.forEach(function(header) { this.append(header[0], header[1]); }, this); } else if (headers) { Object.getOwnPropertyNames(headers).forEach(function(name) { this.append(name, headers[name]); }, this); } } Headers.prototype.append = function(name, value) { name = normalizeName(name); value = normalizeValue(value); var oldValue = this.map[name]; this.map[name] = oldValue ? oldValue + ', ' + value : value; }; Headers.prototype['delete'] = function(name) { delete this.map[normalizeName(name)]; }; Headers.prototype.get = function(name) { name = normalizeName(name); return this.has(name) ? this.map[name] : null }; Headers.prototype.has = function(name) { return this.map.hasOwnProperty(normalizeName(name)) }; Headers.prototype.set = function(name, value) { this.map[normalizeName(name)] = normalizeValue(value); }; Headers.prototype.forEach = function(callback, thisArg) { for (var name in this.map) { if (this.map.hasOwnProperty(name)) { callback.call(thisArg, this.map[name], name, this); } } }; Headers.prototype.keys = function() { var items = []; this.forEach(function(value, name) { items.push(name); }); return iteratorFor(items) }; Headers.prototype.values = function() { var items = []; this.forEach(function(value) { items.push(value); }); return iteratorFor(items) }; Headers.prototype.entries = function() { var items = []; this.forEach(function(value, name) { items.push([name, value]); }); return iteratorFor(items) }; if (support.iterable) { Headers.prototype[Symbol.iterator] = Headers.prototype.entries; } function consumed(body) { if (body.bodyUsed) { return Promise.reject(new TypeError('Already read')) } body.bodyUsed = true; } function fileReaderReady(reader) { return new Promise(function(resolve, reject) { reader.onload = function() { resolve(reader.result); }; reader.onerror = function() { reject(reader.error); }; }) } function readBlobAsArrayBuffer(blob) { var reader = new FileReader(); var promise = fileReaderReady(reader); reader.readAsArrayBuffer(blob); return promise } function readBlobAsText(blob) { var reader = new FileReader(); var promise = fileReaderReady(reader); reader.readAsText(blob); return promise } function readArrayBufferAsText(buf) { var view = new Uint8Array(buf); var chars = new Array(view.length); for (var i = 0; i < view.length; i++) { chars[i] = String.fromCharCode(view[i]); } return chars.join('') } function bufferClone(buf) { if (buf.slice) { return buf.slice(0) } else { var view = new Uint8Array(buf.byteLength); view.set(new Uint8Array(buf)); return view.buffer } } function Body() { this.bodyUsed = false; this._initBody = function(body) { this._bodyInit = body; if (!body) { this._bodyText = ''; } else if (typeof body === 'string') { this._bodyText = body; } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { this._bodyBlob = body; } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { this._bodyFormData = body; } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this._bodyText = body.toString(); } else if (support.arrayBuffer && support.blob && isDataView(body)) { this._bodyArrayBuffer = bufferClone(body.buffer); // IE 10-11 can't handle a DataView body. this._bodyInit = new Blob([this._bodyArrayBuffer]); } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { this._bodyArrayBuffer = bufferClone(body); } else { this._bodyText = body = Object.prototype.toString.call(body); } if (!this.headers.get('content-type')) { if (typeof body === 'string') { this.headers.set('content-type', 'text/plain;charset=UTF-8'); } else if (this._bodyBlob && this._bodyBlob.type) { this.headers.set('content-type', this._bodyBlob.type); } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); } } }; if (support.blob) { this.blob = function() { var rejected = consumed(this); if (rejected) { return rejected } if (this._bodyBlob) { return Promise.resolve(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(new Blob([this._bodyArrayBuffer])) } else if (this._bodyFormData) { throw new Error('could not read FormData body as blob') } else { return Promise.resolve(new Blob([this._bodyText])) } }; this.arrayBuffer = function() { if (this._bodyArrayBuffer) { return consumed(this) || Promise.resolve(this._bodyArrayBuffer) } else { return this.blob().then(readBlobAsArrayBuffer) } }; } this.text = function() { var rejected = consumed(this); if (rejected) { return rejected } if (this._bodyBlob) { return readBlobAsText(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) } else if (this._bodyFormData) { throw new Error('could not read FormData body as text') } else { return Promise.resolve(this._bodyText) } }; if (support.formData) { this.formData = function() { return this.text().then(decode) }; } this.json = function() { return this.text().then(JSON.parse) }; return this } // HTTP methods whose capitalization should be normalized var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']; function normalizeMethod(method) { var upcased = method.toUpperCase(); return methods.indexOf(upcased) > -1 ? upcased : method } function Request(input, options) { options = options || {}; var body = options.body; if (input instanceof Request) { if (input.bodyUsed) { throw new TypeError('Already read') } this.url = input.url; this.credentials = input.credentials; if (!options.headers) { this.headers = new Headers(input.headers); } this.method = input.method; this.mode = input.mode; this.signal = input.signal; if (!body && input._bodyInit != null) { body = input._bodyInit; input.bodyUsed = true; } } else { this.url = String(input); } this.credentials = options.credentials || this.credentials || 'same-origin'; if (options.headers || !this.headers) { this.headers = new Headers(options.headers); } this.method = normalizeMethod(options.method || this.method || 'GET'); this.mode = options.mode || this.mode || null; this.signal = options.signal || this.signal; this.referrer = null; if ((this.method === 'GET' || this.method === 'HEAD') && body) { throw new TypeError('Body not allowed for GET or HEAD requests') } this._initBody(body); } Request.prototype.clone = function() { return new Request(this, {body: this._bodyInit}) }; function decode(body) { var form = new FormData(); body .trim() .split('&') .forEach(function(bytes) { if (bytes) { var split = bytes.split('='); var name = split.shift().replace(/\+/g, ' '); var value = split.join('=').replace(/\+/g, ' '); form.append(decodeURIComponent(name), decodeURIComponent(value)); } }); return form } function parseHeaders(rawHeaders) { var headers = new Headers(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space // https://tools.ietf.org/html/rfc7230#section-3.2 var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); preProcessedHeaders.split(/\r?\n/).forEach(function(line) { var parts = line.split(':'); var key = parts.shift().trim(); if (key) { var value = parts.join(':').trim(); headers.append(key, value); } }); return headers } Body.call(Request.prototype); function Response(bodyInit, options) { if (!options) { options = {}; } this.type = 'default'; this.status = options.status === undefined ? 200 : options.status; this.ok = this.status >= 200 && this.status < 300; this.statusText = 'statusText' in options ? options.statusText : 'OK'; this.headers = new Headers(options.headers); this.url = options.url || ''; this._initBody(bodyInit); } Body.call(Response.prototype); Response.prototype.clone = function() { return new Response(this._bodyInit, { status: this.status, statusText: this.statusText, headers: new Headers(this.headers), url: this.url }) }; Response.error = function() { var response = new Response(null, {status: 0, statusText: ''}); response.type = 'error'; return response }; var redirectStatuses = [301, 302, 303, 307, 308]; Response.redirect = function(url, status) { if (redirectStatuses.indexOf(status) === -1) { throw new RangeError('Invalid status code') } return new Response(null, {status: status, headers: {location: url}}) }; exports.DOMException = self.DOMException; try { new exports.DOMException(); } catch (err) { exports.DOMException = function(message, name) { this.message = message; this.name = name; var error = Error(message); this.stack = error.stack; }; exports.DOMException.prototype = Object.create(Error.prototype); exports.DOMException.prototype.constructor = exports.DOMException; } function fetch(input, init) { return new Promise(function(resolve, reject) { var request = new Request(input, init); if (request.signal && request.signal.aborted) { return reject(new exports.DOMException('Aborted', 'AbortError')) } var xhr = new XMLHttpRequest(); function abortXhr() { xhr.abort(); } xhr.onload = function() { var options = { status: xhr.status, statusText: xhr.statusText, headers: parseHeaders(xhr.getAllResponseHeaders() || '') }; options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL'); var body = 'response' in xhr ? xhr.response : xhr.responseText; resolve(new Response(body, options)); }; xhr.onerror = function() { reject(new TypeError('Network request failed')); }; xhr.ontimeout = function() { reject(new TypeError('Network request failed')); }; xhr.onabort = function() { reject(new exports.DOMException('Aborted', 'AbortError')); }; xhr.open(request.method, request.url, true); if (request.credentials === 'include') { xhr.withCredentials = true; } else if (request.credentials === 'omit') { xhr.withCredentials = false; } if ('responseType' in xhr && support.blob) { xhr.responseType = 'blob'; } request.headers.forEach(function(value, name) { xhr.setRequestHeader(name, value); }); if (request.signal) { request.signal.addEventListener('abort', abortXhr); xhr.onreadystatechange = function() { // DONE (success or failure) if (xhr.readyState === 4) { request.signal.removeEventListener('abort', abortXhr); } }; } xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit); }) } fetch.polyfill = true; if (!self.fetch) { self.fetch = fetch; self.Headers = Headers; self.Request = Request; self.Response = Response; } exports.Headers = Headers; exports.Request = Request; exports.Response = Response; exports.fetch = fetch; Object.defineProperty(exports, '__esModule', { value: true }); }))); ``` ## matchMedia() Pushes support back to IE9. ```js /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. MIT license. https://github.com/paulirish/matchMedia.js/ */ window.matchMedia || (window.matchMedia = function() { 'use strict'; // For browsers that support matchMedium api such as IE 9 and webkit var styleMedia = (window.styleMedia || window.media); // For those that don't support matchMedium if (!styleMedia) { var style = document.createElement('style'), script = document.getElementsByTagName('script')[0], info = null; style.type = 'text/css'; style.id = 'matchmediajs-test'; if (!script) { document.head.appendChild(style); } else { script.parentNode.insertBefore(style, script); } // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle; styleMedia = { matchMedium: function(media) { var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }'; // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers if (style.styleSheet) { style.styleSheet.cssText = text; } else { style.textContent = text; } // Test if media query is true or false return info.width === '1px'; } }; } return function(media) { return { matches: styleMedia.matchMedium(media || 'all'), media: media || 'all' }; }; }()); ``` ## matchMedia().onchange Pushes support back to IE9. ```js /*! matchMedia() polyfill addListener/removeListener extension. Author & copyright (c) 2012: Scott Jehl. MIT license */ (function(){ // Bail out for browsers that have addListener support if (window.matchMedia && window.matchMedia('all').addListener) { return false; } var localMatchMedia = window.matchMedia, hasMediaQueries = localMatchMedia('only all').matches, isListening = false, timeoutID = 0, // setTimeout for debouncing 'handleChange' queries = [], // Contains each 'mql' and associated 'listeners' if 'addListener' is used handleChange = function(evt) { // Debounce clearTimeout(timeoutID); timeoutID = setTimeout(function() { for (var i = 0, il = queries.length; i < il; i++) { var mql = queries[i].mql, listeners = queries[i].listeners || [], matches = localMatchMedia(mql.media).matches; // Update mql.matches value and call listeners // Fire listeners only if transitioning to or from matched state if (matches !== mql.matches) { mql.matches = matches; for (var j = 0, jl = listeners.length; j < jl; j++) { listeners[j].call(window, mql); } } } }, 30); }; window.matchMedia = function(media) { var mql = localMatchMedia(media), listeners = [], index = 0; mql.addListener = function(listener) { // Changes would not occur to css media type so return now (Affects IE <= 8) if (!hasMediaQueries) { return; } // Set up 'resize' listener for browsers that support CSS3 media queries (Not for IE <= 8) // There should only ever be 1 resize listener running for performance if (!isListening) { isListening = true; window.addEventListener('resize', handleChange, true); } // Push object only if it has not been pushed already if (index === 0) { index = queries.push({ mql : mql, listeners : listeners }); } listeners.push(listener); }; mql.removeListener = function(listener) { for (var i = 0, il = listeners.length; i < il; i++){ if (listeners[i] === listener){ listeners.splice(i, 1); } } }; return mql; }; }()); ``` ## window.customEvent() Pushes support back to IE9. ```js /** * CustomEvent() polyfill * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill */ (function () { if (typeof window.CustomEvent === "function") return false; function CustomEvent(event, params) { params = params || { bubbles: false, cancelable: false, detail: undefined }; var evt = document.createEvent('CustomEvent'); evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); return evt; } CustomEvent.prototype = window.Event.prototype; window.CustomEvent = CustomEvent; })(); ``` ## window.requestAnimationFrame() Pushes support back to at least IE6. ```js /** * requestAnimationFrame() polyfill * By Erik Möller. Fixes from Paul Irish and Tino Zijdel. * @link http://paulirish.com/2011/requestanimationframe-for-smart-animating/ * @link http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating * @license MIT */ (function() { var lastTime = 0; var vendors = ['ms', 'moz', 'webkit', 'o']; for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame) { window.requestAnimationFrame = function(callback, element) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, 16 - (currTime - lastTime)); var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function(id) { clearTimeout(id); }; } }()); ```
追风者
2022年3月29日 19:05
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
关于 MrDoc
觅思文档MrDoc
是
州的先生
开发并开源的在线文档系统,其适合作为个人和小型团队的云笔记、文档和知识库管理工具。
如果觅思文档给你或你的团队带来了帮助,欢迎对作者进行一些打赏捐助,这将有力支持作者持续投入精力更新和维护觅思文档,感谢你的捐助!
>>>捐助鸣谢列表
微信
支付宝
QQ
PayPal
Markdown文件
分享
链接
类型
密码
更新密码