Giter VIP home page Giter VIP logo

vue-observe-visibility's Introduction

vue-observe-visibility logo

vue-observe-visibility

Detect when an element is becoming visible or hidden on the page. Demo

Become a Patreon

Sponsors

sponsors logos


Table of contents

Installation

npm install --save vue-observe-visibility

⚠️ This plugin uses the Intersection Observer API that is not supported in every browser (currently supported in Edge, Firefox and Chrome). You need to include a polyfill to make it work on incompatible browsers.

Import

import Vue from 'vue'
import VueObserveVisibility from 'vue-observe-visibility'

Vue.use(VueObserveVisibility)

Or:

import Vue from 'vue'
import { ObserveVisibility } from 'vue-observe-visibility'

Vue.directive('observe-visibility', ObserveVisibility)

Browser

<script src="vue.js"></script>
<script src="https://unpkg.com/vue-observe-visibility/dist/vue-observe-visibility.min.js"></script>

The plugin should be auto-installed. If not, you can install it manually with the instructions below.

Install all the directives:

Vue.use(VueObserveVisibility)

Use specific directives:

Vue.directive('observe-visibility', VueObserveVisibility.ObserveVisibility)

Usage

The v-observe-visibility directive is very easy to use. Just pass a function as the value:

<div v-observe-visibility="visibilityChanged">

This also works on components:

<MyComponent v-observe-visibility="visibilityChanged" />

The function will be called whenever the visiblity of the element changes with the argument being a boolean (true means the element is visible on the page, false means that it is not).

The second argument is the corresponding IntersectionObserverEntry object.

visibilityChanged (isVisible, entry) {
  this.isVisible = isVisible
  console.log(entry)
}

IntersectionObserver options

It's possible to pass the IntersectionObserver options object using the intersection attribute:

<div v-observe-visibility="{
  callback: visibilityChanged,
  intersection: {
    root: ...,
    rootMargin: ...,
    threshold: 0.3,
  },
}">

Once

It can be useful to listen for when the element is visible only once, for example to build introduction animations. Set the once option to true:

<div v-observe-visibility="{
  callback: visibilityChanged,
  once: true,
}">

Throttling visibility

You can use the throttle options (in ms) specifying minimal state duration after which an event will be fired. It's useful when you are tracking visibility while scrolling and don't want events from fastly scrolled out elements.

<div v-observe-visibility="{
  callback: visibilityChanged,
  throttle: 300,
}">

You can also pass a leading option to trigger the callback the first time when the visibility changes without waiting for the throttle delay. I can either be visible, hidden or both.

<div v-observe-visibility="{
  callback: visibilityChanged,
  throttle: 300,
  throttleOptions: {
    leading: 'visible',
  },
}">

Passing custom arguments

You can add custom argument by using an intermediate function:

<div v-observe-visibility="(isVisible, entry) => visibilityChanged(isVisible, entry, customArgument)">

Here visibilityChanged will be call with a third custom argument customArgument.

Disabling the observer

Passing a falsy value to the directive will disable the observer:

<div
  v-for="(item, index) of items"
  :key="item.id"
  v-observe-visibility="index === items.length - 1 ? visibilityChanged : false"
>

Example

<div id="app">
  <button @click="show = !show">Toggle</button>
  <label>
    <input type="checkbox" v-model="isVisible" disabled/> Is visible?
  </label>
  <div ref="test" v-show="show" v-observe-visibility="visibilityChanged">Hello world!</div>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    show: true,
    isVisible: true,
  },
  methods: {
    visibilityChanged (isVisible, entry) {
      this.isVisible = isVisible
      console.log(entry)
    },
  },
})
</script>

License

MIT

vue-observe-visibility's People

Contributors

akryum avatar brokolja avatar econic avatar itanka9 avatar nickknissen avatar onmylife avatar rayrutjes avatar sirish-amatya avatar sirish-year13 avatar tjallingt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vue-observe-visibility's Issues

Add option to disable for tests

I have a setup where I'm using Jest and vue-test-utils to mount and test my components. Some of the inner components use the v-observe-visibility directive. However, it breaks/slows down considerably the tests. It looks like it really doesn't like being run in testing virtual DOM. I got around this by adding:

Vue.prototype.isTest = process.env.JEST_WORKER_ID !== undefined

in my startup config. And then a

v-observe-visibility="isTest ? false : onRulesVisible" i

nside my components. However, this feels very clunky. It'd be nice if there were a hook we could access to disable the directive when running tests. I'll admit, I haven't taken a look at the inner workings of the directive. I can't be the only one who has run into this problem.

Manual check

It would be nice if there was some method to explicitly call to get the current visibility state (without having to wait for a "change").

Once not working with Throttle

When I use both options at the same time the Once option does not work. Is this normal behavior? Each separately works very well.

v-observe-visibility="{
   callback: visibilityChanged,
   throttle: 300,
   once: true
}"

Instructions for using with Vue3

Hi,

Trying to use vue-observe-visilibility in a vue3 using v2.0.0-alpha.1.
No errors but it doesn't call the callback.

Am I using it correctly?

main.js

import { createApp } from 'vue';
import VueObserveVisibility from 'vue-observe-visibility'
import App from './App.vue';

const app = createApp(App);
app.use(VueObserveVisibility);
app.mount('#app');

component.js

<template>
  <ChildComponent
      v-observe-visibility="{
        callback: (isVisible, entry) => alert("visible"),
        throttle: 300,
        intersection: {
          0.8,
        },

  />
</template>

0.4.5 breaking change (throttleOptions)

this code was ok in 0.4.4 version

v-observe-visibility="{
  callback: callback,
  throttle: 300,
  once: true,
}

but doesn't work in 0.4.5 if you do not add throttleOptions: {leading: 'hidden'} to directive

Error in directive observe-visibility bind hook: "TypeError: Cannot read property 'leading' of undefined"

solved issue by adding throttleOptions

v-observe-visibility="{
  callback: callback,
  throttle: 300,
  throttleOptions: {
    leading: 'hidden',
  },
  once: true,
}

How to use in Vue3 ? (rc.2)

Yes, I know Vue3 is not released yet. But 1. you will have to support it anyway very soon, 2. what about being the very first visibility observing component to support it?

I tried the following in main.js:

import { createApp } from 'vue'
import App from './App.vue'
import VueObserveVisibility from 'vue-observe-visibility'

const app = createApp(App)
app.component('observe-visibility', VueObserveVisibility)
app.mount('#app')

Also tried app.directive('observe-visibility', VueObserveVisibility.ObserveVisibility) instead of the component line. In both cases it gets compiled and runs with no errors but the callback is not called when the div is scrolled in/out of view.

What I am doing wrong? / If it is not supposed to work under Vue3 what is the cause. Thank you.

For users from China-给**用户

observe是个很好的api,做滚动加载非常合适。
但是某些特定机型,无法触发,即使用了polyfill,比如oppo的手机,系统版本是Andriod 6

Trigger visibility 200px before it shows up in the viewport.

I would like to trigger loading the data of a component slightly before it shows up in the viewport.
Reading the IntesectionObserver documentation it seemed that this could be achieved by increasing the bottom margin of the root, using the rootMargin option ('0px 0px 200px 0px'). But I probably am mistaken as I could not make it work.

Is there a proper way to do it using vue-observe-visibility?

Cheers

Occasionally misses an intersection taking place

Hi,

We are checking out the directive and were wondering if you are occasionally finding it 'misses' something arriving in the viewport until it is scrolled off the screen and back on again. We are looking in to what might cause it and will submit further if we find a bug ourselves, but have you had any experience of this before? Speed of scrolling does not seem to make a difference.

Thanks for any advice you can give,

Ben

[Q] Conditional observable

I have dynamic detection on whether to observe element or not, based on computed property. I don't see on sources that it removes element from observables on { value } change. Interestingly, I first noticed some issues in my app, decided to fork and start using my fork, but then I found out that the issue was in other place and returned back to your version and it works fine. But still, I'm curious about dynamic directive binding.

Link to a fork: master...LexSwed:master

Support specifying the threshold (feature/pull request)

This is basically a pull request, with the code shown below it becomes possible to do:
<div v-observe-visibility:65="onChange" />
where the "65" is treated as a percentage, i.e. 0.65, and used as the threshold option in the IntersectionObserver constructor.

Here is the relevant section of implementation code:

function validateThreshold(threshold) {
  threshold = parseInt(threshold);
  if(threshold>100 || threshold<0){
    throw new Error("threshold must be beween 0 and 100, or omitted");
  } 
  return isNaN(threshold) ? 0.8 /* default */: threshold/100;
}
...

bind (el, { value, arg }, vnode) {
   let threshold = validateThreshold(arg);
   ... new IntersectionObserver(entries => {
      ...
      el._vue_visibilityCallback.call(null, entry.intersectionRatio > threshold, entry);
      ...} , {threshold});

Weird behaviour in Edge

Hello,

I'm expecting weird behaviour in Edge version 16.16299

All other browsers I've tested that are green in https://caniuse.com/#feat=intersectionobserver work as expected with vue-observe-visibility.

I tested:

I'm sorry, that I can't provide better information here. Let me know if there is something of intereset for debugging purposes.

regards and thanks for the awesome work :)

`rootMargin` has no effect

Greetings,
I have a small problem related to rootMargin.
I have a lazy image component:

<img
  :src="loadedSrc"
  v-observe-visibility="{
    rootMargin,
    callback: handleVisibilityChange,
  }"
  class="base-image"
>

where rootMargin is a computed property related to a component height:

...
computed: {
  rootMargin() {
    const { rootMarginBottom } = this;
    return `0px 0px ${rootMarginBottom}px 0px`;
  },
},
mounted() {
  const { $el } = this;
  this.$nextTick(() => {
    this.rootMarginBottom = $el.getBoundingClientRect().height * 3;
  });
}
...

The problem is that rootMargin has no effect on when callback is toggled. It is always when element is fully in the viewport. As you may guess I'm trying to preload image before it enters a viewport.

It also works exactly the same way if i hardcode the rootMargin. It feels like its ignored...

Can some one give me a hint where the problem is?

Thank you in advance!

Errors compiling template

I'm using the directive in a nuxt project, it works well.

But when I want to set the IntersectionObserver options, I got the error:

in ./pages/index.vue?vue&type=template&id=2a183b29&scoped=true& (./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./pages/index.vue?vue&type=template&id=2a183b29&scoped=true&)
Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):
(Emitted value instead of an instance of Error) 

  Errors compiling template:

  invalid expression: Unexpected token } in

    {callback:headerVisibilityChanged,
  intersection:{
    rootMargin:10%
  }}

  Raw expression: v-observe-visibility="{callback:headerVisibilityChanged,
  intersection:{
    rootMargin:10%
  }}"


  55 |  
  56 |    <section
  57 |      data-category="header"
     |                             
  58 |      v-observe-visibility="{callback:headerVisibilityChanged,
     |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  59 |    intersection:{
     |  ^^^^^^^^^^^^^^^^
  60 |      rootMargin:10%
     |  ^^^^^^^^^^^^^^^^^^
  61 |    }}"
     |  ^^^^^

It seems Vue treat the }} as mustache syntax?

I tried to bind the v-observe-visibility to a data object, but it wouldn't work.

Problem on safari using nuxt

I'm using this on a nuxt project and i'm not able to make it work on safari or mobile (ios) does anyone was able to set it up correctly on nuxt?

False on initial load

I am forced to use setTimeout in mounted hook, since as page load plugin says it is false. nextTick not helping.

Accept options when registering plugin to override defaults

It would reduce duplication and declutter templates if the plugin allowed overriding defaults:

import Vue from 'vue'
import VueObserveVisibility from 'vue-observe-visibility'

Vue.use(VueObserveVisibility, {
  once: true
})

If majority of my use cases require once to be set to true, then I can just do that, and if I need to set it to false occasionally, I can still do:

<div v-observe-visibility="{
  callback: visibilityChanged,
  once: false,
}">

This applies to all options, and so could also resolve #201.

error after import

vue 2 with webpack 2

yarn add intersection-observer vue-observe-visibility
import 'intersection-observer'
import VueObserveVisibility from 'vue-observe-visibility'
// or import {VueObserveVisibility} from 'vue-observe-visibility'

then

error  in ./~/vue-observe-visibility/index.js

Module parse failed: ~/Projects/js/revel/node_modules/vue-observe-visibility/index.js Unexpected token (1:20)
You may need an appropriate loader to handle this file type.
| export default from './dist/vue-observe-visibility'
| export * from './dist/vue-observe-visibility'
| import './dist/vue-observe-visibility.css'

 @ ./src/main.js 12:0-62
 @ multi ./build/dev-client ./src/main.js

Anything else I missed?

Get Directive Target Element

Maybe not the right forum for this question, but is it possible to get the calling element from the directive inside the callback method?

I know I could pass an id/selector as an optional argument and get the element that way, but didn't like the way that felt, as I have 100+ elements to do this for.

I tried passing the $event var as a custom argument but that did not work, as it doesn't seem to be a true event. So is there something similar to '$event' variable that can be passed to the method so I can get the target? Ex:

<div v-observe-visibility="(isVisible, entry) => 
      myCallbackMethod(isVisible, entry, $event)">

function myCallbackMethod (event) { 
   let element = event.target 
   element.classList.add('visible');
} 

Adding args to function

How would I go about adding an argument to the function such as

      <li v-for="person in persons"
          :key="person.id"
          v-observe-visibility="visibilityChanged(person)">

?
I'm seeing Error in directive observe-visibility bind hook: "Error: observe-visibility directive expects a function as the value". I just don't know how to pass in an arg and play nicely with the isVisible and entry args. I'm sure this is just my lack of Vue knowledge.

Thanks in advance for the help. Great package!

BUG | sometimes onVisibilityChanged is called with wrong visibility value

While working I encountered a very weird bug, I get a visibility change event, but with the wrong value.
Scrolling up and down the scroller, when having 1 object that I track visibility on, Sometimes I get false instead of true:
this is my log
visibility change true
visibility change false
visibility change true
visibility change false
visibility change false

How can this happened and is there anything to do to solve this?

And another unrelated question
I have a
< RecycleScroller ref="scroller">..
<v-for=myComponents>..
< /RecycleScroller>..

and in each of the myComponent, I have the vue-observe-visibility

Any chance to track an item before it gets visible - like on the edge of the scrolling? couldn't make it work with rootMargin

Options Object Doesn't Work in Nuxt

When using an options object with vue-observe-visibility using Nuxt, I get the following error: observe-visibility directive expects a function as the value

It works fine if I pass it a function without an options object.

isVisible always false

Callback function of directive is always returns false value of visibility.

You do not properly validate the threshold option. You assume that it is always defined. And returned value is boolean instead of number value.

Infinite loop

If I use this, then I run into infinite loop when it pushes value to array.

addIndex (isVisible, entry, i) {
      if (isVisible) {
        this.indicesInViewPort.push(i)
      } 
    }

This fires even if I don't scroll at all!

Visibility function is not called until scroll completely stops (iOS)

I'm using this plugin in a Cordova app running on iOS. In this environment, the visibility callback is not triggered while the user is still scrolling (even if the user has lifted their finger, and the scroll slows to a stop). Only when the scroll completely stops is the callback triggered. Is there any way to get around this behaviour? I want the callback to be triggered immediately when the element becomes visible.

How to throttle only when visibility is true ?

This is my configuration
I need to use throttle only when visibility is true and instantly emit an event when item not in the visible port

v-observe-visibility="{
              callback: (isVisible, entry) => handelObserveVisibility(isVisible, entry),
              once: false,
              intersection: {
                threshold: 1.0,
              },
              throttle: 3000 }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.