删除先前添加的课程无法正常工作

时间:2019-08-16 14:56:30

标签: javascript

我正在用js向文章中添加一个类,它的全部正常工作,没问题。但是,如果我想删除该类,则此方法将行不通。我不能使用jQuery。

 document.addEventListener("DOMContentLoaded", function() {
            var blogCard = document.querySelectorAll('[data-role="blogCard"]');
            for (let i = 0; i < blogCard.length; i++) {
                blogCard[i].addEventListener('click', function() {
                    this.classList.add('opened-card');

                    var closeCard = document.querySelectorAll('[data-role="closeCard"]');
                    closeCard[0].addEventListener('click', function() {
                        var pThis = this.parentNode;
                        pThis.classList.remove('opened-card');
                    });

                });
            };

        });
    <div class="card" data-role="blogCard">
        <button data-role="closeCard">Close</button>
    </div>

我试图将“删除”更改为“添加”,并且效果很好,它添加了类。但是当我回去删除它时将无法工作。

3 个答案:

答案 0 :(得分:4)

由于click事件传播到父对象,因此实际上在此处触发了两个单击处理程序。 button.closeCard单击处理程序将删除该类,但div.blogCard会将其添加回该类。 :)这就是为什么添加一个类可以但不能删除的原因。

要解决此问题,您可以阻止事件在第二个处理程序中传播,或者仅使用在div.blogCard上设置的组合事件处理程序,并根据event.target选择行为(本质上实现委托) 。像这样:

blogCard[i].addEventListener('click', function(event) {
  if (event.target.getAttribute('data-role') === 'closeCard') {
    this.classList.remove('opened-card');
  }
  else {
    this.classList.add('opened-card');
  }
});

实际上,您可能会考虑使用相同的技术-在目标DOM inside 事件处理程序中检查目标的位置-通过将单个事件处理程序应用于这些卡的父级(如果有单个父级,则为课程):

// a single event handler set on some 'cardContainer' element
var blogCard = event.target.closest('[data-role="blogCard"]');
if (blogCard) {
  var action = event.target.closest('[data-role="closeCard"]') ? 'remove' : 'add';
  blogCard.classList[action]('opened-card');
}

正如@IronFlare正确提到的那样,这不仅比单独循环处理每张卡具有更高的性能和可读性,而且还可以在设置事件处理程序之后处理所有添加到DOM中的卡。

答案 1 :(得分:2)

如其他答案所示,

像这样在循环中分配事件侦听器是可能,但这不是最为简单或有效的选择。将全局点击侦听器与Element.matches()条件一起使用会更好,因为:

  • 在代码中更容易理解。
  • 它效率更高,并且在页面可交互后不立即依赖同步循环。
  • 它可以让您在DOMContentReady开火后添加其他卡牌,而无需任何特殊处理;单击时,所有与选择器匹配的元素都将被侦听器“听到”。

window.addEventListener("click", function(e) {
  console.log("Click!", e.target);
  if (e.target.matches("[data-role='blogCard']")) {
    e.target.classList.add('opened-card');
  }
  if (e.target.matches("[data-role='closeCard']")) {
    e.target.parentElement.classList.remove('opened-card');
  }
});
.opened-card {
  background: red;
}
<div class="card" data-role="blogCard">
  <button data-role="closeCard">Close</button>
</div>

答案 2 :(得分:1)

在您的代码中,每次“打开”卡片时,都会将事件添加到文档的第一个“ closeCard”中。

如果您有1张以上的卡,则此操作将无效。

尝试添加2个单独的事件:


        document.addEventListener("DOMContentLoaded", function() {
            // get blogCards
            var blogCard = document.querySelectorAll('[data-role="blogCard"]');
            // get closeCards
            var closeCard = document.querySelectorAll('[data-role="closeCard"]');

            for (let i = 0; i < blogCard.length; i++) {
                // apply open event 
                blogCard[i].addEventListener('click', function() {
                    this.classList.add('opened-card');
                });
                // apply respective close event
                // this assumes you have one close button for every blogCard
                // if it's not the case then maybe you should scope the querySelectors
                closeCard[i].addEventListener('click', function() {
                    var pThis = this.parentNode;
                    pThis.classList.remove('opened-card');
                });

            };
        });

此代码可以优化(如果您知道blogCard和closeCard之间的DOM关系,则无需单独查询两个“角色”),但我将其留给OP