在多个元素上使用addEventListener,在未找到特定元素时避免使用TypeError

时间:2018-05-05 18:30:55

标签: javascript typeerror addeventlistener bodymovin

我分别使用两个简单的addEventListener mouseenter和mouseleave函数来播放和停止动画(Bodymovin / SVG动画,但我怀疑这个事实无关紧要)。

因此,以下工作正常:

document.getElementById('animationDiv').addEventListener('mouseenter', function(){ 
    animation.play(); 
}) 

(HTML不会更简单:相关部分只是一个由脚本填充的空div占位符 - 即<div id="animationDiv"></div>

我可以将它放在与操作动画代码的文件相同的文件中,或者我可以将它放在单独的&#34;触发器中。文件,在网站页脚中加载了两个文件(以及处理所需的其他文件)。

当我需要能够为给定页面上可能出现或可能不出现的多个类似动画中的任何一个设置触发器时,就会出现问题。

如果页面上只有两个可动画元素中的一个,那么两组触发器中的一个将引发错误。如果两个此类触发器中的第一个不存在,则不会处理第二个触发器,这意味着动画将失败。或者至少对我来说是什么样的事情正在发生。

所以,为了清楚起见,如果我为同一页面添加以下两个触发器,并且存在以下两个元素中的第一个,则动画将在mouseenter上播放。如果仅存在第二个,则其动画不会被触发,显然是因为第一个引发了错误。

document.getElementById('firstAnimationDiv').addEventListener('mouseenter', function(){ 
    firstAnimation.play(); 
})
document.getElementById('secondAnimationDiv').addEventListener('mouseenter', function(){ 
    secondAnimation.play(); 
})

目前我可以通过创建多个触发器文件来解决这个问题,每个动画一个,并且只有在我知道动画元素存在时才设置它们加载,但是这种方法在我使用时效率会越来越低每页多个动画,内容可能被更改的页面。

我已经看过try / catch方法以及事件委派方法,但到目前为止,它们对于处理这个简单问题似乎有点复杂,如果合适的话。

是否有一种有效且灵活的标准方法可以防止或正确处理未找到的元素的错误,从而仍然可以处理后续功能?或者我错过了其他什么或以某种方式误读了我遇到的错误和功能失败?

为什么我选择了解决的问题(加上工作代码)

我很容易通过Baoo工作做出简单,直接反应的答案。

我无法通过Patrick Roberts和Crazy Train的工作得到以下答案,但毫无疑问,我未开发的js技能完全是错误的。当我有时间,或者下一个问题出现在一个更复杂的实施中(可能很快!)时,我会再看看他们的解决方案,看看我是否可以使它们工作或者我是否可以可以通过完全成熟的编码示例来制定更好的问题。

最后,为了让那些可能正在寻找Bodymovin动画答案的人明白,而且js甚至比我的还要弱,以下是工作代码,所有这些都添加到同一个文件中,其中一个更大的集合Bodymovin动画的构建,使我无需创建单独的触发器文件,并防止TypeErrors和功能受损。

//There are three "lets_talk" animations that can play - "home," "snug," and "fixed"
//and three types of buttons needing enter and leave play and stop triggers
let home = document.getElementById('myBtn_bm_home');

if (home) home.addEventListener('mouseenter', function() { 
             lets_talk_home.play(); 
});
if (home) home.addEventListener('mouseleave', function() { 
             lets_talk_home.stop(); 
});

let snug = document.getElementById('myBtn_bm_snug');

if (snug) snug.addEventListener('mouseenter', function() { 
             lets_talk_snug.play(); 
});
if (snug) snug.addEventListener('mouseleave', function() { 
             lets_talk_snug.stop(); 
});

let fixed = document.getElementById('myBtn_bm_fixed');

if (fixed) fixed.addEventListener('mouseenter', function() { 
             lets_talk_fixed.play(); 
});
if (fixed) fixed.addEventListener('mouseleave', function() { 
             lets_talk_fixed.stop(); 
});

在典型的底层HTML(它由PHP函数生成并考虑到其他条件,因此每个按钮不相同),此刻看起来像这样 - 尽管我会在削减数据属性和类,因为我目前还没有使用它们。我提供的机会是有人在那里看到重要或有用的东西。

<div id="letsTalk" class="lets-talk">
    <a id="myBtn" href="#"><!-- a default-prevented link to a pop-up modal --> 
        <div class="bm-button" id="myBtn_bm_snug" data-animation="snug"></div><!-- "snug" (vs "fixed" or "home" is in both instances added by PHP -->
    </a>
</div>

显然,一个更简约灵活的答案可能 - 而且可能应该 - 写出来。在这个注意事项中,在一个条件中正确地组合播放和停止监听器将是显而易见的第一步,但是我甚至在第一次或第二次尝试时做得太多了。也许晚些时候/下一次!

再次感谢所有提供答案的人。我不会要求你尝试将工作解决方案压缩到你建议的框架中 - 但我不会要求你不要......

3 个答案:

答案 0 :(得分:3)

只需编写代码,以便在元素不存在时不会抛出错误,只需检查元素是否存在x1=[0.0,0.1,0.2] y1=[.2,.4,.5] x2=[0.0,0.2,0.2] y2=[.2,.9,.5] x3=[0.0,0.1,0.5] y3=[.2,.4,.0]

result = {"x1": [0.0,0.1,0.2],
          "y1": [.2,.4,.5],
          "x2": [0.0,0.2,0.2]
          "y2": [.2,.4,.5],
          "x3": [0.0,0.1,0.2],
          "y3": [.2,.4,.0]}

答案 1 :(得分:3)

您可以使用委托事件处理稍微改变一下。与mouseover不同,mouseenter会向其祖先元素冒泡,因此您可以向包含每个#animationDiv的祖先元素添加单个事件侦听器,并启用event.target.id调用正确的play()方法:

document.getElementById('animationDivContainer').addEventListener('mouseover', function (event) {
  switch (event.target.id) {
  case 'firstAnimationDiv':
    return firstAnimation.play();
  case 'secondAnimationDiv':
    return secondAnimation.play();
  // and so on
  }
});

您还可以避免使用id并使用更加语义正确的属性(如data-animation)作为此方法与@ CrazyTrain之间的折衷方案:

document.getElementById('animationDivContainer').addEventListener('mouseover', function (event) {
  // assuming <div data-animation="...">
  // instead of <div id="...">
  switch (event.target.dataset.animation) {
  case 'first':
    return firstAnimation.play();
  case 'second':
    return secondAnimation.play();
  // and so on
  }
});

答案 2 :(得分:2)

首先,重构HTML以向所有占位符div添加公共类,而不是使用唯一ID。还要添加data-animation属性以引用所需的动画。

<div class="animation" data-animation="first"></div>
<div class="animation" data-animation="second"></div>

data-属性应具有定位相应动画的值。

(如@PatrickRobers所述,DOM选择可以基于data-animation属性,因此class并非真正需要。)

由于您的动画是作为全局变量保存的,因此您可以使用data-animation的值来查找该变量。但是,如果它们不是全球性的,那就更好了,但它们更像是一个共同的对象。

const animations = {
  first: null,  // your first animation
  second: null, // your second animation
};

然后按类选择占位符元素,并使用data属性查看动画是否存在,如果存在,则播放它。

const divs = document.querySelectorAll("div.animation");
divs.forEach(div => {
  const anim = animations[div.dataset.animation];
  if (anim) {
    anim.play(); // Found the animation for this div, so play it
  }
});

这样您就可以保证只使用存在的占位符div和存在的动画。

(如上所述,使用数据属性的选择可以const divs = document.querySelectorAll("div[data-animation]");完成,因此class变得不必要了。)