在jQuery中我们有一个像这样的函数
$('button').each(function(i) {
$('button').eq(i).css('background', 'red');
});
我们如何用纯JavaScript替换此代码?
答案 0 :(得分:26)
使用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
执行函数循环结构的一种非常常见的方法是借用Array.prototype
的{{3}}方法,并使用函数的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()
如果在应用程序的早期,可以缩短.forEach()
借用,使用函数原型的.call()
方法将.forEach()
方法绑定到this
.call()
的值const each = Function.call.bind(Array.prototype.forEach);
1}}。
each(Q("button"), function(el) {
el.style.background = "red";
});
这样你就可以把它称为一个接收元素集合作为第一个参数的函数。
each(Q("button"), el => el.style.background = "red");
或者在一些最新的浏览器中再次使用箭头功能:
Array.from()
使用.forEach()
还引入了bind()
方法,可以轻松地将类似数组的对象转换为实际数组。这样您就可以直接使用Array.from(Q("button")).forEach(function(el) {
el.style.background = "red";
});
,并可以使用简单的polyfill 修补旧版浏览器(请参阅文档链接)。
Array.from
如果您将Q
电话直接放在我们.forEach()
的上述功能中,您就可以直接拨打Q("button").forEach(function(el) {
el.style.background = "red";
});
。
for-of
使用for (const el of Q("button")) {
el.style.background = "red";
}
循环
在最新的浏览器中,您可以使用Array.from
代替,使所有内容都非常简洁:
Array
这样就无需转换为.forEach
或使用each()
。
展示现代代码
对于需要最现代浏览器的上述示例,可以使用转换器(例如for-of
loop)将最新标准转换为可在旧版浏览器中使用的代码。
制作自定义this
作为旁注,如果您希望each
引用当前元素,或者有任何其他特定行为,这里是一个接收集合和回调的基本function each(a, callback) {
for (var i = 0; i < a.length; i++) {
callback.call(a[i], a[i], i, a);
}
}
实现。< / p>
this
虽然通常不需要使用 2016-11-28 20:48:29.277 SquaresApp[12766:589296] -[SquaresApp.RemoveAdsView handleTap]: unrecognized selector sent to instance 0x7f84ffc0b1e0
2016-11-28 20:48:29.301 SquaresApp[12766:589296] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SquaresApp.RemoveAdsView handleTap]: unrecognized selector sent to instance 0x7f84ffc0b1e0'
*** First throw call stack:
(
0 CoreFoundation 0x00000001107c434b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x000000010fbe921e objc_exception_throw + 48
2 CoreFoundation 0x0000000110833f34 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x0000000110749c15 ___forwarding___ + 1013
4 CoreFoundation 0x0000000110749798 _CF_forwarding_prep_0 + 120
5 UIKit 0x0000000111640e41 -[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 57
6 UIKit 0x0000000111648be0 _UIGestureRecognizerSendTargetActions + 109
7 UIKit 0x00000001116466af _UIGestureRecognizerSendActions + 227
8 UIKit 0x000000011164593b -[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:] + 891
9 UIKit 0x0000000111631a0e _UIGestureEnvironmentUpdate + 1395
10 UIKit 0x0000000111631453 -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 521
11 UIKit 0x0000000111630636 -[UIGestureEnvironment _updateGesturesForEvent:window:] + 286
12 UIKit 0x000000011116f3b9 -[UIWindow sendEvent:] + 3989
13 UIKit 0x000000011111c63f -[UIApplication sendEvent:] + 371
14 UIKit 0x000000011190e71d __dispatchPreprocessedEventFromEventQueue + 3248
15 UIKit 0x00000001119073c7 __handleEventQueue + 4879
16 CoreFoundation 0x0000000110769311 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
17 CoreFoundation 0x000000011074e59c __CFRunLoopDoSources0 + 556
18 CoreFoundation 0x000000011074da86 __CFRunLoopRun + 918
19 CoreFoundation 0x000000011074d494 CFRunLoopRunSpecific + 420
20 GraphicsServices 0x0000000114a1ea6f GSEventRunModal + 161
21 UIKit 0x00000001110fe964 UIApplicationMain + 159
22 SquaresApp 0x000000010c0f80ef main + 111
23 libdyld.dylib 0x000000011324068d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
,因为您已经将元素作为参数。
答案 1 :(得分:5)
您可以使用函数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";
}
编辑:我意识到这不是JS背后jQuery的每个功能的工作方式。 OP并没有声明他想要明确地看到这一点,只是通过JS实现$.each()
功能的一种方式(因为有很多可能性)。这个例子只是一个使用非常基本概念的简单方法。
答案 2 :(得分:4)
var buttons = document.querySelectorAll("button");
for(var x in buttons){
var e = buttons[x];
e.innerHTML="stuff";
}
斜视指出,它应该是按钮[x]和var x。
抱歉。
答案 3 :(得分:3)
支持更多浏览器,因为它不使用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";
});
为了安抚这些话题(请参阅注释),我将指定如果这是针对您正在编写的库,或者如果它将与可能会覆盖它的库一起使用,显然您会想要考虑其他方法。
答案 4 :(得分:2)
但您在示例中也以可疑的方式使用$.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);
}