阻止事件传播到同一 Web 组件的另一个实例?

问题描述

我有一个带有自定义事件的子 Web 组件 (exercise-input),该事件将一些数据发送到嵌套在其中的父 Web 组件 (muscle-group)。问题是当我添加将父组件的其他实例添加页面中,子组件中更新父组件中值的按钮将在父组件的每个实例上更新该值。

我在子进程中使用了 document.dispatchEvent(numSetsDecrease),在父进程中使用了 document.addEventListener - 我需要用什么来替换“文档”?我需要以某种方式stop propagation吗?

子组件:

    const exerciseInputTemplate = document.createElement('template');
    exerciseInputTemplate.innerHTML = ` 
    
      <div class="exercises">
        <hr>
        <div class="exerciseSetsRepRange">
          <div class="sideBySide">
              <input type="text" placeholder="Exercise">
              <div class="numSetsBtns">
                <button class="subtractSetBtn">-</button>
                <span class="numSets"></span>
                <button class="addSetBtn">+</button>
              </div>
          </div>
          <div class="sideBySide">
            <input type="number" placeholder="Rep Range Low" min="1">
            <input type="number" placeholder="Rep Range High" min="1">
          </div>
        </div>
      </div>
    `;
     
    class ExerciseInput extends HTMLElement {
     constructor() {
       super(); 
       this.attachShadow({ mode: 'open'});
       this.shadowRoot.appendChild(exerciseInputTemplate.content.cloneNode(true));
     }
    
      connectedCallback() {
        const subtractSetBtn = this.shadowRoot.querySelector('.subtractSetBtn');
        const numSets = this.shadowRoot.querySelector('.numSets');
        const addSetBtn = this.shadowRoot.querySelector('.addSetBtn');
        numSets.textContent = 3 + ' sets';
    
        // Click listner for subtract set btn with nested custom event
        subtractSetBtn.addEventListener('click',function(event){
          let numSetsInt = parseInt(numSets.textContent);
          numSetsInt -= 1;
          numSets.textContent = numSetsInt + ' sets';
    
          if (parseInt(numSets.textContent) === 0) {
            subtractSetBtn.disabled = true;
          }
    
          // Custom event sending -1 to the parent
          const numSetsDecrease = new CustomEvent('numSetsDecrease',{
            bubbles: false,detail: {
              numSetValue: -1
            }
          });
          document.dispatchEvent(numSetsDecrease);
        });
    
        // Click listner for add set btn with nested custom event
        addSetBtn.addEventListener('click',function(event){
          let numSetsInt = parseInt(numSets.textContent);
          numSetsInt += 1;
          numSets.textContent = numSetsInt + ' sets';
    
          if (parseInt(numSets.textContent) > 0) {
            subtractSetBtn.disabled = false;
          }
    
          // Custom event sending +1 to the parent
          const numSetsIncrease = new CustomEvent('numSetsIncrease',detail: {
              numSetValue: 1
            }
          });
          document.dispatchEvent(numSetsIncrease);
        });
      }
    }
   
    window.customElements.define('exercise-input',ExerciseInput);

父组件:

    const muscleGroupTemplate = document.createElement('template');
    muscleGroupTemplate.innerHTML = ` 
      <form class="programBuilder">
        <div class="inputDiv muscleGroupDiv">
          <input id="muscleGroupInput" name="muscleGroupInput" list="muscleGroupsList" placeholder="Muscle Group">
          <datalist id="muscleGroupsList">
          </datalist>
        </div>
    
        <div class="inputDiv setsDiv">
          <label for="setsRange" class="setsValue"></label>
          <input type="range" min="1" max="30" class="setsRange" value="15">
          <p class="numSetsOutput"></p>
        </div>
    
        <exercise-input></exercise-input>
        <exercise-input></exercise-input>
        <exercise-input></exercise-input>
    
        <button class="addExerciseBtn">Add Exercise</button>
      </form>
    `;
     
    class MuscleGroup extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open'});
        this.shadowRoot.appendChild(muscleGroupTemplate.content.cloneNode(true));
      }
    
      connectedCallback() {
        // Updating the number sets output with data from the child elements
        const numSetsOutput = this.shadowRoot.querySelector('.numSetsOutput');
        numSetsOutput.textContent = '9 total sets';
    
        document.addEventListener('numSetsDecrease',function(event){
          let numSetsOutputValue = parseInt(numSetsOutput.textContent);
          numSetsOutputValue += event.detail.numSetValue;
          numSetsOutput.textContent = `${numSetsOutputValue} total sets`; 
        });
        
        document.addEventListener('numSetsIncrease',function(event){
          let numSetsOutputValue = parseInt(numSetsOutput.textContent);
          numSetsOutputValue += event.detail.numSetValue;
          numSetsOutput.textContent = `${numSetsOutputValue} total sets`;
        });
      }
    }
     
    window.customElements.define('muscle-group',MuscleGroup);

任何帮助将不胜感激!我一整天都在想办法解决这个问题。

解决方法

一个事件冒泡 DOM。

https://javascript.info/bubbling-and-capturing

document.addEventListener 从其子项中捕获所有命名事件

代替你:

  • 来自按钮的dispatchEvent
  • 事件需要bubbles:true composed:true才能穿越shadowRoots
  • <muscle-group> 处的侦听器然后捕获冒泡事件并对其进行处理
    它只接收来自它的孩子的事件!
  • 如果你没有嵌套组你没有使用stopPropagation,事件冒泡到document没有伤害立>

附注。您的 <exercise-iput> 有 2 个按钮,它们的作用相同,一个是加,一个是减。

在您复制/粘贴第一个按钮代码的那一刻,“This Needs A Component” 警报应该会警告您创建一个 <exercise-input-button add="-1">

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...