4

I have a javascript code that is given below that is ES6 compatible however IE 11 does not support this. What would be the replacement code for this such that it works across all browsers?

[...document.querySelectorAll('.row')]

Im using this for 'click' event handling:

Array.prototype.slice.call(document.querySelectorAll('.row'))
    .forEach(function(header) {
      return header.addEventListener('click', function(e) {
        headerClick(e, header, header.querySelector('.exy'))
      });
    });
RobG
  • 142,382
  • 31
  • 172
  • 209
Neophile
  • 5,660
  • 14
  • 61
  • 107
  • `Array.prototype.slice.call(document.querySelectorAll('.row'))` – gcampbell Aug 01 '16 at 10:08
  • 3
    You could just use Babel. – gcampbell Aug 01 '16 at 10:09
  • Instead of babel, use abstraction and upgrade as you drop support for older browsers. Javascript is scripting but moving to where you soon always have to compile. I'ts not rocket science but we're making it become just that... – Asken Nov 10 '16 at 07:45

1 Answers1

7

For all browsers, you can use Array.prototype.slice via call or apply (it works on any array-like object):

Array.prototype.slice.call(document.querySelectorAll('.row'))

About your updated question:

Im using this for 'click' event handling:

Array.prototype.slice.call(document.querySelectorAll('.row'))
    .forEach(function(header) {
      return header.addEventListener('click', function(e) {
        headerClick(e, header, header.querySelector('.exy'))
      });
    });

I wouldn't use querySelectorAll for this at all, I'd use event delegation. Presumably all of those .row elements are inside a common container (ultimately, of course, they're all in body, but hopefully there's a container "closer" to them than that). With event delegation, you do this:

  • Hook click just once, on the container

  • When a click occurs, check to see if it passed through one of your target elements en route to the container

For your quoted code, that looks something like this:

// A regex we'll reuse
var rexIsRow = /\brow\b/;
// Hook click on the container
document.querySelector("selector-for-the-container").addEventListener(
    "click",
    function(e) {
        // See if we find a .row element in the path from target to container
        var elm;
        for (elm = e.target; elm !== this; elm = elm.parentNode) {
            if (rexIsRow.test(elm.className)) {
                // Yes we did, call `headerClick`
                headerClick(e, elm, elm.querySelector('.exy'));
                // And stop looking
                break;
            }
        }
    },
    false
);

On more modern browsers, you could use elm.classList.contains("row") instead of the regular expression, but sadly not on IE9 or earlier.


That said, rather than maintaining a separate codebase, as gcampbell pointed out you could use ES6 (ES2015) features in your code and then transpile with a transpiler that converts them (well, the ones that can be converted, which is a lot of them) to ES5 syntax. Babel is one such transpiler.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • A non-performant option could be a `for` loop. The `for` loop allows for some filtering on very old browsers, that don't support `Array.filter()`. – Ismael Miguel Aug 01 '16 at 10:12
  • 1
    @IK You can go on https://babeljs.io/repl/ to try it out and to transpile simple stuff. – Ismael Miguel Aug 01 '16 at 10:13
  • Sounds like a great idea. Let me give that a go! Thanks. This link should possibly be used for all ES6 conversion codes I think. – Neophile Aug 01 '16 at 10:13
  • @IK It does involve adding a tool to your toolchain, yes. – T.J. Crowder Aug 01 '16 at 10:13
  • @IsmaelMiguel: True, although for the example. to my very great surprise, if you put `var a = [...document.querySelectorAll(".foo")];` into their REPL, it spits out `var a = [].concat(document.querySelectorAll(".foo"));`, which is both inefficient and, more importantly, **wrong**. (You'll end up with an array with a single entry: the `NodeList` returned by qsa.) – T.J. Crowder Aug 01 '16 at 10:16
  • Yeah and the solution you provided is quite nice and easily readable too. Are there any conversion libraries out there like Babel which would provide this conversion since IE is out of the picture? – Neophile Aug 01 '16 at 10:18
  • @IK I don't happen to know of one (other than Babel). – T.J. Crowder Aug 01 '16 at 10:19
  • @IsmaelMiguel: Whereas in my real project using Babel, it converts it to `var a = [].concat(_toConsumableArray(document.querySelectorAll(".foo")));` which while still inefficient (creating and throwing away an array) is no longer actually *wrong*. – T.J. Crowder Aug 01 '16 at 10:20
  • @T.J.Crowder That is some weird, weird stuff. In my defense, I said "[...] to try simple stuff". Which may or may not work. Maybe a bug should be filed? Also, a tiny fact: older versions of jQuery (at least, 1.8 or older) use your method on their `jQuery.toArray()` method. – Ismael Miguel Aug 01 '16 at 10:39
  • Quick question: The querySelectorAll would work only if the element is there on the page. What if there are elements that are added later (dynamic content) and I want the same call for that too? – Neophile Aug 01 '16 at 11:38
  • @IK: It depends on what you're doing. If you're doing event handling, for instance, you might look at event delegation. – T.J. Crowder Aug 01 '16 at 11:39
  • Yes I am adding an event handler for the click event. I have updated my question with that. Could you please let me know if you have any suggestions? – Neophile Aug 01 '16 at 11:41
  • Thanks @T.J.Crowder. How can I add more elements to the rexIsRow because I have more than one row with a unique class name? – Neophile Aug 01 '16 at 13:40
  • @IK: I don't understand. – T.J. Crowder Aug 01 '16 at 13:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/118820/discussion-between-i-k-and-t-j-crowder). – Neophile Aug 01 '16 at 13:50
  • I have another couple of classes called "row1" and "row2" that need to have this same click functionality. – Neophile Aug 01 '16 at 13:52
  • 1
    With the regex solution, just change the regex to `/\b(?:row|row1|row2)\b/`, which means "a word boundary, then `row` or `row1` or `row2`, then another word boundary." – T.J. Crowder Aug 01 '16 at 13:56