将侦听器附加到多个子元素

时间:2014-12-23 13:49:08

标签: javascript listener

我试图从我的页面中删除jquery并将一些功能重写为纯js。 有2个列表与类工作,包含很少的li元素。每个li元素都应该有一个单击动作来添加class' active'它。在jquery中它非常简单:

$('.work li').on('click', function () {
            var that = $(this);
            that.parent().find('li.active').removeClass('active');
            $(this).addClass('active');
        })

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

6 个答案:

答案 0 :(得分:5)

  

有两个包含类工作的列表,包含很少的li元素。每个李   element应该在click上有一个动作,将类'active'添加到它。

您可以通过向li返回的所有querySelectorAll添加事件侦听器来创建整个功能。 querySelectorAll返回一个nodeList而不是一个数组,因此需要映射它以便迭代它。但请注意,我们仍在迭代该集合。

示例代码段

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>


实际上,您也可以使用事件委派并仅在ul上添加事件侦听器,然后使用event.target来处理您的例程。这比为每个li添加一个事件监听器更好,以防它们有很多。只有ul的处理程序。


  

在纯粹的js中是否有更好的解决方案,而不是制作类似的东西   这与嵌套循环

不是真的。无论如何,你必须迭代这组元素。在爵士方面更好,是的。在避免循环方面更好,没有。在效率方面更好,是的。将上述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');
});

答案 1 :(得分:3)

您实际上可以在包装器元素上附加Click事件侦听器,并使用li(或对于IE event.target)过滤掉单击的event.srcElement元素。

尝试下面的代码段(不是生产代码,它应该增强,特别是对于浏览器兼容性)。这个解决方案的好处是你不必注意动态添加到包装元素的新元素...

&#13;
&#13;
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);
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 2 :(得分:2)

不确定。为什么不更好地利用document.querySelectorAll?它确实使用了css的语法,因此我们可以一次抓取它们。

也许是这样的:

{
 liList = document.querySelectorAll('.work li');
 var i, n = liList.length;
 for (i=0; i<n; i++)
  liList[i].addEventListener(eventName, functionName, useCapture);
}

答案 3 :(得分:2)

querySelectorAll()是非常强大的工具。

我为你做了JSFiddle

代码仍然足够好看和可读:

for (var item of document.querySelectorAll(".work li")) {
 item.addEventListener("click", function (evt) {
     evt.target.classList.add("active");
 }, false);
}

另一种方法是(如果您仍想使用forEach进行迭代):

Array.prototype.forEach.call(document.querySelectorAll(".work li"), function(item) {
   item.addEventListener("click", function (evt) {
        evt.target.classList.add("active");
   }, false);
});

此处(JSFiddle)我们通过querySelectorAll(".work li")NodeList calling方法对Array.prototype.forEach进行了整理。

这里(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);
});

最后,如果一次只有一个活动元素(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);
});

答案 4 :(得分:1)

人们抱怨使用原生DOM方法的冗长,但没有问题,一些战略性的糖无法帮助。首先写下你喜欢的方式:

eachClass('work', function(work) {
    eachSelector(work, 'li', function(li) {
        on(li, 'click', function() {
            eachChild(parent(li), active.remove);
            active.add(li);
        });
    });
});

现在您只需撰写eachClasseachSelectoreachChildparenton,如下所示。他们每人约2-3行。你只需要写一次,瞧,你有自己的小库。

function eachClass(cls, fn) {
    [].forEach.call(document.getElementsByClassName(cls), fn);
}

什么是active?它是我们可以创建的用于设置和删除特定类的小对象:

function makeClassSetter(cls) {
    return {
        add: function(elt) { elt.classList.add(cls); },
        remove: function(elt) {elt.classList.remove(cls); }
    };
}
var active = makeClassSetter('active');

等等。优秀的程序员不断构建小框架和层,实用程序和迷你语言。这就是使他们的代码紧凑,可读,减少错误并且更容易编写的原因。

答案 5 :(得分:0)

我也一直在寻找简洁但有用的解决方案。

这是我想出的最好方法:

tag = document.querySelectorAll('.tag');

tag.forEach(element => {
  element.addEventListener("mouseover", aFunction);
});

function aFunction(){
  this.style.background = "grey";
}

querySelectorAll是一个功能强大的方法,当存在用例时,foreach总是更短并且更易于使用。 我更喜欢给函数命名并添加它们,尽管我想如果愿意的话,您可以轻松地用foreach中的另一个箭头函数替换它