javascript 将侦听器附加到多个子元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27621699/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Attach listener to multiple children elements
提问by mjanisz1
Im trying to get rid of jquery from my page and rewriting some functionalities to pure js. There are 2 lists with class work, containing few li elements. Each li element should have an action on click to add class 'active' to it. In jquery it is very simple:
我试图从我的页面中删除 jquery,并将一些功能重写为纯 js。有 2 个带类作业的列表,包含很少的 li 元素。每个 li 元素都应该有一个点击操作来向它添加类“活动”。在 jquery 中它非常简单:
$('.work li').on('click', function () {
var that = $(this);
that.parent().find('li.active').removeClass('active');
$(this).addClass('active');
})
Is there a nicer solution in pure js rather than making something like this with nested loops:
在纯 js 中是否有更好的解决方案,而不是用嵌套循环制作这样的东西:
var lists = document.getElementsByClassName('work');
for(var i=0; i<lists.length; i++){
var children = lists[i].querySelectorAll('li');
for(var j=0; j<children.length;j++){
children[j].addEventListener();
}
}
采纳答案by Abhitalks
There are 2 lists with class work, containing few li elements. Each li element should have an action on click to add class 'active' to it.
有 2 个带类作业的列表,包含很少的 li 元素。每个 li 元素都应该有一个点击操作来向它添加类“活动”。
You could create that entire functionality by adding an event listener to all li
s returned by the querySelectorAll
. The querySelectorAll
returns a nodeList and not an array, so need to map it in order to iterate it. However, note that we are still iterating the set.
你可以通过添加事件监听到所有创建全部功能li
通过传回的querySelectorAll
。所述querySelectorAll
返回一个节点列表,而不是一个数组,所以需要将其以映射到迭代它。但是,请注意,我们仍在迭代该集合。
Example Snippet:
示例片段:
var lists = document.querySelectorAll(".work li"),
doSomething = function() {
[].map.call(lists, function(elem) { elem.classList.remove("active") });
this.classList.add("active");
};
[].map.call(lists, function(elem) {
elem.addEventListener("click", doSomething, false);
});
li { cursor: pointer; }
.active { background-color: yellow; }
<ul class="work"><li>One</li><li>Two</li><li>Three</li></ul>
<ul class="work"><li>Four</li><li>Five</li><li>Six</li></ul>
In fact you could also use event delegation and add event listener only on the ul
and then use the event.target
to handle your routine. This is better than adding an event listener to each and every li
, in case there are many of them. There will be only as many handlers as ul
s.
事实上,您也可以使用事件委托并仅在 上添加事件侦听器ul
,然后使用event.target
来处理您的例程。这比向 each 添加一个事件侦听器要好li
,以防它们很多。将只有与ul
s一样多的处理程序。
Is there a nicer solution in pure js rather than making something like this with nested loops
在纯 js 中是否有更好的解决方案,而不是用嵌套循环制作这样的东西
Not really. You have to iterate over the set of elements anyway. Nicer in terms of jazzy, yes. Nicer in terms of avoiding the loops, no. Nicer in terms of efficiency, well yes. Compare the above vanilla javascript code with the jQuery one in your question:
并不真地。无论如何,您必须遍历元素集。在爵士方面更好,是的。在避免循环方面更好,不。在效率方面更好,是的。将上面的 vanilla javascript 代码与您问题中的 jQuery 代码进行比较:
$('.work li').on('click', function () { // internally will iterate and add listener to each li
var that = $(this);
that.parent().find('li.active').removeClass('active'); // go to parent and then find li
$(this).addClass('active');
});
.
.
回答by sbgoran
You could actually attach click event listener on wrapper element and filter out clicked li
element using event.target
(or for IE event.srcElement
).
您实际上可以在包装器元素上附加单击事件侦听器,并li
使用event.target
(或用于 IE event.srcElement
)过滤掉单击的元素。
Try out below snippet (not production code by any means, it should be enhanced especially for browser compatibility). Good side of this solution is that you don't have to watch for new elements dynamically added to wrapper element...
试试下面的代码片段(无论如何都不是生产代码,它应该特别是为了浏览器兼容性而增强)。这个解决方案的好处是你不必注意动态添加到包装元素的新元素......
function clickHandler(e) {
var elem, evt = e ? e : event;
if (evt.srcElement) {
elem = evt.srcElement;
} else if (evt.target) {
elem = evt.target;
}
// Filter out li tags
if (elem && ('LI' !== elem.tagName.toUpperCase())) {
return true;
}
console.log('You clicked: ' + elem.innerHTML)
return true;
}
var lists = document.getElementsByClassName('work');
for (var i = 0; i < lists.length; i++) {
var children = lists[i].addEventListener('click', clickHandler);
}
<ul class="work">
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
<li>1.4</li>
</ul>
<ul class="work">
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
<li>2.4</li>
</ul>
<ul class="work">
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
<li>3.4</li>
</ul>
回答by enhzflep
Sure. Why not just make better use of document.querySelectorAll
? It does use the syntax of css, so we can grab both in one go.
当然。为什么不更好地利用document.querySelectorAll
?它确实使用了 css 的语法,因此我们可以一口气获取两者。
Perhaps something like this:
也许是这样的:
{
liList = document.querySelectorAll('.work li');
var i, n = liList.length;
for (i=0; i<n; i++)
liList[i].addEventListener(eventName, functionName, useCapture);
}
回答by Alexander Arutinyants
querySelectorAll()is quite powerfull tool.
querySelectorAll()是非常强大的工具。
I've made a JSFiddlefor you.
我为你制作了一个JSFiddle。
Code can still be enough nice and readable:
代码仍然足够漂亮和可读:
for (var item of document.querySelectorAll(".work li")) {
item.addEventListener("click", function (evt) {
evt.target.classList.add("active");
}, false);
}
Another way is (if you still want to iterate using forEach):
另一种方法是(如果您仍然想使用 forEach 进行迭代):
Array.prototype.forEach.call(document.querySelectorAll(".work li"), function(item) {
item.addEventListener("click", function (evt) {
evt.target.classList.add("active");
}, false);
});
Here (JSFiddle) we are interating through a NodeListreturned by querySelectorAll(".work li")
by callingthe Array.prototype.forEachmethod on it.
这里(的jsfiddle)我们是通过作用中一个节点列表返回通过querySelectorAll(".work li")
由调用的Array.prototype.forEach方法就可以了。
And here (Jsfiddle) is the full example, that toggles the class:
这里(Jsfiddle)是完整的例子,它切换类:
Array.prototype.forEach.call(document.querySelectorAll(".work li"), function(item) {
item.addEventListener("click", function (evt) {
evt.target.toggleActive = !evt.target.toggleActive;
evt.target.classList[!!evt.target.toggleActive ? 'add' : 'remove']("active");
}, false);
});
And finally if there should be only one active element at a time (Jsfiddle):
最后,如果一次应该只有一个活动元素(Jsfiddle):
var currentActiveElement;
Array.prototype.forEach.call(document.querySelectorAll('.work li'), function(item) {
item.addEventListener('click', function(evt) {
currentActiveElement && currentActiveElement.classList.remove('active');
currentActiveElement = evt.target;
currentActiveElement.classList.add('active');
}, false);
});
回答by Alexander Arutinyants
People complain about the wordiness of using native DOM methods, but there's no problem a few bits of strategically-placed sugar could not help. Start off by writing it the way you'd like to:
人们抱怨使用原生 DOM 方法的冗长,但没有问题,一些战略性地放置的糖也无济于事。首先按照您喜欢的方式编写它:
eachClass('work', function(work) {
eachSelector(work, 'li', function(li) {
on(li, 'click', function() {
eachChild(parent(li), active.remove);
active.add(li);
});
});
});
Now you just have to write eachClass
, eachSelector
, eachChild
, parent
, and on
, something like the following. They'll each be about 2-3 lines. You just have to write them once, and voila, you have your own little library.
现在您只需编写eachClass
, eachSelector
, eachChild
, parent
, 和on
,类似于以下内容。它们每个大约有 2-3 行。你只需要写一次它们,瞧,你有自己的小图书馆。
function eachClass(cls, fn) {
[].forEach.call(document.getElementsByClassName(cls), fn);
}
What is active
? It's a little object we can create for setting and removing a particular class:
什么是active
?这是我们可以创建用于设置和删除特定类的小对象:
function makeClassSetter(cls) {
return {
add: function(elt) { elt.classList.add(cls); },
remove: function(elt) {elt.classList.remove(cls); }
};
}
var active = makeClassSetter('active');
And so on. Great programmers are constantly building little frameworks and layers and utilities and mini-languages. That's what makes their code compact, readable, less buggy, and easier to write.
等等。伟大的程序员不断地构建小框架、层、实用程序和迷你语言。这就是使他们的代码紧凑、可读、错误更少且更易于编写的原因。
回答by thuperman
I too was looking for a concise but useful solution to this.
我也在寻找一个简洁但有用的解决方案。
This is the best approach I came up with:
这是我想出的最好的方法:
tag = document.querySelectorAll('.tag');
tag.forEach(element => {
element.addEventListener("mouseover", aFunction);
});
function aFunction(){
this.style.background = "grey";
}
querySelectorAll is a powerful method, a foreach is always much shorter and easier to use when the use-case is there. I prefer naming the functions and adding to them, although I suppose you could easily replace that with another arrow function within the foreach if you prefer
querySelectorAll 是一种强大的方法,当有用例时,foreach 总是更短且更易于使用。我更喜欢命名函数并添加到它们中,尽管我想如果您愿意,您可以轻松地将其替换为 foreach 中的另一个箭头函数