Search code examples
javascriptcss-selectorscss-animationsevent-delegationhtml-tag-details

Is there any way to effectively manage a large number of tags through JavaScript? (Animation is added as the <details> tag is opened and closed)


I want to put a blinking animation in the details tag, stop the animation when the tag is opened, and blink again when the tag is closed. A structure in which a detail tag is contained within a detail tag, and the number of detail tags is expected to increase.

I also saw JavaScript's event delegation, but it didn't work well because the structure was different.

Currently, I created a script for each tag and pasted them one by one. Can this be managed with an efficient script?

Below is the code I wrote(pasted them one by one).

<body>
<details id="details_id_1">
<summary id="s_id1" class="blink">summary1</summary>
    <span>contents1</span>    
        <details id="details_id_2">
            <summary id="s_id2" class="blink">summary2</summary>
            <span>contents2</span>    
                <details id="details_id_3">
                    <summary id="s_id3" class="blink">summary3</summary>
                    <span>contents3</span>
                </details>
        </details>
</details>
</body>
<script>
    const details_1 = document.querySelector("#details_id_1");
    const summary = document.querySelector("#s_id1");   
    
    summary.addEventListener("click", (event) => {
        
        if(details_1.open){
            summary.classList.add("blink");
        }else{
            summary.classList.remove("blink");
        }
    });
</script>

<script>
    const details_2 = document.querySelector("#details_id_2");
    const summary_2 = document.querySelector("#s_id2"); 
    
    summary_2.addEventListener("click", (event) => {
        
        if(details_2.open){
            summary_2.classList.add("blink");
        }else{
            summary_2.classList.remove("blink");
        }
    });
</script>

<script>
    const details_3 = document.querySelector("#details_id_3");
    const summary_3 = document.querySelector("#s_id3"); 
    
    summary_3.addEventListener("click", (event) => {
        
        if(details_3.open){
            summary_3.classList.add("blink");
        }else{
            summary_3.classList.remove("blink");
        }
    });
</script>

Thanks a lot!


Solution

  • Event delegation will still work here. All you need is to match up a clicked <summary> element with its parent <details> element.

    document.body.addEventListener('click', (e) => {
      if (e.target.matches('summary')) {
        e.target.classList.toggle('blink', e.target.parentElement.open);
      }
    });
    .blink {
      color: orange;
      transition-property: color;
      transition-duration: 1s;
    }
    <details id="details_id_1">
      <summary id="s_id1" class="blink">summary1</summary>
      <span>contents1</span>
      <details id="details_id_2">
        <summary id="s_id2" class="blink">summary2</summary>
        <span>contents2</span>
        <details id="details_id_3">
          <summary id="s_id3" class="blink">summary3</summary>
          <span>contents3</span>
        </details>
      </details>
    </details>