Javascript ES6 TypeError в Safari и IE

Я работаю с кодом Javascript, написанным кем-то, кто использовал ES6 на сайте Wordpress. Он делает вызов Ajax для отображения данных в DOM, который работает для Chrome и Firefox, но по какой-то причине Safari выдает следующую ошибку в консоли:

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)

Это функция:

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);
    })
  });
}

И я не могу точно сказать, почему у Safari могут возникнуть проблемы с этим, в то время как Chrome / FF даже не регистрирует никаких ошибок об этой конкретной части в консоли и работает просто отлично. Я знаю, что это должна быть проблема с совместимостью браузера, но пока не нашел много информации.


person Amma    schedule 30.08.2017    source источник
comment
В какой версии Safari это не работает?   -  person pvg    schedule 31.08.2017
comment
@AndrewLi, на самом деле это NodeList#forEach но это может быть не во всех браузерах. Амма, в какой версии Safari вы это тестировали? Кажется, это может поддерживаться или не поддерживаться в версиях до Safari 10.   -  person Patrick Evans    schedule 31.08.2017
comment
@PatrickEvans Ах, да. Спасибо за внимание.   -  person Andrew Li    schedule 31.08.2017
comment
Вы правы, это работает в Safari 10, но в консоли по-прежнему регистрируется TypeError. И функциональность полностью ломается от Safari 9 ниже. Я использую Browserstack рядом с моим собственным Mac.   -  person Amma    schedule 31.08.2017


Ответы (3)


document.querySelectorAll возвращает объект NodeList, а не массив. Как это можно увидеть на MDN, NodeList имеет forEach метод но он плохо поддерживается, поэтому он работает в последних версиях Firefox и Chrome, но не в других браузерах (Safari):

Хотя NodeList не является массивом, его можно перебирать с помощью forEach(). Несколько старых браузеров еще не реализовали этот метод.

Для совместимого перебора списков узлов и других итераторов следует использовать Array.from (может быть полифилл в старых браузерах):

Array.from(document.querySelectorAll(".js_zip-lookup__submit")).forEach(...);
person Estus Flask    schedule 30.08.2017
comment
Это удалило TypeError, и оно работает в Safari 10, но не для 9 и ниже. Есть ли альтернатива для этого, чтобы переопределить совместимость? А также для IE, если можно? - person Amma; 31.08.2017
comment
Как уже было сказано, Array.from является полизаполняемым. Используйте полифиллы, например. github.com/zloirock/core-js. Это должно быть сделано по умолчанию в любом веб-проекте. - person Estus Flask; 31.08.2017
comment
Для тех, кто может столкнуться с этим в будущем, в идеале core-js полифилил бы и здесь .forEach, но посмотрим. github.com/zloirock/core-js/issues/329 - person loganfsmyth; 31.08.2017
comment
На самом деле [email protected] уже включен в этот проект как часть пакета babel, но до сих пор нет полифилла .forEach... - person Amma; 01.09.2017
comment
@Amma Проблема, на которую ссылается loganfsmyth, говорит о том, что полифилла NodeList forEach еще нет. Используйте Array.from, как следует из ответа. - person Estus Flask; 01.09.2017
comment
Я понимаю. Но Array.from не решает проблему для Safari 9 ниже и IE, с ним работает только Safari 10. - person Amma; 01.09.2017
comment
Это определенно так, поэтому это было предложено. Вы можете проверить, что эта планка работает в любом разумном браузере. Если у вас все еще есть проблемы с вашим случаем, рассмотрите возможность предоставления stackoverflow.com/help/mcve, который может воспроизвести проблему. - person Estus Flask; 01.09.2017

Я перепробовал множество вариантов Array.prototype, но единственное, что решило проблему совместимости IE и Safari, — это включение приведенного ниже фрагмента polypill, решение найдено в этот блог:

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

Как упоминалось @Estus, более старые версии Safari не реализуют .forEach для объектов списка узлов. Однако Array.prototype.forEach определяется как общий в ES5.1 спецификация:

Функция forEach намеренно является универсальной; он не требует, чтобы его значение this было объектом Array. Поэтому его можно перенести на другие виды объектов для использования в качестве метода. Возможность успешного применения функции forEach к хост-объекту зависит от реализации.

Следовательно, рабочим решением является вызов Array.prototype.forEach в нодлисте и передача ему функции, которую вы хотите выполнить. В качестве урезанного (и дешевого) тестового примера:

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>

Это было протестировано и работает в Safari 5.1.7 для Windows и в Интернете. Эмуляция проводника 9. Закомментированная строка воспроизводит ошибку, о которой сообщалось в сообщении в Safari 5.1.7.

person traktor    schedule 31.08.2017
comment
Спасибо @Трактор. Я использовал следующий код, и все еще не подходит для IE внутри Browsertack. Любые альтернативы? var zipSubmit = document.querySelectorAll('.js_zip-lookup__submit'); Array.prototype.forEach.call(zipSubmit, function(button){ button.addEventListener('click', function(){ const zip = button.parentNode.querySelector('.zip-lookup__input').value; displayResults(zip) ; }); }); - person Amma; 01.09.2017
comment
Трудно сказать без подробностей об ошибках и HTML-кода. Если Browserstack не помогает, попробуйте протестировать на компьютере с Windows, на котором запущен IE с открытыми инструментами разработки (F12). Измените режим эмуляции, чтобы протестировать старые браузеры. Если ничего не появляется, напишите цикл for, чтобы добавить прослушиватели событий и убедиться, что остальная часть кода работает. .forEach — это ES5.1, и он не будет работать в IE8 и более ранних версиях. - person traktor; 02.09.2017