首页 > 解决方案 > 单击侦听器上的 e.stopPropagation 不起作用

问题描述

我正在尝试构建随机颜色生成器。它为每种生成的颜色创建框。您可以将鼠标悬停在它们上,然后复制或锁定您喜欢的颜色。

当我点击复制按钮或背景部分时出现问题。它也会触发锁定按钮,锁定按钮也会触发复制按钮。

我不知道为什么会发生这种情况,因为e.stopPropagation)使用了函数。

可视化:RGB 颜色发生器

我是 js 世界的新手,所以如果你发现任何可以修复的东西,我会很感激让我知道如何改进它

完整代码:

//selecting JS OBJECTS

const background = document.querySelector('.background');
const copy = document.querySelector('#copy');
const p = document.querySelector('p');
const body = document.querySelector('body');
const colorContainer = document.querySelector('.color-container');

let spacebarFirstCount = 0;

body.addEventListener('keydown', (e) => {
    if (e.key === " ") {
        const color = generateRGB();
        changeBackgroundColor(color);
        p.innerText = color;

        if (copy.innerText != "copy") {
            copy.innerText = "copy";
        }

        addColorBox(color);

        if (spacebarFirstCount !== 3) {
            if (spacebarFirstCount === 2) {
                document.querySelector('.spacebar').remove();
            }
            spacebarFirstCount++;
        }
    }
});

copy.addEventListener('click', () => {
    navigator.clipboard.writeText(p.innerText);
    copy.innerText = "done";
})

const addColorBox = (color) => {
    const colorBox = document.createElement('div');
    colorBox.style.backgroundColor = color;
    colorBox.classList.toggle('color-box');

    colorBox.addEventListener('mouseover', hover2)

    fixBoxNumber();

    colorContainer.append(colorBox);


}

const generate255 = () => {
    return Math.floor(Math.random() * 255) + 1;
}

const generateRGB = () => {
    const r = generate255();
    const g = generate255();
    const b = generate255();
    const alpha = (Math.floor((Math.random() + 0.1) * 100) / 100);

    return `rgb(${r},${g},${b},${alpha})`;

}

const changeBackgroundColor = (color) => {
    background.style.backgroundColor = color;
}

function hover2() {
    const copyText = document.createElement('span');
    const lockText = document.createElement('span');

    copyText.append("copy");

    copyText.classList.toggle("hoverText");
    lockText.classList.toggle("hoverText");
    
    this.classList.toggle('box-animation');
    this.removeEventListener('mouseover', hover2);

    const currentColor = background.style.background;
    changeBackgroundColor(this.style.backgroundColor);

    function copyToClipboard(event) {
        navigator.clipboard.writeText(this.style.backgroundColor);
        copyText.innerText = "copied";
        this.removeEventListener('click', copyToClipboard);
        event.stopPropagation();
    }

    function leaving() {
        copyText.remove();
        lockText.remove();
        this.classList.remove('box-animation');
        this.addEventListener('mouseover', hover2);
        this.removeEventListener('click', copyToClipboard);
        this.removeEventListener('mouseleave', leaving);
        this.removeEventListener('click', locking);
    }

    this.addEventListener('click', copyToClipboard);

    this.addEventListener('mouseleave', leaving)



    if (!this.hasAttribute('locked')) {
        lockText.append("lock");
    }
    else {
        lockText.append("locked");
    }


    // ! Stop propagation nie działa


    function locking(e) {

        if (!this.hasAttribute('locked')) {
            this.setAttribute('locked', 'true');
            lockText.innerText = "locked";
        } else {
            this.removeAttribute('locked');
            lockText.innerText = "lock";
        }
        e.stopPropagation();
    }

    this.addEventListener('click', locking);



    this.append(lockText);
    this.append(copyText);


}

const fixBoxNumber = () => {
    if (colorContainer.childElementCount >= 19) {

        const deleteBoxes = Object.values(colorContainer.childNodes).filter((box) => !box.hasAttribute("locked"));

        if (deleteBoxes.length < 12) {
            for (let i = 0; i < deleteBoxes.length; i++) {
                deleteBoxes[i].remove();
            }
        }
        else {
            for (let i = 0; i < 12; i++) {
                deleteBoxes[i].remove();
            }
        }

    }
}

标签: javascriptevent-listenerstoppropagation

解决方案


这是一个随机颜色的片段:

  • 您可以锁定/解锁颜色
  • 你可以复制一种颜色(好的,navigator.clipboard这里禁止使用它)
  • 您可以重新随机化颜色(锁定颜色保持不变)

const colorSquare = document.getElementById('color-square')
const btnRandomizeColors = document.getElementById('btn-randomize-colors')
const copiedColor = document.getElementById('copied-hex-color')

const randomColor = () => {
  return `#${Math.floor(Math.random()*16777215).toString(16)}`
}

// color object factory
const getRandomColor = () => ({
  locked: false,
  color: randomColor()
})

// init of color list
const initColorList = () => [...Array(16)].map(getRandomColor)

// color list class:
// - tracks state of colors (locked/not locked)
// - provides functions to manipulate color list
class ColorList {
  constructor() {
    this.colors = initColorList()
  }

  toggleLockedByIdx(idx) {
    this.colors[idx].locked = !this.colors[idx].locked
  }

  randomizeColors() {
    this.colors = this.colors.map(color => {
      if (color.locked) {
        return color
      } else {
        return getRandomColor()
      }
    })
  }
}

const colorList = new ColorList()

// single color square presentation
const singleSquareHtml = ({
  color,
  idx,
  locked
}) => {
  let html = ''
  html += `
    <div class="single-color-square" style="background-color:${color};">
      <button class="btn-ctrl btn-lock" data-idx="${idx}">${locked ? 'UNLOCK' : 'LOCK'}</button>
      <button class="btn-ctrl btn-copy" data-hexcolor="${color}">COPY</button>
    </div>
  `
  return html
}

// color square presentation
const colorSquareHtml = ({
  colorList
}) => {
  let html = ''
  colorList.colors.forEach(({
    color,
    locked
  }, idx) => {
    html += singleSquareHtml({
      color,
      idx,
      locked,
    })
  })
  return html
}

function lockColor(idx, colorList) {
  colorList.toggleLockedByIdx(idx)
  // to update the button label:
  updateColorSquareHTML({
    colorList
  })
}

function copyHexColor(hexColor) {
  // copying to clipboard source:
  // https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript
  // the fallback is not implemented here
  navigator.clipboard.writeText(hexColor).then(function() {
    console.log('Async: Copying to clipboard was successful!');
  }, function(err) {
    console.error('Async: Could not copy text: ', err);
  });
}

// updating color square HTML
// - adding btn handlers
const updateColorSquareHTML = ({
  colorList
}) => {
  colorSquare.innerHTML = colorSquareHtml({
    colorList
  })
  const lockBtns = document.querySelectorAll('.btn-lock')
  lockBtns.forEach(btn => {
    btn.addEventListener('click', function(e) {
      e.stopPropagation()
      const idx = Number(this.getAttribute('data-idx'))
      lockColor(idx, colorList)
    })
  })

  const copyBtns = document.querySelectorAll('.btn-copy')
  copyBtns.forEach(btn => {
    btn.addEventListener('click', function(e) {
      e.stopPropagation()
      const hexColor = this.getAttribute('data-hexcolor')
      copyHexColor(hexColor)

      // to display the color picked
      copiedColor.textContent = hexColor
    })
  })

}

updateColorSquareHTML({
  colorList: colorList
})

btnRandomizeColors.addEventListener('click', function() {
  colorList.randomizeColors()
  updateColorSquareHTML({
    colorList
  })
})
#container {
  width: 100%;
  height: 100%;
}

#color-square {
  display: grid;
  grid-template-columns: repeat(4, 80px);
  grid-template-rows: repeat(4, 80px);
}

.single-color-square {
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}

.btn-ctrl {
  display: none;
  margin: 4px;
}

.single-color-square:hover .btn-ctrl {
  display: block;
}
<div id="container">
  <div>
    <button id="btn-randomize-colors">
      RANDOMIZE COLORS
    </button>
    <br />
    <div id="copied-hex-color"></div>
  </div>
  <div id="color-square"></div>
</div>

你可以看到

  • 颜色列表操作被封装在ColorList类中:在该类之外没有任何变化或修改颜色列表。这意味着每次修改颜色列表后,都应该重新渲染视图(否则修改不会显示)
  • eventListeners 在每次重新渲染时添加:这在更大的范围内可能无效,但在这种大小下,它并没有真正的区别(浏览器倾向于删除不需要的侦听器,所以为什么要打扰?;))
  • 而不是class我本可以使用 a factory(现在更流行),但这并没有什么区别(source 1source 2等)

另外,我想说:在单独的文件中会更好 - 因此,如果您在这样的环境中工作,请尝试将其分解为更合理的块。


推荐阅读