如何阻止滚动事件重复发生?

I W*_*ers 2 javascript dom scroll dom-events

有人可以帮助我处理网站上的活动吗?问题是,每次触发滚动事件时它们都会重复出现。导航菜单位于标题下方,但在滚动时粘在顶部,但我希望当我们一直向上滚动时它回到标题下方。我不知道我做错了什么。这是我的代码:

var navigation = document.querySelector("nav");
var borderBottom = navigation.getAttribute("border-bottom");
var currentDistance = (document.documentElement.scrollHeight);

addEventListener ("scroll", function() {

if (currentDistance > "90px") {
navigation.style.position = "fixed";
navigation.style.top = "0";
navigation.style.borderBottom = "3px solid rgb(157,0,53)";
}

else removeEventListener ("scroll", function() {
navigation.style.position = "fixed";
navigation.style.top = "0";
navigation.style.borderBottom = "3px solid rgb(157,0,53)";
}
);
});
Run Code Online (Sandbox Code Playgroud)

此外,当页面加载并触发滚动事件时,一个 div 飞入,这就是我想要的。但我的问题是,每次触发滚动事件时,盒子都会不断飞入,而不是事件停止,即不断重新发生。这是我使用的代码:

var flyInDiv = document.getElementById("flyInDiv");

addEventListener ("scroll", function() {
var programMenu = document.createElement("DIV");

programMenu.className = "programMenu";
programMenu.appendChild(flyInDiv);

document.body.appendChild(programMenu);

programMenu.style.position = "fixed";

});
Run Code Online (Sandbox Code Playgroud)

我网站的 URL 是 agwconitsha.org

Jam*_*den 5

我查看了您的代码和网站,看起来您正在尝试实现以下目标:

  • 粘性导航,仅当用户滚动经过标题时才变得粘性
  • 当用户第一次滚动时“飞”入一次并停留在那里的服务时间列表

TL;DR - 我在http://jsfiddle.net/9o7frxp1/4/上制作了一个包含此基本行为的 jsfiddle 。

在上面的小提琴中,我建议使用 CSS 类来控制导航的粘性和飞行 div 的动画。JavaScript 只是根据用户是否滚动以及滚动位置来打开和关闭这些类。

这对于分离 JS 和 CSS 的关注点非常有用,允许每种语言都做它最擅长的事情,并且意味着代码因此更容易维护。

为了进一步解释,我们<nav>现在就以您的例子为例,这样这篇文章就不会太长。也请查看我上面的 jsfiddle 链接以获取飞行 div - 我已在代码中添加了注释来解释发生的情况,但如果它没有意义,请告诉我。

所以,我们有一个 nav 元素:

<nav id="nav">
  Nav
</nav>
Run Code Online (Sandbox Code Playgroud)

我们有一些样式<nav>- 一些默认样式,还有一些仅在它也有类时才会应用.is-sticky

#nav {
  width: 100%;
  height: 40px;
}

#nav.is-sticky {
  position: fixed;
  top: 0;
}
Run Code Online (Sandbox Code Playgroud)

在我们的 JavaScript 中,我们发现<nav>

var navigation = document.getElementById('nav');
Run Code Online (Sandbox Code Playgroud)

现在,我们准备一个当用户滚动时将触发的函数。我们还没有添加任何侦听滚动的内容 - 我们只是准备好该功能。

function addStickyClass() {
  // Every time this function fires, we calculate the scroll position
  var currentDistance = document.body.scrollTop;

  // If we've scrolled 90px down, add the 'is-sticky' class to our nav.
  // If not, remove the 'is-sticky' class.
  if (currentDistance > 90) {
    navigation.className = 'is-sticky';
  } else {
    navigation.className = '';
  }
}
Run Code Online (Sandbox Code Playgroud)

太酷了 - 我们快准备好了。最后一件事 - 让我们确保每次用户滚动时都会触发上述函数。

window.addEventListener('scroll', addStickyClass);
Run Code Online (Sandbox Code Playgroud)

这意味着每次用户滚动时我们都会询问“它们距离窗口顶部是否有 90 像素(或更多)?” - 如果是,导航应该是粘性的,如果不是,则不应该。

stekhn是正确的,您还没有完全成功地正确实现事件侦听器,并且它导致您的页面行为异常。

但是,在上面的代码示例中,我建议只需要添加一个事件侦听器,而无需删除它。我还进一步建议您应该使用 JS 和 CSS 的替代组合以稍微不同的方式想象这个问题,正如我上面提到的。

在这个答案的代码中,我稍微简化了一些事情,以便更清楚地解释正在发生的事情 - 您将在我发布的 jsfiddle 中看到更详细的版本。

然而,即使是我的 jsfiddle 仍然是您网站上的简化版本,因此您需要对其进行调整 - 尝试一下,让我知道您的进展如何。


更新

长话短说:

我实际上意识到 , 的代码可以更简单一些#flyInDiv,并且不需要任何布尔值或if语句。摆弄注释解释http://jsfiddle.net/9o7frxp1/5/


但我仍然想解释一下我最初在下面实现的内容 - 我认为它值得了解:

所以,如果您还记得的话,我们有一个每次用户滚动时都会触发的函数:

function addStickyClass() {
  // Every time this function fires, we calculate the scroll position
  var currentDistance = document.body.scrollTop;

  // If we've scrolled 90px down, add the 'is-sticky' class to our nav.
  // If not, remove the 'is-sticky' class.
  if (currentDistance > 90) {
    navigation.className = 'is-sticky';
  } else {
    navigation.className = '';
  }
}
Run Code Online (Sandbox Code Playgroud)

每次用户滚动时,此函数都会决定是否.is-sticky向我们的元素添加或删除该类。

说到这里 - 我们希望我们的代码根据用户何时滚动来决定 - 显示您的#flyInDiv. 也许我们可以将它包含在这个代码块中?

其逻辑#flyInDiv是这样的:

  • 当用户第一次登陆页面时,默认情况下该元素是不可见的,这要归功于 CSS
  • 在用户第一次滚动时,由于添加了额外的 CSS 类,该元素应该出现
  • 在用户访问页面的剩余时间里,该元素应保留在原来的位置。我们不再关心用户是否滚动。

让我们看看它在代码方面是如何工作的。首先,由于 CSS,该元素默认是不可见的。我们有.is-sticky用于使元素可见的类,但默认情况下该类不在元素上:

#flyInDiv {
  width: 100px;
  height: 100px;

  /* Positioned hard right */
  position: fixed;
  right: 0;

  /* Translated an extra 100px to the right, sending it out of view */
  transform: translateX(100px);
}

#flyInDiv.is-sticky {
  /* No translation, so the element should now simply be hard right */
  transform: translateX(0);
}
Run Code Online (Sandbox Code Playgroud)

舞台已经搭建好了……我们需要一些 JavaScript!

让我们回到我们的addStickyClass()函数 - 但我将从这些示例中删除与 相关的其他代码<nav>以使事情更清楚。

function addStickyClass() {
  // Every time this function fires, we calculate the scroll position
  var currentDistance = document.body.scrollTop;

  // If we've scrolled 90px down, add the 'is-sticky' class to our nav.
  // If not, remove the 'is-sticky' class.
  if (currentDistance > 90) {
    navigation.className = 'is-sticky';
  } else {
    navigation.className = '';
  }
}
Run Code Online (Sandbox Code Playgroud)

请记住,每次用户滚动时都会触发此事件(感谢我们之前添加的事件侦听器),那么会发生什么呢?

用户第一次滚动时,元素将获取该类.is-sticky。每次用户滚动时它都会被设置。

碰巧,这不会影响您网站的行为,我在最新的小提琴中提到了这一点。如果这个类继续在 上设置,实际上很好#flyInDiv,因为它只意味着从用户第一次滚动的那一刻起,它就一直可见。

不过,严格来说,这段代码只需要运行一次。我们只想.is-sticky在第一个滚动事件之后设置类。我们不需要在用户每次滚动时都进行设置。

对于这个例子,我们逃脱了它,因为当代码flyingDiv.className = "is-sticky";在每个滚动上执行时,它只是不断地覆盖类名。结果:你flyingDiv总是有.is-sticky课。

然而,肯定有很多时候你确实想要强制执行一些肯定只在第一次滚动之后发生的事情 - 例如,假设对于这个项目,你需要使用它来appendChild()向 DOM 添加一个新的、更复杂的元素在第一次滚动之后,而不是简单地使用 CSS 使其可见 - 我们绝对无法摆脱不断重复的代码 - 这意味着我们的元素不断被附加!

这就是你要做的 - 让我们的代码了解该元素是否可见。这样,它就可以决定是否需要运行代码。

在滚动时,它可以不断询问:

  • 元素是不可见的吗?
    • 不:这一定是第一卷。让它可见!
    • 是的:这不是第一卷。已经可见了。酷,什么也不做。

所以首先我们需要一个变量来评估。让我们命名它,并将其设置false为默认值。为什么?因为变量是flyingDivIsVisible(读“飞行的 div 可见吗?”),并且在页面上发生任何其他事情之前,正如我们已经提到的,答案应该是“否”。

重要的是,这发生在任何功能之外和之上。我们在这里做好准备,我们不希望每次用户滚动时这段代码都继续运行。它只是在这里为代码提供我们已经掌握的初步知识 - 当用户访问页面时,但在滚动之前, 是#flyInDiv不可见的。

#flyInDiv {
  width: 100px;
  height: 100px;

  /* Positioned hard right */
  position: fixed;
  right: 0;

  /* Translated an extra 100px to the right, sending it out of view */
  transform: translateX(100px);
}

#flyInDiv.is-sticky {
  /* No translation, so the element should now simply be hard right */
  transform: translateX(0);
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们向函数添加一些内容。至关重要的是,我们添加的这些新内容位于一个if块中,并且仅在!flyingDivIsVisibletrue 时运行(或者换句话说,flyingDivIsVisible === false):

var flyingDiv = document.getElementById("flyInDiv");

function addStickyClass() {
  flyingDiv.className = "is-sticky";
}
Run Code Online (Sandbox Code Playgroud)

还记得吗,我们是var flyingDivIsVisible = false;在函数的上方和外部设置的?所以至少这个函数第一次运行时,flyingDivIsVisible === false肯定会评估为true。

但是,这些东西只在什么时候运行flyingDivIsVisible === false?嗯,它也发生了变化flyingDivIsVisible = true。为什么?因为运行的内容是将.is-sticky类添加到#flyInDiv,这意味着它现在可见 - 但我们需要通过设置来为我们的代码提供这些知识flyingDivIsVisible = true

因此,从现在开始,每次用户滚动时,我们都会有flyingDivIsVisible = true,这意味着块中的任何代码if (!flyingDivIsVisible)将不再运行。

因此,我们刚刚实现了一些代码,该代码在用户第一次滚动时运行,但此后不再运行。

为了确保这一点尽可能清晰,让我们看一下连续运行两次的函数并分解发生了什么。我们还假设我们不断询问代码 div 是否可见。既然我们已经找到了一种方法来向它提供这些知识,它就可以不断地告诉我们。PS,我在这个例子中使用了IIFE - 它们是自行运行的函数,而不是等待单独调用。对于这个例子来说更有意义,我们连续两次同步运行一个代码块:

var flyingDivIsVisible = false;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,作为事后的想法,如果这#flyInDiv是该代码控制的唯一事情,我们不妨在第一次滚动后删除事件监听器,以便代码不再监听滚动(它不再监听滚动)不需要,对吧?它已经完成了使 div 在第一次滚动时可见的工作)。

但碰巧的是,我们在与<nav>显示和隐藏相同的代码上下文中想象这一点,这仍然需要事件侦听器不断询问“用户是否在滚动?” 一直如此,所以我们不想删除事件侦听器。

希望一切都有意义!