带有 transitionend 事件的错误未正确删除 CSS 类

wjv*_*vic 6 html javascript css

我最近一直在学习更多的 JavaScript,因为我将在即将到来的实习中大量使用它。公司推荐了很多不同的资源给我看,但我的一个朋友也推荐了这个网站:

https://javascript30.com/

我决定试一试,昨天开始了第一个项目,它是一个 JavaScript 架子鼓。

所以整个项目并不难,但我注意到发生了一个小错误。因此,该项目将一个“keydown”事件侦听器附加到几个不同的 HTML 元素,这些元素在按下任一 ASDFGHJKJL 时触发。除了播放声音之外,侦听器还为元素附加了一个类,为按下的键增加了一点效果。

为了移除这个类,我们在元素上附加了一个 transitionend 监听器。因此,当按下键后 CSS 属性完成转换时,类将被删除,图形在浏览器中恢复正常。

现在,如果您缓慢按下按键,它就可以正常工作。但我注意到,如果你按住一个键一两秒钟,看起来好像添加的类永远不会被删除并且图形是永久的。然后我检查了控制台,注意到 transitionend 事件一旦像那样卡住就永远不会触发。

这是来自完成文件的代码。

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

function playSound(e) 
{
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
    if (!audio) 
      return;

    key.classList.add('playing');
    audio.currentTime = 0;
    audio.play();
}
Run Code Online (Sandbox Code Playgroud)

这就是我们附加 transitionend 侦听器的方式,不确定这是否与它有关

const keys = Array.from(document.querySelectorAll('.key'));
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
Run Code Online (Sandbox Code Playgroud)

所以我们在 removeTransition 函数中加入了 if 循环,这样它就不会删除每个正在转换的属性的类,但我注意到当你删除 if 循环时,页面工作得很好。

所以我不完全确定 transitionend 事件发生了什么,以及为什么它一旦像那样卡住就完全停止触发。而且,为什么它在 if 循环被删除时有效。也许当它循环遍历所有属性时,它有更多机会删除该类?没有线索。想知道这里是否有人可能知道发生了什么。

编辑:

我稍微改变了 playSound 函数,试图找到它的底部。

function playSound(e)
{
    const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
    const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
    if(!audio)
    {
        return;
    }
    if(key.classList.contains("playing"))
    {
        console.log("True");
        key.classList.remove("playing");
    }
    audio.currentTime = 0;
    audio.play();
    console.log("adding");
    key.classList.add("playing");
}
Run Code Online (Sandbox Code Playgroud)

因此,我添加了一个 if 语句,用于检查元素一旦卡住就已包含“播放”类。我还添加了一个 classList.remove 调用以尝试在同一分支中摆脱它。我发现一旦它卡住了,我再次按下该键,它就会分支到 if 语句并打印控制台消息,但仍然不会删除“播放”类。

sha*_*ncs 1

造成无法删除 CSS 类问题的原因有 2 个:

  1. 标记动画是否播放的标志是 class playing,这也是打开/关闭动画的关键。这样的设定让问题变得非常复杂。
  2. 标志/标记更改是添加/删除 class playing。这是一个 DOM 操作,会花费比必要的时间更多的时间。

为了解决这个问题,最好将标志/标记信息存储在内存中(作为变量),如果转换未完成,则停止键盘事件侦听器。

将标志/标记信息存储在变量中:

var keysUnderTransition = {
  "65": 0,
  "83": 0,
  "68": 0,
  "70": 0,
  "71": 0,
  "72": 0,
  "74": 0,
  "75": 0,
  "76": 0
};
Run Code Online (Sandbox Code Playgroud)

因为每个元素在一个周期内都会有 2 次转换。0意味着元素不在转换中(没有剩余转换),2意味着它处于第一个转换(剩余 2 个转换需要完成),并且1意味着它处于最后一个转换(剩余 1 个转换需要完成)。

在函数 中playSound,检查并标记标志:

if (keysUnderTransition[e.keyCode]) {
  return;
}
keysUnderTransition[e.keyCode] = 2;
Run Code Online (Sandbox Code Playgroud)

在函数 中removeTransition,恢复标志/标记:

  var dataKey = this.getAttribute('data-key');
  var underTransitionVal = keysUnderTransition[dataKey];
  if (underTransitionVal === 2) {
    setTimeout(function() {
      keysUnderTransition[dataKey] = 1;
    }, 50);
  } else if (underTransitionVal === 1) {
    keysUnderTransition[dataKey] = 0;
  }
Run Code Online (Sandbox Code Playgroud)

50在上面的代码中使用在第二次转换结束之前更改标志。

这是一个工作的jsfiddle