Reputation: 173
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
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
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
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