简体   繁体   English

如何监听VueJS组件中的窗口滚动事件?

[英]How to listen to the window scroll event in a VueJS component?

I want to listen to the window scroll event in my Vue component.我想在我的 Vue 组件中监听窗口滚动事件。 Here is what I tried so far:到目前为止,这是我尝试过的:

<my-component v-on:scroll="scrollFunction">
    ...
</my-component>

With the scrollFunction(event) being defined in my component methods but it doesn't seem to work.在我的组件方法中定义了scrollFunction(event) ,但它似乎不起作用。

Anyone has any idea how to do this?任何人都知道如何做到这一点?

Thanks!谢谢!

Actually I found a solution.其实我找到了解决办法。 I add an event listener on the scroll event when the component is created and remove the event listener when the component is destroyed.我在创建组件时在scroll事件上添加事件侦听器,并在销毁组件时删除事件侦听器。

export default {
  created () {
    window.addEventListener('scroll', this.handleScroll);
  },
  destroyed () {
    window.removeEventListener('scroll', this.handleScroll);
  },
  methods: {
    handleScroll (event) {
      // Any code to be executed when the window is scrolled
    }
  }
}

Hope this helps!希望这可以帮助!

In my experience, using an event listener on scroll can create a lot of noise due to piping into that event stream, which can cause performance issues if you are executing a bulky handleScroll function.根据我的经验,在滚动上使用事件侦听器会由于管道进入该事件流而产生大量噪音,如果您正在执行庞大的handleScroll函数,这可能会导致性能问题。

I often use the technique shown here in the highest rated answer, but I add debounce on top of it, usually about 100ms yields good performance to UX ratio.我经常在评分最高的答案中使用此处显示的技术,但我在其上添加了去抖动,通常大约100ms会产生良好的性能与 UX 比率。

Here is an example using the top-rated answer with Lodash debounce added:这是一个使用添加了 Lodash debounce 的顶级答案的示例:

import debounce from 'lodash/debounce';

export default {
  methods: {
    handleScroll(event) {
      // Any code to be executed when the window is scrolled
      this.isUserScrolling = (window.scrollY > 0);
      console.log('calling handleScroll');
    }
  },

  mounted() {
    this.handleDebouncedScroll = debounce(this.handleScroll, 100);
    window.addEventListener('scroll', this.handleDebouncedScroll);
  },

  beforeDestroy() {
    // I switched the example from `destroyed` to `beforeDestroy`
    // to exercise your mind a bit. This lifecycle method works too.
    window.removeEventListener('scroll', this.handleDebouncedScroll);
  }
}

Try changing the value of 100 to 0 and 1000 so you can see the difference in how/when handleScroll is called.尝试将100的值更改为01000 ,以便您可以看到调用handleScroll的方式/时间的差异。

BONUS: You can also accomplish this in an even more concise and reuseable manner with a library like vue-scroll .奖励:您还可以使用vue-scroll类的库以更简洁和可重用的方式完成此操作。 It is a great use case for you to learn about custom directives in Vue if you haven't seen those yet.如果您还没有看过 Vue 中的自定义指令,这是一个很好的用例。 Check out https://github.com/wangpin34/vue-scroll .查看https://github.com/wangpin34/vue-scroll

This is also a great tutorial by Sarah Drasner in the Vue docs: https://v2.vuejs.org/v2/cookbook/creating-custom-scroll-directives.html这也是 Sarah Drasner 在 Vue 文档中的一个很棒的教程: https ://v2.vuejs.org/v2/cookbook/creating-custom-scroll-directives.html

For Vue 3 users对于 Vue 3 用户

In vue3 you should use unmounted or beforeUnmount, instead of beforeDestroy .在 vue3 中,你应该使用 unmounted 或 beforeUnmount,而不是beforeDestroy

Lifecycle hook beforeDestroy is not emitted in Vue3 Vue3 中未发出生命周期钩子 beforeDestroy

Here's what works directly with Vue custom components.这是直接与 Vue 自定义组件一起使用的内容。

 <MyCustomComponent nativeOnScroll={this.handleScroll}>

or或者

<my-component v-on:scroll.native="handleScroll">

and define a method for handleScroll.并为handleScroll 定义一个方法。 Simple!简单的!

I've been in the need for this feature many times, therefore I've extracted it into a mixin .我多次需要此功能,因此我将其提取到mixin中。 It can be used like this:它可以这样使用:

import windowScrollPosition from 'path/to/mixin.js'

new Vue({
  mixins: [ windowScrollPosition('position') ]
})

This creates a reactive position property (can be named whatever we like) on the Vue instance.这会在 Vue 实例上创建一个响应式position属性(可以任意命名)。 The property contains the window scroll position as an [x,y] array.该属性包含作为[x,y]数组的窗口滚动位置。

Feel free to play around with this CodeSandbox demo .随意玩这个 CodeSandbox 演示

Here's the code of the mixin.这是mixin的代码。 It's thoroughly commentated, so it should not be too hard to get an idea how it works:它经过了彻底的注释,因此不难了解它是如何工作的:

function windowScrollPosition(propertyName) {
  return {
    data() {
      return {
        // Initialize scroll position at [0, 0]
        [propertyName]: [0, 0]
      }
    },
    created() {
      // Only execute this code on the client side, server sticks to [0, 0]
      if (!this.$isServer) {
        this._scrollListener = () => {
          // window.pageX/YOffset is equivalent to window.scrollX/Y, but works in IE
          // We round values because high-DPI devies can provide some really nasty subpixel values
          this[propertyName] = [
            Math.round(window.pageXOffset),
            Math.round(window.pageYOffset)
          ]
        }

        // Call listener once to detect initial position
        this._scrollListener()

        // When scrolling, update the position
        window.addEventListener('scroll', this._scrollListener)
      }
    },
    beforeDestroy() {
      // Detach the listener when the component is gone
      window.removeEventListener('scroll', this._scrollListener)
    }
  }
}

I know this is an old question, but I found a better solution with Vue.js 2.0+ Custom Directives : I needed to bind the scroll event too, then I implemented this.我知道这是一个老问题,但我找到了Vue.js 2.0+ 自定义指令的更好解决方案:我也需要绑定滚动事件,然后我实现了这个。

First of, using @vue/cli , add the custom directive to src/main.js (before the Vue.js instance) or wherever you initiate it:首先,使用@vue/cli ,将自定义指令添加到src/main.js (在 Vue.js 实例之前)或您启动它的任何位置:

Vue.directive('scroll', {
  inserted: function(el, binding) {
    let f = function(evt) {
      if (binding.value(evt, el)) {
        window.removeEventListener('scroll', f);
      }
    }
    window.addEventListener('scroll', f);
  }
});

Then, add the custom v-scroll directive to the element and/or the component you want to bind on.然后,将自定义v-scroll指令添加到要绑定的元素和/或组件。 Of course you have to insert a dedicated method: I used handleScroll in my example.当然,您必须插入一个专用方法:我在示例中使用handleScroll

<my-component v-scroll="handleScroll"></my-component>

Last, add your method to the component.最后,将您的方法添加到组件中。

methods: {
  handleScroll: function() {
    // your logic here
  }
}

You don't have to care about the Vue.js lifecycle anymore here, because the custom directive itself does.您不必再关心 Vue.js 的生命周期,因为自定义指令本身就可以。

我认为最好的方法就是添加“.passive”

v-on:scroll.passive='handleScroll'

What about something like this?这样的事情呢? This is Vue 3 by the way顺便说一下,这是 Vue 3

setup() {
    function doOnScroll(event) {
      window.removeEventListener("scroll", doOnScroll);
      console.log("stop listening");
      // your code here ....
      setTimeout(() => {
        window.addEventListener("scroll", doOnScroll, { passive: true });
        console.log("start listening");
      }, 500);
    }

    window.addEventListener("scroll", doOnScroll, { passive: true });
  }

The idea here is to listen for the scroll event only once, do your script and then reattach the scroll listener again with a delay in the setTimeout function.这里的想法是只侦听一次滚动事件,执行您的脚本,然后在setTimeout函数中延迟再次连接滚动侦听器。 If after this delay the page is still scrolling the scroll event will be handled again.如果在此延迟之后页面仍在滚动,则将再次处理滚动事件。 Basically the scroll event is listened only once every 500ms (in this example).基本上每 500 毫秒(在本例中)只监听一次滚动事件。

I'm using this just to add a css class during the scroll to move away a button.我使用它只是为了在滚动过程中添加一个 CSS 类以移开一个按钮。

this does not refresh your component I solved the problem by using Vux create a module for vuex "page"这不会刷新您的组件我通过使用 Vux 为 vuex“页面”创建一个模块解决了这个问题

export const state = {
    currentScrollY: 0,
};

export const getters = {
    currentScrollY: s => s.currentScrollY
};

export const actions = {
    setCurrentScrollY ({ commit }, y) {
        commit('setCurrentScrollY', {y});
    },
};

export const mutations = {
    setCurrentScrollY (s, {y}) {
       s.currentScrollY = y;
    },
};

export default {
    state,
    getters,
    actions,
    mutations,
};

in App.vue :在 App.vue 中:

created() {
    window.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    window.removeEventListener("scroll", this.handleScroll);
  },
  methods: {
    handleScroll () {
      this.$store.dispatch("page/setCurrentScrollY", window.scrollY);
      
    }
  },

in your component :在您的组件中:

  computed: {
    currentScrollY() {
      return this.$store.getters["page/currentScrollY"];
    }
  },

  watch: {
    currentScrollY(val) {
      if (val > 100) {
        this.isVisibleStickyMenu = true;
      } else {
        this.isVisibleStickyMenu = false;
      }
    }
  },

and it works great.而且效果很好。

Vue3, I added the listener on beforeMount, and it works, just if your case like mine, I need the listener to be triggered on the entire app Vue3,我在 beforeMount 上添加了监听器,它可以工作,如果你的情况像我一样,我需要在整个应用程序上触发监听器

beforeMount() {
    window.addEventListener('scroll', this.handleScroll)
},
methods: {
   handleScroll(){
     // work here
  }
}

Vue3 with <script setup> working example: Vue3 与<script setup>工作示例:

<template>
  <header :class="stickyHeader ? 'sticky' : ''" ></header>
<template>

<script setup>
import { ref, onBeforeMount } from 'vue'

onBeforeMount(() => {
  window.addEventListener('scroll', handleScroll)
})

const stickyHeader = ref(false)

function handleScroll(){
  if (window.pageYOffset) {
    stickyHeader.value = true
  } else {
    stickyHeader.value = false
  }
}

</script>

In vue scrolling not a window element.在 vue 中滚动不是窗口元素。 MB in u app will work smth like this:你应用程序中的 MB 将像这样工作:

mounted () {
    document.querySelector('.className').addEventListener('scroll', this.handleScroll);
},

methods: {
    handleScroll(e){
        console.log(this.scrollEl.scrollTop)
    }
},
document.addEventListener('scroll', function (event) {
    if ((<HTMLInputElement>event.target).id === 'latest-div') { // or any other filtering condition
  
    }
}, true /*Capture event*/);

You can use this to capture an event and and here "latest-div" is the id name so u can capture all scroller action here based on the id you can do the action as well inside here.您可以使用它来捕获事件,并且这里的“latest-div”是 id 名称,因此您可以根据 id 在此处捕获所有滚动操作,您也可以在此处执行操作。

In combination this.$vuetify.breakpoint.name with and loading on demand, the scroll event is a really useful feature.结合this.$vuetify.breakpoint.name和按需加载,滚动事件是一个非常有用的特性。

Use a trigger.使用触发器。 For example, a tab:例如,一个选项卡:

<v-tabs
  v-bind:grow="!isFullScreen()"
  v-bind:vertical="isFullScreen()"
>

Some class attributes:一些类属性:

private isUserScrolling: boolean = false;
private isLoaded: boolean = false;
private startScroll: number = 3;

Function that reacts to the trigger (adjustment if necessary):对触发器做出反应的功能(必要时进行调整):

private isFullScreen(): boolean {
    switch (this.$vuetify.breakpoint.name) {
      case "xs":
        this.startScroll = 500;
        return false;
      case "sm":
        this.startScroll = 300;
        return false;
      case "md":
        this.startScroll = 100;
        return true;
      case "lg":
        this.startScroll = 50;
        return true;
      case "xl":
        this.startScroll = 3;
        return true;
    }
}

Add your event:添加您的活动:

created() {
   window.addEventListener("scroll", this.handleScroll);
}

React to your event:对您的活动做出反应:

private handleScroll(event: any): void {
   this.isUserScrolling = window.scrollY > this.startScroll;
   
   if (this.isUserScrolling && !this.isLoaded) {
     // load your stuff
     ...
     this.isLoaded = true;
   }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM