Какая замена функции jQuery each() в JavaScript?

В jQuery мы имеем функцию, подобную этой

$('button').each(function(i) {
  $('button').eq(i).css('background', 'red');
});

Как мы можем заменить этот код на обычный JavaScript?

Ответы

Ответ 1

Выбор DOM с помощью querySelectorAll()

Браузеры имеют несколько способов выбора элементов из DOM, но, возможно, наиболее гибким и удобным является querySelectorAll. Он позволяет использовать селектор стиля CSS для захвата всех соответствующих элементов из заданного корня.

В вашем случае это будет выглядеть так:

document.querySelectorAll("button");

Сокращение querySelectorAll()

Как ни крути, это немного многословно, поэтому нередко создавать функцию обертывания, которая сокращает ее. Это то, что мы сделаем здесь, указав ему имя Q.

function Q(root, selector) {
  if (typeof root === "string") {
    selector = root
    root = document
  }
  return root.querySelectorAll(selector)
}

Первый аргумент - это контекст, из которого вы делаете выделение, а второй - селектор. Если вы передаете только строку, она будет использовать document в качестве контекста.

Итак, теперь ваш выбор DOM будет таким, который мы будем использовать в дальнейшем:

Q("button");

Заимствование Array.prototype.forEach

Довольно простой способ создания функциональной циклической конструкции - заимствовать forEach метод Array.prototype и вызвать его на сбор элементов с помощью функции .call() метод следующим образом:

Array.prototype.forEach.call(Q("buttons"), function(el) {
  el.style.background = "red";
});

Или в самых современных браузерах мы можем использовать функции стрелок, чтобы немного сократить его:

Array.prototype.forEach.call(Q("buttons"), el => el.style.background = "red");

Связывание и кэширование заимствованного .forEach()

Задолженность .forEach() может быть сокращена, если в начале вашего приложения вы используете прототип функции bind() для привязки .forEach() к значению this .call().

const each = Function.call.bind(Array.prototype.forEach);

Таким образом вы можете просто называть его как функцию, которая получает коллекцию элементов в качестве первого аргумента.

each(Q("button"), function(el) {
  el.style.background = "red";
});

Или снова используя функцию стрелки в некоторых из новейших браузеров:

each(Q("button"), el => el.style.background = "red");

Использование Array.from()

Был также введен метод Array.from, чтобы легко преобразовывать объекты, подобные массиву, в реальные массивы. Это позволит вам напрямую использовать .forEach() и может быть исправлена ​​в устаревшие браузеры с помощью простого polyfill (см. Ссылку docs).

Array.from(Q("button")).forEach(function(el) {
  el.style.background = "red";
});

Если вы поместите вызов Array.from непосредственно в нашу функцию Q сверху, вы сможете напрямую вызвать .forEach().

Q("button").forEach(function(el) {
  el.style.background = "red";
});

Использование цикла for-of

В последних браузерах вы можете использовать for-of loop, делая все очень короткое и чистое:

for (const el of Q("button")) {
  el.style.background = "red";
}

Таким образом, нет необходимости конвертировать в Array или использовать .forEach.


Трансляция современного кода

В приведенных выше примерах, которые требуют самых современных браузеров, доступны транспилеры (например Babel), которые будут переводить последние стандартов в код, который будет работать в старых браузерах.


Создание пользовательского each()

В качестве побочного примечания, если вы хотите, чтобы this ссылался на текущий элемент или имел какое-либо другое конкретное поведение, вот базовая реализация each, которая получает коллекцию и обратный вызов.

function each(a, callback) {
  for (var i = 0; i < a.length; i++) {
    callback.call(a[i], a[i], i, a);
  }
}

Хотя использование this таким образом вообще не требуется, поскольку у вас уже есть элемент как параметр.

Ответ 2

Вы можете использовать функцию getElementsByTagName(), которая запрашивает DOM для элементов определенного тега и возвращает коллекцию элементов. Вы можете вызвать эту функцию на document, или вы можете быть более конкретным и выбрать элемент с помощью document.getElementById(), а затем искать теги внутри этого элемента.

В любом случае, когда у вас есть коллекция элементов, вы можете затем перебирать коллекцию и применять соответствующие стили.

//query the document for all <button> elements
var buttons = document.getElementsByTagName('button');

/* -- OR -- */

//query the document for all buttons inside of a specific element
var container = document.getElementById('container');
var buttons = container.getElementsByTagName('button');

//loop over the collection of buttons and set each background to 'red'
for(var i=0; i<buttons.length; i++) {
    buttons[i].style.background = "red";
}

Edit: Я понимаю, что JS не поддерживает JQuery для каждой функции. OP не заявлял, что хотел бы видеть это конкретно, просто способ реализовать функциональность $.each() с помощью JS (так как есть много возможностей). Этот пример - просто тривиальный подход, использующий очень базовую концепцию.

Ответ 3

var buttons = document.querySelectorAll("button");
for(var x in buttons){
  var e = buttons[x];
  e.innerHTML="stuff";
}

как указывал косоглазие, должны быть кнопки [x] и var x. извините за это.

Ответ 4

Поддерживается больше браузеров, так как он не использует forEach.

Если вы используете getElementsByTagName, добавьте функцию в HTMLCollection.

HTMLCollection.prototype.each = function(callback){
    for(var i=0; i<this.length; i++) callback(this[i]);
};

document.getElementsByTagName('div').each(function(div){
    div.innerHTML = "poo";
});

Если вы используете querySelectorAll, вы можете прикрепить ту же функцию к NodeList

NodeList.prototype.each = function(callback){
    for(var i=0; i<this.length; i++) callback(this[i]);
};

document.querySelectorAll('div').each(function(div){
    div.innerHTML = "podo";
});

Чтобы успокоить chanters (см. комментарии), я укажу, что если это для библиотеки, которую вы пишете, или если она будет использоваться вместе с библиотекой, которая могла бы ее перезаписать, очевидно, вы бы хотели рассмотреть другие методы.

Fiddle: https://jsfiddle.net/er5amk8j/

Ответ 5

Но вы также используете $.each() в сомнительном ключе в своем примере. Намного проще и быстрее обращаться к элементам с помощью this.

$('button').each(function(i) {
  $(this).css('background', 'red');
});

Самая простая замена - это использование простых закрытий функций, но это не очень.

var $buttons = $('button');

for(var i = 0; i < $buttons.length; i++){
  (function(i){
    $buttons.eq(i).css('background', 'red');
  })(i);
}

Или для установки this используйте .call() для вызова функции.

var $buttons = $('button');

for(var i = 0; i < $buttons.length; i++){
  (function(i){
    $(this).css('background', 'red');    
  }).call($buttons[i], i);
}