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

Stop event from propagating to another instance of same web component?

我有一个 child 网络组件 (exercise-input),它带有一个自定义事件,可以将一些数据发送到我的 parent 网络组件 (muscle-group) 它是问题是,当我将 parent 组件的其他实例添加到页面时,child 组件中更新 parent 组件中的值的按钮将更新该值在 parent 组件的每个实例上。

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

child 组件:

    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', {
            bubbles: false,
            detail: {
              numSetValue: 1
            }
          });
          document.dispatchEvent(numSetsIncrease);
        });
      }
    }
   
    window.customElements.define('exercise-input', ExerciseInput);

parent 组件:

    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 从其 children

捕获 all 命名事件

代替你:

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

PS。您的 <exercise-iput> 有 2 个按钮,它们的功能相同,一个加,一个减。

当您 copy/pasted 第一个按钮代码时,"This Needs A Component" 警报应该警告您创建 <exercise-input-button add="-1">