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

How do I stop scroll events from recurring?

任何人都可以帮助我处理网站上的活动吗?问题是,每次触发滚动事件时,它们都会保留 re-occurring 。导航菜单在 header 下,但在滚动时粘在顶部,但我希望它在我们一直向上滚动时返回到 header 下。我不知道我做错了什么。这是我的代码:

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)";
}
);
});

此外,当页面加载并触发滚动事件时,div 飞入,这正是我想要的。但我的问题是每次触发滚动事件时,框都会继续飞入而不是事件停止,即保持 RE-OCCURRING。这是我使用的代码:

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";

});

我网站的 URL 是 agwconitsha.org

问题是您没有正确添加和删除事件侦听器。每个事件侦听器都需要一个目标、一个动作和处理程序。

window.addEventListener("scroll", myfunction);
window.removeEventListener("scroll", myfunction);

我建议您将滚动事件侦听器挂接到 window 对象(整个页面),然后施展您的魔法。

您还可以使用变量来控制带有变量的 flyIn 元素的状态 isOpen

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

window.addEventListener("scroll", navigationHandler);

function navigationHandler() {

    if (currentDistance > "90px") {

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

        navigation.style.position = "relative";
        navigation.style.borderBottom = "none";
    }
}


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

window.addEventListener("scroll", flyInHandler);

function flyInHandler() {

    if (!isOpen) {
        var programMenu = document.createElement("DIV");
        programMenu.className = "programMenu";
        programMenu.appendChild(flyInDiv);
        document.body.appendChild(programMenu);
        programMenu.style.position = "fixed";

        isOpen = true;
        window.removeEventListener("scroll", flyInHandler);
    }
}

我查看了您的代码和网站,您似乎正在努力实现以下目标:

  • 只有当用户滚动经过 header
  • 时才会变粘的粘性导航
  • 当用户第一次滚动并停留在那里时 'flys' 一次的服务时间列表

TL;DR - 我在 http://jsfiddle.net/9o7frxp1/4/.

制作了一个包含此基本行为的 jsfiddle

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

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

为了进一步解释,让我们暂时以您的 <nav> 为例,这样 post 就不会太长。查看我的 jsfiddle link 上面的 flying div - 我已经在代码中添加了注释来解释发生了什么,但如果它没有意义请告诉我.

所以,我们有一个导航元素:

<nav id="nav">
  Nav
</nav>

我们为 <nav> 提供了一些样式 - 一些默认样式,然后一些只有在它也具有 .is-sticky class:

时才会应用的样式
#nav {
  width: 100%;
  height: 40px;
}

#nav.is-sticky {
  position: fixed;
  top: 0;
}

在我们的 JavaScript 中,我们找到 <nav>:

var navigation = document.getElementById('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 = '';
  }
}

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

window.addEventListener('scroll', addStickyClass);

这意味着每次用户滚动时我们都会询问 "Are they 90px (or more) away from the top of the window?" - 如果是,导航应该是粘性的,如果不是,则不应该。

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

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

在我这个答案的代码中,我简化了一些事情以更清楚地解释发生了什么 - 你会在 jsfiddle 中看到一个更详细的版本我已经 post编辑

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


更新

TL;DR:

我实际上意识到 #flyInDiv 的代码可以更简单一些,并且不需要任何布尔值或 if 语句。 Fiddle 并在 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 = '';
  }
}

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

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

#flyInDiv 的逻辑是这样的:

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

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

#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);
}

舞台已经搭建...我们需要一些 JavaScript!

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

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

function addStickyClass() {
  flyingDiv.className = "is-sticky";
}

请记住,每次用户滚动时它都会被触发(多亏了我们刚才添加的那个事件侦听器),那么会发生什么?

用户第一次滚动时,元素将获得 .is-sticky class。每次用户滚动时它都会继续设置。

碰巧,这不会影响您网站的行为,我在最近的 fiddle 中提到了这一点。如果这个 class 一直设置在 #flyInDiv 上实际上没问题,因为它只是意味着从用户第一次滚动的那一刻起, 它只是一直可见。

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

对于这个例子,我们不用它,因为当代码 flyingDiv.className = "is-sticky"; 在每个卷轴上执行时,它只是不断地覆盖 class 名称。结果:你的 flyingDiv 总是有 .is-sticky class.

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

这就是您的做法 - 让我们为代码提供有关元素是否可见的知识。这样,它可以决定是否需要 运行 代码。

滚动时,可以不断询问:

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

所以首先我们需要一个变量来评估。我们给它命名,默认设置为false。为什么?因为变量是 flyingDivIsVisible(读取 "Is the flying div visible?"),并且在页面上发生任何其他事情之前,正如我们已经提到的,答案应该是 "no".

重要的是,这发生在任何功能之外和之上。我们在这里设置舞台,我们不希望这段代码在用户每次滚动时保持 运行ning。它只是在这里为代码提供我们已经拥有的初步知识 - 当用户访问页面时,但在他们滚动之前,#flyInDiv 是不可见的。

var flyingDivIsVisible = false;

接下来,我们向函数中添加一些内容。至关重要的是,我们添加的这个新东西在 if 块中,并且当 !flyingDivIsVisible 为真时只有 运行s(或者换句话说,flyingDivIsVisible === false):

// Tell our code that the div isn't visible.
var flyingDivIsVisible = false;

function addStickyClass() {

  // Only run this code if div isn't visible.
  if (!flyingDivIsVisible) {
    flyingDiv.className = "is-sticky";

    // Tell our code that the div is visible
    flyingDivIsVisible = true;
  }
}

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

但是,当 flyingDivIsVisible === false 时只有 运行 的东西?嗯,正好也变了flyingDivIsVisible = true。为什么?因为 运行s 正在将 .is-sticky class 添加到 #flyInDiv,这意味着它现在可见 - 但我们需要通过设置 [=49 来为我们的代码提供知识=].

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

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

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

// Tell our code the div isn't visible
var flyingDivIsVisible = false;

(function () {
  // Us: Is the div visible?
  // Code: No. You told it me it isn't.

  if (!flyingDivIsVisible) {
    flyingDiv.className = "is-sticky";

    // Tell our code that the div is visible
    flyingDivIsVisible = true;
  }
})();

(function () {
  // Us: Is the div visible?
  // Code: Yes. You told me it was last time I ran this function.

  if (!flyingDivIsVisible) {
    // Nothing in this block runs, because the div is visible.

    flyingDiv.className = "is-sticky";
    flyingDivIsVisible = true;
  }
})();

顺便说一下,事后想想,如果 #flyInDiv 是这段代码唯一控制的东西,我们也可以在第一次滚动后删除事件侦听器,这样代码就不会不再监听滚动(不需要,对吗?它已经完成了使 div 在第一次滚动时可见的工作)。

但碰巧的是,我们在与 <nav> 显示和隐藏相同的代码的上下文中想象它,这仍然需要事件侦听器一直询问 "Is the user scrolling?",所以我们不想删除事件侦听器。

希望一切都有意义!

您可以尝试使用 overflow-x: hidden [或] overflow-y: hidden 取决于你的滚动条在哪里。