├── README.md ├── package.json └── stickyScroll.js /README.md: -------------------------------------------------------------------------------- 1 | # vue-sticky-scroll 2 | #### update: this directive is for vue 1.x. a vue 2.0 compatible version is available by theomessin [here](https://github.com/theomessin/vue-chat-scroll). 3 | ## a vue.js directive that keeps an element scrolled to the bottom 4 | 5 | 6 | vue-sticky-scroll keeps an eye on your element and whenever content is added inside of it, it scrolls down so that the viewer can remain focused on the newest content! 7 | 8 | 9 | ### install 10 | 11 | NPM: 12 | note: vue-sticky-scroll requires vue 1.x; check your version by running `npm list vue`; a 2.0 compatible version is available [here](https://github.com/theomessin/vue-chat-scroll) 13 | ```bash 14 | npm i --save vue-sticky-scroll 15 | ``` 16 | Require it in your vue.js component file: 17 | 18 | ```javascript 19 | // ES5 20 | var stickyScroll = require('vue-sticky-scroll'); 21 | // ES6 22 | import 'vue-sticky-scroll'; 23 | ``` 24 | 25 | ### usage instructions 26 | 27 | Add `v-sticky-scroll` as an attribute on the element you wish to always scroll to the bottom of: 28 | 29 | ```html 30 |
31 | ``` 32 | 33 | ### options: animate 34 | 35 | the scrolling will jump to the bottom by default. 36 | if you prefer a smooth scroll, add: 37 | - argument 'animate' 38 | - optional: expression (default is 300) 39 | 40 | ```html 41 |
42 | 43 |
44 | ``` 45 | 46 | 47 | ### how it works 48 | 49 | vue-sticky-scroll uses a wonderful, highly underrated browser feature: [mutation observers](https://developer.mozilla.org/en/docs/Web/API/MutationObserver). By creating a `new MutationObserver` and telling it which events to `.observe()`, you can do wonderful things! 50 | This method is much simpler than some implementations of sticky scrolling that use requestAnimationFrame. 51 | 52 | ### license 53 | 54 | WTFPL 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-sticky-scroll", 3 | "version": "0.1.0", 4 | "description": "a vue.js directive that keeps an element scrolled to the bottom as new content is added", 5 | "main": "stickyScroll.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/heatherbooker/vue-sticky-scroll.git" 12 | }, 13 | "keywords": [ 14 | "vue", 15 | "vue-directive", 16 | "scroll", 17 | "autoscroll" 18 | ], 19 | "author": "Heather Booker", 20 | "license": "WTFPL", 21 | "bugs": { 22 | "url": "https://github.com/heatherbooker/vue-sticky-scroll/issues" 23 | }, 24 | "homepage": "https://github.com/heatherbooker/vue-sticky-scroll#readme" 25 | } 26 | -------------------------------------------------------------------------------- /stickyScroll.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // if we are in node.js enviro, require vue 4 | try { 5 | var Vue = require('vue'); 6 | } catch (e) { 7 | // no worries, in browser enviro Vue should already be global 8 | } 9 | 10 | var vueStickyScroll = Vue.directive('sticky-scroll', { 11 | bind: function() { 12 | 13 | //use browser MutationObserver object 14 | var observer = new MutationObserver(scrollToBottom); 15 | //looking for new children that will change the height 16 | var config = { childList: true }; 17 | observer.observe(this.el, config); 18 | 19 | //need reference to this, otherwise 'this'=MutationObserver 20 | var me = this; 21 | 22 | function animateScroll(duration) { 23 | 24 | var start = me.el.scrollTop; 25 | var end = me.el.scrollHeight; 26 | var change = end - start; 27 | var increment = 20; 28 | 29 | function easeInOut(currentTime, start, change, duration) { 30 | //by Robert Penner 31 | currentTime /= duration / 2; 32 | if (currentTime < 1) { 33 | return change / 2 * currentTime * currentTime + start; 34 | } 35 | currentTime -= 1; 36 | return -change / 2 * (currentTime * (currentTime - 2) - 1) + start; 37 | } 38 | 39 | function animate(elapsedTime) { 40 | 41 | elapsedTime += increment; 42 | var position = easeInOut(elapsedTime, start, change, duration); 43 | me.el.scrollTop = position; 44 | 45 | if (elapsedTime < duration) { 46 | setTimeout(function() { 47 | animate(elapsedTime); 48 | }, increment) 49 | } 50 | } 51 | animate(0); 52 | } 53 | 54 | function scrollToBottom() { 55 | if (me.arg === 'animate') { 56 | //default is 300 57 | var duration = Number(me.expression) || 300; 58 | animateScroll(duration); 59 | } else { 60 | //default is jump to bottom 61 | me.el.scrollTop = me.el.scrollHeight; 62 | } 63 | } 64 | } 65 | }); 66 | 67 | // check whether we are in node.js enviro 68 | try { 69 | module.exports = vueStickyScroll; 70 | } catch (e) { 71 | // no worries, our directive will just be registered in browser 72 | } 73 | 74 | })(); 75 | --------------------------------------------------------------------------------