HTML删除和添加事件侦听器

时间:2019-05-17 20:39:22

标签: javascript

假设我有一系列详细信息HTML元素,当切换一个元素时,所有这些元素我都想切换为打开状态。每个元素看起来都像这样:

<details class="basic" data-target="basic">
     <summary>Click me 1</summary>
     Some descriptive text 1
<details>

我在每个元素的切换事件上添加一个事件侦听器:

addEventListeners();

function addEventListeners(){ 

      var deets = document.getElementsByTagName("details");
      for(var i = 0; i < deets.length; i++){     
           deets[i].addEventListener("toggle", togGroups, false);
      }

 }

function togGroups(event){
    
    var targetClass = this.getAttribute("data-target"),
    elements = document.getElementsByClassName(targetClass),
    open = this.open; //currently open or not?

    for(var i = 0; i < elements.length; i++){ 
        let el = elements[i];
        
        //Remove the event listener to stop triggering
        //the toggle event when setting every element's "open"
        //status
        el.removeEventListener("toggle", togGroups, false);
        el.open = open;
    }    

    //Scroll the originally-toggled element into view
    this.scrollIntoView({
        behavior: 'smooth',
        block: 'start'
    });

    //Re-add event listeners for the next toggle
    addEventListeners();

}

此代码可以打开所有元素,但是我无法弄清楚如何滚动到最初切换的元素。而是滚动到最后一个切换元素-基本上是页面底部。我认为,如果在for ..循环中未删除任何事件侦听器,则该行为将是有意义的-每当“ open”属性设置为true时,都会触发每个元素的toggle事件,因此该函数将触发多次并最终“这”将是最后一个元素。但是,我试图将其删除,然后仅在打开每个元素之后才将其重新设置。

顺便说一句,如果我删除了最后的addEventListeners()行,它可以正确滚动,但随后我将失去再次单击的能力。

我将不胜感激。

非常感谢。

2 个答案:

答案 0 :(得分:2)

我们不需要删除任何事件侦听器,也不需要绑定到每个<details>。我们需要的是一个注册到祖先标签*的事件监听器,用于无限数量的后代标签。

详细信息在演示中进行了评论。 <details>中也有更多详细信息。
* 它甚至可以是窗口或文档,但除非是用于关键事件,否则不建议使用

// Reference <main>
const main = document.querySelector('main');

// Register <main> to click event -- fire callback
main.addEventListener('click', callback);

// Callback pass the Event Object
function callback(event) {
  // Prevent <details> from toggling it's own [open]
  event.preventDefault();
  // Reference the clicked tag
  const tgt = event.target;
  // Collect all <details> into a NodeList
  const dtl = document.querySelectorAll('details');
  // Iterate through NodeList -- on each <details> toggle [open]
  for (let detail of dtl) {
    detail.toggleAttribute('open');
  }
  // Scroll clicked tag into center view
  tgt.scrollIntoView({
    behavior: 'smooth',
    block: 'center'
  });
  // Terminate function
  return false;
}
:root {
  font: 400 16px/1.2 Arial
}

body {
  overflow-x: hidden;
  overflow-y: scroll;
}

main {
  padding: 40vh 0;
}

details {
  cursor: pointer;
  margin: 15px auto;
  border-bottom: 4px ridge #444;
  border-top: 4px ridge #444;
  padding: 5px 5px 10px
}

details:focus-within {
  outline: 3px solid cyan;
}

summary {
  font-family: Verdana;
  padding: 5px;
  border-right: 5px ridge #444;
  border-left: 5px ridge #444;
}
<main>
  <details>
    <summary>Read More...</summary>
    Register an event to an ancestor tag that all targeted tags share in common. In this demo that ancestor tag is <code>&lt;main&gt;</code>.
  </details>

  <details>
    <summary>Further Details</summary>
    Write the callback function so that it only reacts to events triggered on specific tags (ie <code>&lt;details&gt;</code>) and ignore the rest. This is possible because the click event propigates (or bubbles).
  </details>

  <details>
    <summary>Code Description</summary>
    This programming pattern is called Event Delegation.
  </details>
</main>

答案 1 :(得分:0)

您只需要添加一次发泄侦听器。.

 addEventListeners();

  function addEventListeners() {
    var deets = document.getElementsByTagName("details");
    for(var i = 0; i < deets.length; i++){
      deets[i].addEventListener("toggle", togGroups, false);
    }
  }

  function togGroups(event) {
    var targetClass = this.getAttribute("data-target");
    var elements = document.getElementsByClassName(targetClass);

    for(var i = 0; i < elements.length; i++){
      let el = elements[i];
      el.open = this.open;
    }

    // Scroll the originally-toggled element into view
    this.scrollIntoView({
      behavior: 'smooth',
      block: 'start'
    });
  }