Keydown 事件触发 classList.remove 如果连续按下太多次按键将不起作用

Keydown event triggering classList.remove doesn't work if the key is pressed too many times in a row

我已经实现了 Wes Bos 的 Javascript 30 课程的架子鼓 (https://www.youtube.com/watch?v=VuN8qwZoego),但有些地方很奇怪。

当按下一个键时,我添加一个 class 和 key.classList.add('playing'),它将键的 div 转换为 border-color: yellow,除此之外,然后删除class 再次 this.classList.remove('playing'); 一旦被 transitionend 事件触发。

它工作正常,直到我尝试非常快地多次按下某个键。然后,最终 .playing class "sticks" 到 div,即不再删除。

window.addEventListener('keydown', (e) => {
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
    if (!audio) return; //stop the function from running
    audio.currentTime = 0;
    audio.play();
    key.classList.add('playing');
});

function removeTransition(e){
    if (e.propertyName !== 'transform'){
        return;
    }
    this.classList.remove('playing');
}

const keys =  document.querySelectorAll('.key');
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
.drum-keys{
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 100px;
    padding: 0;
}

.key{
    display: flex;
    height: 70px;
    width: 60px;
    background-color: rgb(0,0,0, .2);
    font-style: bold;
    margin: 1em;
    border: 2px solid lightgrey;
    flex-direction: column;
    transition: 0.09s;
    text-align: center;
}

.key h1 {
    font-size: 1.7em;
    margin-top: 10px;
    margin-bottom: 0;
}

.key p {
    font-size: 0.7em;
    margin-top: 5px;
}

.drum-keys li{
    list-style-type: none;
}

.playing{
    border-color: yellow;
    box-shadow: 0 0 10px grey;
    transform: scale(1.1);
}
<main class="site-content">
  
    <div class="playground-main">

        <ul class="drum-keys">
            <li>
                <div data-key="65" class="key"><h1>A</h1><p>CLAP</p></div>
            </li>
            <li>
                <div data-key="83" class="key"><h1>S</h1><p>HIHAT</p></div>
            </li>
            <li>
                <div data-key="68" class="key"><h1>D</h1><p>KICK</p></div>
            </li>
            <li>
              <div data-key="70" class="key"><h1>F</h1><p>OPENHAT</p></div>
            </li>             
            <li>
                <div data-key="71" class="key"><h1>G</h1><p>BOOM</p></div>
            </li>
            <li>
              <div data-key="72" class="key"><h1>H</h1><p>RIDE</p></div>
            </li>
            <li>
                <div data-key="74" class="key"><h1>J</h1><p>SNARE</p></div>
            </li>
            <li>
                <div data-key="75" class="key"><h1>K</h1><p>TOM</p></div>
            </li>
            <li>
                <div data-key="76" class="key"><h1>L</h1><p>TINK</p></div>
            </li>
        </ul>
     
    </div>

问题出在您对 keydown 动作的竞标,如果您按住它,它会比 transitionend 触发多次。因为它发生了多次,你最终得到 class 'playing' 并且没有发生转换,因此 'transitionend' 事件不再被触发。

将 keyup 更改为 keydown,应该没问题。

如果你像这样在控制台登录这两个函数并观察控制台,你可以很容易地看到这一点

window.addEventListener('keyup', (e) => {
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
/*     if (!audio) return; //stop the function from running
     *//*     audio.currentTime = 0;
    audio.play(); */
    key.classList.add('playing');
    console.log('keydown');
});

function removeTransition(e){
    if (e.propertyName !== 'transform'){
        return;
    }

this.classList.remove('playing');
console.log('transition end');
}

const keys =  document.querySelectorAll('.key');
keys.forEach(key => key.addEventListener('transitionend', removeTransition));

如果您真的热衷于使用 'keydown',我认为您可以使用 key.classList.toggle('playing')