Giter VIP home page Giter VIP logo

vue-in-viewport-mixin's Introduction

Vue In Viewport Mixin

Vue 3 mixin to determine when a DOM element is visible in the client window by updating Vue data values using the IntersectionObserver. You can then use those data values to do things like trigger transitions.

You may want to check out the directive vesion of this package: vue-in-viewport-directive.

Demo: https://bkwld.github.io/vue-in-viewport-mixin

Install

This package depends on the IntersectionObserver API which needs a polyfill for old browsers, including all of IE. Consider using the W3C IntersectionObserver polyfill.

Usage

  • Just require the mixin from your component and use it with watch:

     inViewport = require('vue-in-viewport-mixin');
     module.exports = {
     	mixins: [ inViewport ],
     	watch: {
     		'inViewport.now': function(visible) {
     			console.log('This component is '+( visible ? 'in-viewport' : 'hidden'));
     		}
     	}
     }
  • Use the optional offset props to configure the component:

     <large-copy
     	:in-viewport-root-margin='-50% 0%'
     	:in-viewport-once='true'>
     </large-copy>
  • Use data values within your component to trigger transitions (or do other things):

     <div class='.large-copy'>
     	<transition name='fade'>
     		<h1 v-show='inViewport.now'>{{ copy.title }}</h1>
     	</transition>
     </div>
  • It's worth noting that the IntersectionObserver counts an element as intersecting the viewport when it's top offset is equal to the height of height of the page. For instance, if you have an element with margin-top: 100vh, that actually counts as intersecting and this mixin will have inViewport.now == true.

  • If your component's height changes, you should call this.reInitInViewportMixin() to recreate the IntersectionObserver instance with the new, calculated threshold.

Props

  • in-viewport-active (true) - Whether to listen update the mixin data. Setting to false later in the lifecyle will remove listeners and setting back to true will re-apply listeners.

  • in-viewport-once (false) - Whether to remove listeners once the component enters viewport. If the component is in viewport when mounted, listeners are never added.

  • in-viewport-root-margin (0px 0px -1px 0px) - Specify the IntersectionObserver rootMargin. For example, set to "-20% 0%" to delay the inViewport.now from switching state until your component is 20% of the viewport height into the page. This defaults to 0px 0px -1px 0px so that a target that is positioned at 100vh registers as not in viewport.

  • in-viewport-root - Specify the IntersectionObserver root. Defaults to the browser viewport.

  • in-viewport-threshold ([0,1]) - Specify the IntersectionObserver threshold. The defaults should work for most cases.

Data

The whole point of this package is for you to make use of this data to do stuff. The following describes the data that is mixed into your component. Note that all properties are namespaced under inViewport.

data: {
	inViewport: {
		now: Boolean   // Is some part of the component currently in the viewport?
		fully: Boolean // Is the component completely in the viewport?
		above: Boolean // Is any part of the component above the viewport?
		below: Boolean // Is any part of the component below the viewport?
	}
}

Tests

  1. Start Storybook: yarn storybook
  2. Open Cypress: yarn cypress open

The Travis tests that run on deploy run against the demo site which gets updated as part of the npm version commands.

vue-in-viewport-mixin's People

Contributors

benjaminnolan avatar d-0-r avatar dependabot[bot] avatar eh2406 avatar escapedcat avatar hjrt avatar thelucre avatar weotch 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

vue-in-viewport-mixin's Issues

Error in IE11?

When using this, im getting an error in IE11, saying: "Error in callback for watcher "inViewport.now": "TypeError: Object doesn't support this action"

Isn't it supported in IE11 ?

Add hooks for activated / deactivated

I'm getting the following error:
Uncaught TypeError: Cannot read property 'height' of null at VueComponent.updateInViewport (vendors-9734db2b5db5665331401bfd393f02d0.js:101585)

When using <keep-alive> to cache the <router-view> component, I believe there needs to be activated / deactivated hooks that make sure that the inViewportHandlers are added or removed

Refactor to pass in configuration via a factory function

I'm imagining a new API that is like:

import { makeInViewportMixin } from 'vue-in-viewport-mixin'
export default {
  mixins: [
    makeInViewportMixin({
      once: true,
      intersectionObserverOptions: {
        root: => document.getElementById('scroller')
      }
    })
  ]
}

2.0.0 - images never loading

When we upgraded from 1.0.4 to 2.0.0, we had a small percentage of customers for whom no images loaded (ever, regardless of scrolling). It's impossible to know exactly how many, but based on how many reported it, I'd guess between 1 and 5%. Almost all of them reported the problem on Safari or Mobile Safari. We were never able to reproduce the issue. We rolled back to 1.0.4 and it appears to have resolved the issue; customers who couldn't see images can now see them and we've had no new reports of missing images.

Binding to a different container

With vuewport Mixinx is possibile to bind a different container that not is the BODY?
Becouse i have a container that scroll but is not the body of the page

It doesn't work for horizontal scroll div

Hello,

I have a div with x-scroll and it has some components inside it.

I am trying to do something if a component is shown (when I scroll to right) but inViewport.now returns true for all components in the main div.

Am I missing something or it doesn't work for x-scroll stuff?

Thanks in advance.

Refactor so that multiple calls to getBoundingClientRect() don't get made

Since this gets added to many elements and has a callback that gets called on every scroll, I'm extra concerned about performance. In 19f7308 a new check for whether an element was fully visible got added.

I think a faster approach would be to change the visibility.isInViewport() to like visibility.check() which returns an object with keys like:

inViewport: true
entirelyInViewport: false

ScrollMonitor has these:

  • elementWatcher.isInViewport - true if any part of the element is visible, false if not.
  • elementWatcher.isFullyInViewport - true if the entire element is visible [1].
  • elementWatcher.isAboveViewport - true if any part of the element is above the viewport.
  • elementWatcher.isBelowViewport - true if any part of the element is below the viewport.
  • elementWatcher.top - distance from the top of the document to the top of this watcher.
  • elementWatcher.bottom - distance from the top of the document to the bottom of this watcher.
  • elementWatcher.height - top - bottom.

I think the visibility.check() returns all of these pairs and then mixes them into data on the model so there is only 1 getBoundingClientRect() check and we infer as much stuff as we could possibly need out of it.

IntersectionObserver constructor error while using globally

Working on a Nuxt custom module, I register globally the mixin in a plugin with Vue.mixin(inViewport)
When first loading my page with 3 components using the watcher I get this error:

IntersectionObserver constructor: Element of sequence branch of (double or sequence) is not a finite floating-point value.

If the webpack hot reloader rebundles the error disappears and it all works well...
Can't we use the mixin globally?

Issues detecting above, below, and fully when scrolling fast past a 100vh item

If you are tracking a 100vh item and you scroll from it being below to above the viewport fast, IntersectionObserver doesn't update the handler at all ... like it can miss responding to the scroll even with a threshold that includes 1.

I may need to add a scroll event listener when in viewport to track these things ... do I bring scrollMonitor back for this? Or maybe scroll mediator?

Binding to child elements of the component

I wonder if it would be possible to use this on elements w/in the component, similar to the viewport directive you've made where I can just add the directive anywhere.

Maybe for example w/in my component I add a :in-viewport-child='identifier' and then my watch of inViewport.now is passed that value as a second argument along with visible.

watch: {
  'inViewport.now' (visible, child) {
  }

Or maybe a second type to watch, inViewport.child, this way the feature is a bit more backwards compatible.

I'd definitely find this pretty helpful.

IntersectionObserver constructor error: Infinity in threshold array

Since it is my first message here: thank you for this great piece of software!

I noticed random (?) IntersectionObserver constructor errors because the threshold array (passed to the constructor) is sometimes containing -Infinity. This is coming from a division by target.height that equals 0 (see attached screenshots below, with various debugger watches).
Honestly I don't know how I can help any further but if you can suggest me additional investigations, I would be happy to be "remote controlled" :)

mixin error 1

mixin error 2

npm module does not include compiled files

I have tried use this mixin, but when I import using require(), I get this error:

Module parse failed: ./~/vue-in-viewport-mixin/index.coffee Unexpected character '#' (1:0) You may need an appropriate loader to handle this file type.

Vue 3 compatibility

Hi, I updated this mixing to make it compatible with Vue 3.
Only old option syntax is supported as my goal was to enable Vue 3 based projects to quickly migrate without refactoring code that uses this mixin.
I also migrated project to latest version of Storybook.
Tests run ok and I'm already using it in my Vue 3 based work project.
I didn't updated Cypress too as default support for Coffescript have been dropped starting from version 4.x.

As my changes break compatibility with Vue 2, this can be considered version 3 of the mixin.
Could any maintainer kindly give me permission to open a branch and do a PR?

display: none causing errors

if i use this mixin in a component, and then put something like print_hidden from tailwindcss above or on that component, i get the following error:

TypeError: Failed to construct 'IntersectionObserver': The provided double value is non-finite.
    at VueComponent.addInViewportHandlers (index.js:526)
    at VueComponent.inViewportInit (index.js:514)
    at VueComponent.reInitInViewportMixin (index.js:509)
    at VueComponent.inViewportThresholdWithMax (index.js:500)
    at Watcher.run (commons.app.js:14062)
    at flushSchedulerQueue (commons.app.js:13804)
    at Array.<anonymous> (commons.app.js:11485)
    at flushCallbacks (commons.app.js:11411)

the line that triggers it:

      this.inViewportObserver = new IntersectionObserver(this.updateInViewport, {

In viewport element of the component

Hi guys,

Awesome project!

Newbie with Vue.js here!

I was wondering if this mixin can help me detect if one of the components child, an HTML element, is in view-port.

I have a really tall component (a form with lots of inputs and I want to know when the user is close to an input so that I can show some helpers).

Thanks!

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.