$('button').each(function(i) {
$('button').eq(i).css('background', 'red');
});
我们如何使用普通JavaScript替换这段代码?
$('button').each(function(i) {
$('button').eq(i).css('background', 'red');
});
我们如何使用普通JavaScript替换这段代码?
querySelectorAll()
进行DOM选择
浏览器有几种从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
使用函数的 .call()
方法,从 Array.prototype
借用 forEach
方法用于循环操作是一种相当常见的函数式编程方法,像这样:
Array.prototype.forEach.call(Q("buttons"), function(el) {
el.style.background = "red";
});
或者在最现代的浏览器中,我们可以使用箭头函数来简化它:
Array.prototype.forEach.call(Q("buttons"), el => el.style.background = "red");
将借用的.forEach()
方法进行绑定和缓存
如果在应用程序早期使用函数原型的bind()
方法将.forEach()
方法绑定到.call()
的this
值,那么.forEach()
的借用就可以被缩短。
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(请参阅文档链接) 来修补旧版浏览器。
Array.from(Q("button")).forEach(function(el) {
el.style.background = "red";
});
如果您直接在上面的Q
函数中调用Array.from
,则可以直接调用.forEach()
。
Q("button").forEach(function(el) {
el.style.background = "red";
});
使用for-of
循环
在最新的浏览器中,您可以使用for-of
循环替代,使代码非常简短和干净:
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
通常是不必要的,因为你已经将元素作为参数了。
$(...).each()
中,this
将指向元素。 - haim770getElementsByTagName()
,该函数查询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";
}
编辑:我意识到这不是jQuery的each函数背后的JS。OP并没有说他想特别看到那个,只是想用JS实现$ .each()功能的一种方法(因为有很多可能性)。这个例子只是使用一个非常基本概念的微不足道的方法。
var buttons = document.querySelectorAll("button");
for(var x in buttons){
var e = buttons[x];
e.innerHTML="stuff";
}
由于它不使用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";
});
但是你在示例中使用$.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);
}
或者使用.call()
来调用函数,从而设置this
。
var $buttons = $('button');
for(var i = 0; i < $buttons.length; i++){
(function(i){
$(this).css('background', 'red');
}).call($buttons[i], i);
}
i
在循环中立即被使用而不是在回调中稍后被使用。但你关于 OP 的技巧是正确的。在循环中一遍又一遍地调用 $('button')
会很慢。 - user1106925Michael Hamilton
的答案,以显示尽管结果相同,但替换.each()
的方法并不正确。 - Bizniztime
Array.prototype.forEach()
,但它本身不能替换那个代码。 - Pointyfor...of
循环。 - Bergi