Amma
Amma

Reputation: 173

Javascript ES6 TypeError in Safari & IE

I am working with a Javascript code written by someone else who used ES6 in a Wordpress site. It makes an Ajax call to show a data in DOM, which works for Chrome and Firefox but for some reason Safari gives following error in console:

TypeError: document.querySelectorAll(".js_zip-lookup__submit").forEach is not a function. (In 'document.querySelectorAll(".js_zip-lookup__submit").forEach(function(e){e.addEventListener("click",function(){displayResults(e.parentNode.querySelector(".zip-lookup__input").value)})})', 'document.querySelectorAll(".js_zip-lookup__submit").forEach' is undefined)

This is the function:

function finderInit(){
  document.querySelectorAll('.js_zip-lookup__submit').forEach(function(button){
    button.addEventListener('click', function(){
      const zip = button.parentNode.querySelector('.zip-lookup__input').value;
      displayResults(zip);
    });
  });

  document.querySelectorAll('.zip-lookup').forEach(function(form){
    form.addEventListener('submit', function(e){
      e.preventDefault();
      const zip = form.querySelector('.zip-lookup__input').value;
      displayResults(zip);
    })
  });
}

And I can't quite tell why Safari would have an issue with this, while Chrome/FF doesn't even log any error about this particular part in the console and it works just fine. I know it should be some browser compatibility issue but haven't found much information so far.

Upvotes: 2

Views: 2189

Answers (3)

Amma
Amma

Reputation: 173

I tried many variations of Array.prototype, but the only thing that solved IE & Safari compatibility issue was inclusion of this polypill snippet below, solution found in this blog:

(function () {
    if ( typeof NodeList.prototype.forEach === "function" ) return false;
    NodeList.prototype.forEach = Array.prototype.forEach;
})();

Upvotes: 2

traktor
traktor

Reputation: 19386

As mentioned by @Estus, older versions of Safari do not implement .forEach on nodelist objects. However, Array.prototype.forEach is defined as generic in the ES5.1 specifiction:

The forEach function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the forEach function can be applied successfully to a host object is implementation-dependent.

Hence a workable solution is to call Array.prototype.forEach on the nodelist and pass it the function you want to execute. As a cut down (and cheap) test case:

var col = document.getElementsByTagName("p");
//col.forEach(                    function (el) {document.write(el.id)});
Array.prototype.forEach.call(col, function (el) {document.write(el.id)});
<p id="p1"></p>
<p id="p2"></p>
<p id="p3"></p>
<p id="p4"></p>

This was tested and found to work in Safari 5.1.7 for Windows and under Internet. Explorer 9 emulation. The commented out line reproduces the error reported in the post in Safarai 5.1.7.

Upvotes: 1

Estus Flask
Estus Flask

Reputation: 223318

document.querySelectorAll returns NodeList object, not an array. As it can be seen on MDN, NodeList has forEach method but it's not well-supported, that's why it works in recent Firefox and Chrome but not in other browsers (Safari):

Although NodeList is not an Array, it is possible to iterate on it using forEach(). Several older browsers have not implemented this method yet.

To iterate over node lists and other iterators in a compatible way, Array.from should be used (can be polyfilled in older browsers):

Array.from(document.querySelectorAll(".js_zip-lookup__submit")).forEach(...);

Upvotes: 2

Related Questions