"Object doesn't support this property or method" IE10/11
Solution 1:
You're adding forEach
to the window
object, not to the object you return; you're calling $
as a function, not a constructor. Since you're using loose mode (apparently), this
within the function call is a reference to the global object (also accessible as window
on browsers). You're returning the collection from querySelectorAll
unchanged.
The reason it works on Chrome is that the collection returned by querySelectorAll
has its own forEach
(this is a fairly recent addition).
For this to work, four options:
-
Return an object and add
forEach
to it, copying the elements from the QSA collection to that object. E.g.:function $(selector) { const result = Array.from(document.querySelectorAll(selector)); result.forEach = Array.prototype.forEach; // Perhaps map, filter, etc.; add in a loop? return result; }
Or in ES5:
var $ = (function() { var methods = Array.prototype; function $(selector) { var result = methods.slice.call(document.querySelectorAll(selector)); result.forEach = methods.forEach; // Perhaps map, filter, etc.; add in a loop? return result; } return $; })();
-
Add
forEach
to theNodeList
prototype if it's not already there and useforEach
directly on the collection fromquerySelectorAll
. For instance:if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) { // Yes, direct assignment is fine here, no need for `Object.defineProperty` NodeList.prototype.forEach = Array.prototype.forEach; }
(No need for
Object.defineProperty
above,enumerable
[surprisingly],configurable
, andwritable
are alltrue
for it on Chrome and Firefox, so we can just do direct assignment as above.)...and then of course your
$
becomes nothing more thanfunction $(selector) { return document.querySelectorAll(selector); }
(To start with. If you wanted to add more features, you'd probably want to go the way of #1.)
-
Return an array:
function $(selector) { return Array.from(document.querySelectorAll(selector)); }
Or in ES5:
function $(selector) { return Array.prototype.slice.call(document.querySelectorAll(selector)); }
-
Subclass
Array
(which cannot be perfectly polyfilled on pre-ES2015 JavaScript engines) so you can add your own features on top ofArray
's features:class MyThingy extends Array { // Perhaps further methods here } function $(selector) { return MyThingy.from(document.querySelectorAll(selector)); }
No ES5 option here, you'd need to at least transpile and polyfill.
If you're going to add features beyond those provided by Array
, I quite like #4 other than the polyfilling available only being "so" good. It may well be sufficient for your purposes.