babel polyfill being included, but forEach still doesn't work in IE11 on NodeLists

12,843

Update: As of Babel 7.4.0, Babel has switched to using core-js directly rather than wrapping it with @babel/polyfill. core-js already polyfills forEach on NodeList, so no additional polyfill required anymore.


babel-polyfill doesn't polyfill missing web API/prototype methods like NodeList.prototype.forEach.

Also please note that your question title is misleading as NodeList.prototype.forEach is not an ES6 feature. forEach on iterable collections is currently only a candidate recommendation (as of August 2018).

Simply include your own polyfill at the top level of your Javascript:

if (window.NodeList && !NodeList.prototype.forEach) {
    NodeList.prototype.forEach = Array.prototype.forEach;
}

This might change as soon as core-js 3 is stable: https://github.com/zloirock/core-js/issues/329

You can also go without any polyfill if you start to adopt the common pattern being used in ES6 times:

const testList = [...document.querySelectorAll('.test-list li')];

or

const testList = Array.from(document.querySelectorAll('.test-list li'));

The other option you have is to use for...of instead:

const lis = document.querySelectorAll('.test-list li');
for (const li of lis) {
  // li.addEventListener(...) or whatever
}

Finally, you can also adopt the common ES5 pattern:

var testList = document.querySelectorAll('.test-list li');
Array.prototype.forEach.call(testList, function(li) { /*whatever*/ });
Share:
12,843
Robert_QSS
Author by

Robert_QSS

Updated on June 17, 2022

Comments

  • Robert_QSS
    Robert_QSS almost 2 years

    I've got Webpack working with Babel and including the @babel/polyfill, yet IE11 is still throwing a SCRIPT438 error when trying to use .forEach on a NodeList.

    Here's my package.json

    {
      ...
      "scripts": {
        "build:js": "webpack --config ./_build/webpack.config.js"
      },
      ...
      "browserslist": [
        "IE 11",
        "last 3 versions",
        "not IE < 11"
      ],
      "babel": {
        "presets": [
          [
            "@babel/preset-env",
            {
              "useBuiltIns": "usage"
            }
          ]
        ]
      },
      "devDependencies": {
        "@babel/core": "^7.1.6",
        "@babel/preset-env": "^7.1.6",
        "babel-loader": "^8.0.4",
        "webpack": "^4.25.1",
        "webpack-cli": "^3.1.2"
      },
      "dependencies": {
        "@babel/polyfill": "^7.0.0"
      }
    }
    

    My webpack.config.js:

    const path = require('path');
    const webpack = require('webpack');
    
    module.exports = (env, argv) => {
    
      const javascript = {
        test: /\.js$/,
        use: {
          loader: 'babel-loader'
        }
      };
    
      // config object
      const config = {
        entry: {
          main: './_src/js/main.js',
        },
        devtool: 'source-map',
        output: {
          path: path.resolve(__dirname, '../js'),
          filename: '[name].js',
        },
        module: {
          rules: [javascript]
        }
      }
    
      return config;
    }
    

    And finally /_src/main.js that I'm running through webpack and babel:

    const testList = document.querySelectorAll('.test-list li');
    
    testList.forEach(item => {
      console.log(item.innerHTML);
    })
    

    The docs at https://babeljs.io/docs/en/babel-polyfill say that you don't need to import or require polyfill when loading it via Webpack with useBuiltIns: "usage". But even if I remove that option and manually import the whole polyfill at the top of main.js (making my bundle huge), it still errors out in IE11.

    So...what am I doing wrong?

  • Ramy Rais
    Ramy Rais almost 5 years
    Array.from() is not going to work on IE11 as it is not supported.
  • connexo
    connexo almost 5 years
    @RamyRais Which is why I also added the ES5 way. IE 11 does not support any ES6 feature except const and let keyword.