2

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

因此,以下工作正常:

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

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

我可以将它放在与运行动画代码的文件相同的文件中,或者我可以将它放在单独的“触发器”文件中,并将两个文件(以及处理所需的其他其他文件)都加载到站点页脚中。

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

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

因此,为了清楚起见,如果我为同一页面添加以下两个触发器,并且以下两个元素中的第一个存在,那么动画将在 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>

显然,可以——而且可能应该——写出更简洁和灵活的答案。关于这一点,在单个条件中正确组合播放和停止侦听器显然是第一步,但我太喜欢 js 了,甚至在第一次或第二次尝试时都无法做到这一点。也许以后/下一次!

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

4

3 回答 3

3

只需编写代码,以便在元素不存在时不会引发错误,只需检查if元素是否存在即可。

let first = document.getElementById('firstAnimationDiv');
if (first) first.addEventListener('mouseenter', function() {firstAnimation.play();});
于 2018-05-05T18:33:52.290 回答
3

您可以使用委托事件处理稍微不同地处理这个问题。mouseover, 不像mouseenter, 冒泡到其祖先元素,因此您可以将单个事件侦听器添加到#animationDiv包含 every 的祖先元素,然后打开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
  }
});
于 2018-05-05T18:42:33.193 回答
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 和存在的动画。

(如上所述,可以使用 data 属性进行选择,const divs = document.querySelectorAll("div[data-animation]");这样就class变得不必要了。)

于 2018-05-05T18:44:50.807 回答