├── .npmrc
├── test
├── specs
│ └── index.spec.js
└── index.js
├── .eslintignore
├── .gitignore
├── img
└── vue.png
├── .babelrc
├── docs
├── main.js
└── App.vue
├── .npmignore
├── src
├── index.js
└── sticky.js
├── .editorconfig
├── index.html
├── circle.yml
├── LICENSE
├── karma.conf.js
├── .eslintrc.js
├── README.md
├── package.json
└── dist
├── min
└── vue-sticky-js.min.js
├── vue-sticky-js.js.map
└── vue-sticky-js.js
/.npmrc:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/specs/index.spec.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | dist
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | lib
4 | *.log
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/img/vue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ianaya89/vue-sticky-js/HEAD/img/vue.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"],
3 | "plugins": ["transform-runtime"],
4 | "comments": false
5 | }
6 |
--------------------------------------------------------------------------------
/docs/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 |
4 | new Vue({
5 | el: 'body',
6 | components: { App }
7 | });
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | test
3 | build
4 | docs
5 | test
6 | .babelrc
7 | .eslintignore
8 | .eslintrc
9 | karma.conf.js
10 | circle.yml
11 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import stickyDirective from './sticky';
2 |
3 |
4 | module.exports = {
5 | install(Vue) {
6 | Vue.directive('sticky', stickyDirective);
7 | },
8 |
9 | stickyDirective
10 | };
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Docs | vue-sticky-js
7 |
8 |
9 |
10 | vue-sticky-js
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | /* global */
2 |
3 | // require all test files (files that ends with .spec.js)
4 | const testsContext = require.context('./specs/', true, /\.spec$/);
5 | testsContext.keys().forEach(testsContext);
6 |
7 |
8 | // require all src files except main.js for coverage.
9 | // you can also change this to match only the subset of files that
10 | // you want coverage for.
11 | const srcContext = require.context('../src', true, /\.js$/);
12 | srcContext.keys().forEach(srcContext);
13 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | timezone:
3 | America/Vancouver
4 | node:
5 | version: 6
6 |
7 | dependencies:
8 | override:
9 | - npm install
10 | - npm install coveralls
11 | - npm install bithound
12 |
13 | test:
14 | override:
15 | - npm test
16 | - COVERALLS_SERVICE_NAME="circleci" COVERALLS_SERVICE_JOB_ID="$CIRCLE_BUILD_NUM" ./node_modules/coveralls/bin/coveralls.js < ./coverage/lcov.info
17 | - ./node_modules/.bin/bithound check BITHOUND_TOKEN
18 | post:
19 | - cp -R ./test/unit/coverage/* $CIRCLE_ARTIFACTS/
20 |
--------------------------------------------------------------------------------
/src/sticky.js:
--------------------------------------------------------------------------------
1 | import Sticky from 'sticky-js';
2 |
3 | export default {
4 | bind(val) {
5 | this.el.parentElement.setAttribute('data-sticky-container', '');
6 | this.el.className += ' sticky';
7 | },
8 |
9 | update(val) {
10 | if (val) {
11 | if (val.marginTop) { this.el.setAttribute('data-margin-top', val.marginTop); }
12 | if (val.forName) { this.el.setAttribute('data-sticky-for', val.forName); }
13 | if (val.className) { this.el.setAttribute('data-sticky-class', val.className); }
14 | }
15 |
16 | new Sticky('.sticky');
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 <%= authorName %>
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const merge = require('webpack-merge');
3 |
4 | const baseConfig = require('./webpack.config');
5 |
6 | const webpackConfig = merge(baseConfig, {
7 | devtool: '#inline-source-map'
8 | });
9 |
10 | delete webpackConfig.entry;
11 |
12 | webpackConfig.module.preLoaders = webpackConfig.module.preLoaders || [];
13 |
14 | webpackConfig.module.preLoaders.unshift({
15 | test : /\.js$/,
16 | loader : 'isparta',
17 | include: path.resolve('src/')
18 | });
19 |
20 | webpackConfig.module.loaders.some((loader, i) => {
21 | if (loader.loader === 'babel') {
22 | loader.include = path.resolve('test/');
23 | return true;
24 | }
25 |
26 | return false;
27 | });
28 |
29 | module.exports = function(config) {
30 | config.set({
31 | browsers: ['PhantomJS'],
32 | frameworks: ['mocha'],
33 | reporters: ['spec', 'coverage'],
34 | files: ['test/index.js'],
35 | preprocessors: {
36 | 'test/index.js': ['webpack', 'sourcemap']
37 | },
38 | webpack: webpackConfig,
39 | webpackMiddleware: { noInfo: true },
40 | colors: true,
41 | logLevel: config.LOG_DISABLE,
42 | coverageReporter: {
43 | dir : './coverage',
44 | reporters: [
45 | { type: 'lcov', subdir: '.' },
46 | { type: 'text-summary' }
47 | ]
48 | }
49 | });
50 | };
51 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root : true,
3 | extends : 'airbnb-base',
4 | plugins : [ 'html' ],
5 | rules : {
6 | 'no-multi-spaces' : 0,
7 | 'no-underscore-dangle' : [0],
8 | 'consistent-return' : 0,
9 | 'no-unused-expressions' : [2, { 'allowShortCircuit': true }],
10 | 'no-param-reassign' : 0,
11 | 'func-names' : 0,
12 | 'space-before-function-paren' : [2, 'never'],
13 | 'comma-dangle' : [2, 'never'],
14 | 'no-shadow' : 0,
15 | 'guard-for-in' : 0,
16 | 'no-restricted-syntax' : [2, 'WithStatement'],
17 | 'newline-per-chained-call' : [2, { 'ignoreChainWithDepth': 5 }],
18 | 'space-in-parens' : 0,
19 | 'key-spacing' : 0,
20 | 'no-unused-vars' : [2, { 'vars': 'all', 'args': 'none' }],
21 | 'max-len' : 1,
22 | 'padded-blocks' : 0,
23 | 'no-console' : 0,
24 | 'no-continue' : 0,
25 | 'no-extra-boolean-cast' : 0,
26 | 'import/no-extraneous-dependencies': 0,
27 | 'import/newline-after-import' : 0,
28 | 'no-mixed-operators' : 0,
29 | 'import/no-unresolved' : 0,
30 | 'no-prototype-builtins' : 0,
31 | 'no-bitwise' : ['error', { 'allow': ['~'] }],
32 | 'no-new' : 0
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-sticky-js
2 |
3 | >🏏 Vue.js directive to make sticky elements built with [sticky-js](https://github.com/rgalus/sticky-js)
4 |
5 | **⚠️ Vue.js 2 is not supported yet. [WIP]**
6 |
7 |
8 | ## Prerequisites
9 | [Vue.js](https://github.com/vuejs/vue)
10 |
11 | ## Installation
12 | `$ npm i -S vue-sticky-js`
13 |
14 | ## Directive
15 | `v-sticky` => Define a new sticky element.
16 |
17 | ## Implementation
18 |
19 | ### Global
20 | `main.js`
21 | ```javascript
22 | import Vue from 'vue';
23 | import VueSticky from 'vue-sticky-js';
24 |
25 | Vue.use(VueSticky.install);
26 |
27 | // ...
28 | ```
29 |
30 | `Component.vue`
31 | ```html
32 |
48 |
49 |
50 | .container
51 | .box(v-sticky='stickyOptions') //- stickyOptions are not mandatory
52 | p Sticky Element
53 |
54 |
55 | ```
56 |
57 | ### Component
58 |
59 | `Component.vue`
60 | ```html
61 |
73 |
74 |
75 | .container
76 | .box(v-s) //- stickyOptions are not mandatory
77 | p Sticky Element
78 |
79 | ```
80 |
81 | ## Development Setup
82 | ```bash
83 | # install dependencies
84 | $ npm install
85 |
86 | # dev mode
87 | $ npm run dev
88 |
89 | # test
90 | $ npm run test
91 |
92 | # build
93 | $ npm run build
94 | ```
95 |
96 | **This project was built with [yeoman](http://yeoman.io/) and [generator-vue-component](https://github.com/ianaya89/generator-vue-component) :heart:**
97 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-sticky-js",
3 | "version": "1.0.0",
4 | "description": "Vue.js directive to make sticky elements built on sticky-js",
5 | "main": "dist/vue-sticky-js.min.js",
6 | "author": {
7 | "name": "Ignacio Anaya",
8 | "email": "ignacio.anaya89@gmail.com"
9 | },
10 | "license": "MIT",
11 | "repository": {
12 | "type" : "git",
13 | "url" : "https://github.com/ianaya89/vue-sticky-js"
14 | },
15 | "scripts": {
16 | "dev": "webpack-dev-server --inline --hot --quiet --port 8080 --open --config build/webpack.config.js",
17 | "build-all": "npm run build && npm run build-docs",
18 | "build-docs": "webpack --progress --hide-modules --config build/webpack.config.js && set NODE_ENV=production webpack --progress --hide-modules",
19 | "build": "webpack --progress --hide-modules --config build/webpack.build.min.js && webpack --progress --hide-modules --config build/webpack.build.js",
20 | "prepublish": "npm run build-all",
21 | "eslint": "eslint --ext .js,.vue src"
22 | },
23 | "devDependencies": {
24 | "babel-core": "^6.1.21",
25 | "babel-loader": "^6.1.0",
26 | "babel-plugin-transform-runtime": "^6.1.18",
27 | "babel-preset-es2015": "^6.1.18",
28 | "babel-runtime": "^6.3.19",
29 | "css-loader": "^0.21.0",
30 | "eslint": "^3.5.0",
31 | "eslint-config-airbnb": "^11.1.0",
32 | "eslint-loader": "^1.5.0",
33 | "eslint-plugin-html": "^1.5.2",
34 | "eslint-plugin-import": "^1.15.0",
35 | "eslint-plugin-jsx-a11y": "^2.2.2",
36 | "eslint-plugin-react": "^6.2.1",
37 | "extract-text-webpack-plugin": "^1.0.1",
38 | "less": "^2.5.3",
39 | "less-loader": "^2.2.1",
40 | "node-sass": "^3.4.1",
41 | "prismjs": "^1.5.1",
42 | "pug": "^2.0.0-beta6",
43 | "sass-loader": "^3.1.1",
44 | "style-loader": "^0.13.0",
45 | "url-loader": "^0.5.7",
46 | "vue": "^1.0.26",
47 | "vue-hot-reload-api": "^1.2.0",
48 | "vue-html-loader": "^1.2.3",
49 | "vue-loader": "8.5.3",
50 | "vue-style-loader": "^1.0.0",
51 | "webpack": "^1.12.2",
52 | "webpack-dev-server": "^1.12.0"
53 | },
54 | "pre-commit": [
55 | "eslint"
56 | ],
57 | "dependencies": {
58 | "sticky-js": "^1.1.4"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/docs/App.vue:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 | .container
27 | .box(v-s='stickyOptions')
28 | p Sticky Element
29 |
30 | .box
31 | | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
32 | | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
33 | | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
34 | | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
35 | | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
36 | | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
37 |
38 |
39 |
68 |
--------------------------------------------------------------------------------
/dist/min/vue-sticky-js.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports["vue-sticky-js"]=e():t["vue-sticky-js"]=e()}(this,function(){return function(t){function e(s){if(i[s])return i[s].exports;var o=i[s]={exports:{},id:s,loaded:!1};return t[s].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){"use strict";function s(t){return t&&t.__esModule?t:{default:t}}var o=i(1),c=s(o);t.exports={install:function(t){t.directive("sticky",c.default)},stickyDirective:c.default}},function(t,e,i){"use strict";function s(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=i(2),c=s(o);e.default={bind:function(t){this.el.parentElement.setAttribute("data-sticky-container",""),this.el.className+=" sticky"},update:function(t){t&&(t.marginTop&&this.el.setAttribute("data-margin-top",t.marginTop),t.forName&&this.el.setAttribute("data-sticky-for",t.forName),t.className&&this.el.setAttribute("data-sticky-class",t.className)),new c.default(".sticky")}}},function(t,e,i){var s=i(3);t.exports=s},function(t,e,i){function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}/**
2 | * Sticky.js
3 | * Library for sticky elements written in vanilla javascript. With this library you can easily set sticky elements on your website. It's also responsive.
4 | *
5 | * @version 1.2.0
6 | * @author Rafal Galus
7 | * @website https://rgalus.github.io/sticky-js/
8 | * @repo https://github.com/rgalus/sticky-js
9 | * @license https://github.com/rgalus/sticky-js/blob/master/LICENSE
10 | */
11 | var o=function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};s(this,t),this.selector=e,this.elements=[],this.version="1.2.0",this.vp=this.getViewportSize(),this.body=document.querySelector("body"),this.options={wrap:i.wrap||!1,marginTop:i.marginTop||0,stickyFor:i.stickyFor||0,stickyClass:i.stickyClass||null,stickyContainer:i.stickyContainer||"body"},this.updateScrollTopPosition=this.updateScrollTopPosition.bind(this),this.updateScrollTopPosition(),window.addEventListener("load",this.updateScrollTopPosition),window.addEventListener("scroll",this.updateScrollTopPosition),this.run()}return t.prototype.run=function(){var t=this,e=setInterval(function(){if("complete"===document.readyState){clearInterval(e);var i=document.querySelectorAll(t.selector);t.forEach(i,function(e){return t.renderElement(e)})}},10)},t.prototype.renderElement=function(t){var e=this;t.sticky={},t.sticky.active=!1,t.sticky.marginTop=parseInt(t.getAttribute("data-margin-top"))||this.options.marginTop,t.sticky.stickyFor=parseInt(t.getAttribute("data-sticky-for"))||this.options.stickyFor,t.sticky.stickyClass=t.getAttribute("data-sticky-class")||this.options.stickyClass,t.sticky.wrap=!!t.hasAttribute("data-sticky-wrap")||this.options.wrap,t.sticky.stickyContainer=this.options.stickyContainer,t.sticky.container=this.getStickyContainer(t),t.sticky.container.rect=this.getRectangle(t.sticky.container),t.sticky.rect=this.getRectangle(t),"img"===t.tagName.toLowerCase()&&(t.onload=function(){return t.sticky.rect=e.getRectangle(t)}),t.sticky.wrap&&this.wrapElement(t),this.activate(t)},t.prototype.wrapElement=function(t){t.insertAdjacentHTML("beforebegin",""),t.previousSibling.appendChild(t)},t.prototype.activate=function(t){t.sticky.rect.top+t.sticky.rect.height=t.sticky.container.rect.top+t.sticky.container.rect.height||t.sticky.stickyFor>=this.vp.width&&t.sticky.active)&&(t.sticky.active=!1),this.setPosition(t)},t.prototype.initScrollEvents=function(t){var e=this;t.sticky.scrollListener=function(){return e.onScrollEvents(t)},window.addEventListener("scroll",t.sticky.scrollListener)},t.prototype.destroyScrollEvents=function(t){window.removeEventListener("scroll",t.sticky.scrollListener)},t.prototype.onScrollEvents=function(t){t.sticky.active&&this.setPosition(t)},t.prototype.setPosition=function(t){this.css(t,{position:"",width:"",top:"",left:""}),this.vp.heightt.sticky.rect.top-t.sticky.marginTop?(this.css(t,{position:"fixed",width:t.sticky.rect.width+"px",left:t.sticky.rect.left+"px"}),this.scrollTop+t.sticky.rect.height+t.sticky.marginTop>t.sticky.container.rect.top+t.sticky.container.offsetHeight?(t.sticky.stickyClass&&t.classList.remove(t.sticky.stickyClass),this.css(t,{top:t.sticky.container.rect.top+t.sticky.container.offsetHeight-(this.scrollTop+t.sticky.rect.height)+"px"})):(t.sticky.stickyClass&&t.classList.add(t.sticky.stickyClass),this.css(t,{top:t.sticky.marginTop+"px"}))):(t.sticky.stickyClass&&t.classList.remove(t.sticky.stickyClass),this.css(t,{position:"",width:"",top:"",left:""}),t.sticky.wrap&&this.css(t.parentNode,{display:"",width:"",height:""})))},t.prototype.update=function(){var t=this;this.forEach(this.elements,function(e){e.sticky.rect=t.getRectangle(e),e.sticky.container.rect=t.getRectangle(e.sticky.container),t.activate(e),t.setPosition(e)})},t.prototype.destroy=function(){var t=this;this.forEach(this.elements,function(e){t.destroyResizeEvents(e),t.destroyScrollEvents(e),delete e.sticky})},t.prototype.getStickyContainer=function(t){for(var e=t.parentNode;!e.hasAttribute("data-sticky-container")&&!e.parentNode.querySelector(t.sticky.stickyContainer)&&e!==this.body;)e=e.parentNode;return e},t.prototype.getRectangle=function(t){this.css(t,{position:"",width:"",top:"",left:""});var e=Math.max(t.offsetWidth,t.clientWidth,t.scrollWidth),i=Math.max(t.offsetHeight,t.clientHeight,t.scrollHeight),s=0,o=0;do s+=t.offsetTop||0,o+=t.offsetLeft||0,t=t.offsetParent;while(t);return{top:s,left:o,width:e,height:i}},t.prototype.getViewportSize=function(){return{width:Math.max(document.documentElement.clientWidth,window.innerWidth||0),height:Math.max(document.documentElement.clientHeight,window.innerHeight||0)}},t.prototype.updateScrollTopPosition=function(){this.scrollTop=(window.pageYOffset||document.scrollTop)-(document.clientTop||0)||0},t.prototype.forEach=function(t,e){for(var i=0,s=t.length;i\n * @website https://rgalus.github.io/sticky-js/\n * @repo https://github.com/rgalus/sticky-js\n * @license https://github.com/rgalus/sticky-js/blob/master/LICENSE\n */\n\nvar Sticky = function () {\n /**\n * Sticky instance constructor\n * @constructor\n * @param {string} selector - Selector which we can find elements\n * @param {string} options - Global options for sticky elements (could be overwritten by data-{option}=\"\" attributes)\n */\n function Sticky() {\n var selector = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n _classCallCheck(this, Sticky);\n\n this.selector = selector;\n this.elements = [];\n\n this.version = '1.2.0';\n\n this.vp = this.getViewportSize();\n this.body = document.querySelector('body');\n\n this.options = {\n wrap: options.wrap || false,\n marginTop: options.marginTop || 0,\n stickyFor: options.stickyFor || 0,\n stickyClass: options.stickyClass || null,\n stickyContainer: options.stickyContainer || 'body'\n };\n\n this.updateScrollTopPosition = this.updateScrollTopPosition.bind(this);\n\n this.updateScrollTopPosition();\n window.addEventListener('load', this.updateScrollTopPosition);\n window.addEventListener('scroll', this.updateScrollTopPosition);\n\n this.run();\n }\n\n /**\n * Function that waits for page to be fully loaded and then renders & activates every sticky element found with specified selector\n * @function\n */\n\n\n Sticky.prototype.run = function run() {\n var _this = this;\n\n // wait for page to be fully loaded\n var pageLoaded = setInterval(function () {\n if (document.readyState === 'complete') {\n clearInterval(pageLoaded);\n\n var elements = document.querySelectorAll(_this.selector);\n _this.forEach(elements, function (element) {\n return _this.renderElement(element);\n });\n }\n }, 10);\n };\n\n /**\n * Function that assign needed variables for sticky element, that are used in future for calculations and other\n * @function\n * @param {node} element - Element to be rendered\n */\n\n\n Sticky.prototype.renderElement = function renderElement(element) {\n var _this2 = this;\n\n // create container for variables needed in future\n element.sticky = {};\n\n // set default variables\n element.sticky.active = false;\n\n element.sticky.marginTop = parseInt(element.getAttribute('data-margin-top')) || this.options.marginTop;\n element.sticky.stickyFor = parseInt(element.getAttribute('data-sticky-for')) || this.options.stickyFor;\n element.sticky.stickyClass = element.getAttribute('data-sticky-class') || this.options.stickyClass;\n element.sticky.wrap = element.hasAttribute('data-sticky-wrap') ? true : this.options.wrap;\n // @todo attribute for stickyContainer\n // element.sticky.stickyContainer = element.getAttribute('data-sticky-container') || this.options.stickyContainer;\n element.sticky.stickyContainer = this.options.stickyContainer;\n\n element.sticky.container = this.getStickyContainer(element);\n element.sticky.container.rect = this.getRectangle(element.sticky.container);\n\n element.sticky.rect = this.getRectangle(element);\n\n // fix when element is image that has not yet loaded and width, height = 0\n if (element.tagName.toLowerCase() === 'img') {\n element.onload = function () {\n return element.sticky.rect = _this2.getRectangle(element);\n };\n }\n\n if (element.sticky.wrap) {\n this.wrapElement(element);\n }\n\n // activate rendered element\n this.activate(element);\n };\n\n /**\n * Wraps element into placeholder element\n * @function\n * @param {node} element - Element to be wrapped\n */\n\n\n Sticky.prototype.wrapElement = function wrapElement(element) {\n element.insertAdjacentHTML('beforebegin', '');\n element.previousSibling.appendChild(element);\n };\n\n /**\n * Function that activates element when specified conditions are met and then initalise events\n * @function\n * @param {node} element - Element to be activated\n */\n\n\n Sticky.prototype.activate = function activate(element) {\n if (element.sticky.rect.top + element.sticky.rect.height < element.sticky.container.rect.top + element.sticky.container.rect.height && element.sticky.stickyFor < this.vp.width && !element.sticky.active) {\n element.sticky.active = true;\n }\n\n if (this.elements.indexOf(element) < 0) {\n this.elements.push(element);\n }\n\n if (!element.sticky.resizeEvent) {\n this.initResizeEvents(element);\n element.sticky.resizeEvent = true;\n }\n\n if (!element.sticky.scrollEvent) {\n this.initScrollEvents(element);\n element.sticky.scrollEvent = true;\n }\n\n this.setPosition(element);\n };\n\n /**\n * Function which is adding onResizeEvents to window listener and assigns function to element as resizeListener\n * @function\n * @param {node} element - Element for which resize events are initialised\n */\n\n\n Sticky.prototype.initResizeEvents = function initResizeEvents(element) {\n var _this3 = this;\n\n element.sticky.resizeListener = function () {\n return _this3.onResizeEvents(element);\n };\n window.addEventListener('resize', element.sticky.resizeListener);\n };\n\n /**\n * Removes element listener from resize event\n * @function\n * @param {node} element - Element from which listener is deleted\n */\n\n\n Sticky.prototype.destroyResizeEvents = function destroyResizeEvents(element) {\n window.removeEventListener('resize', element.sticky.resizeListener);\n };\n\n /**\n * Function which is fired when user resize window. It checks if element should be activated or deactivated and then run setPosition function\n * @function\n * @param {node} element - Element for which event function is fired\n */\n\n\n Sticky.prototype.onResizeEvents = function onResizeEvents(element) {\n this.vp = this.getViewportSize();\n\n element.sticky.rect = this.getRectangle(element);\n element.sticky.container.rect = this.getRectangle(element.sticky.container);\n\n if (element.sticky.rect.top + element.sticky.rect.height < element.sticky.container.rect.top + element.sticky.container.rect.height && element.sticky.stickyFor < this.vp.width && !element.sticky.active) {\n element.sticky.active = true;\n } else if (element.sticky.rect.top + element.sticky.rect.height >= element.sticky.container.rect.top + element.sticky.container.rect.height || element.sticky.stickyFor >= this.vp.width && element.sticky.active) {\n element.sticky.active = false;\n }\n\n this.setPosition(element);\n };\n\n /**\n * Function which is adding onScrollEvents to window listener and assigns function to element as scrollListener\n * @function\n * @param {node} element - Element for which scroll events are initialised\n */\n\n\n Sticky.prototype.initScrollEvents = function initScrollEvents(element) {\n var _this4 = this;\n\n element.sticky.scrollListener = function () {\n return _this4.onScrollEvents(element);\n };\n window.addEventListener('scroll', element.sticky.scrollListener);\n };\n\n /**\n * Removes element listener from scroll event\n * @function\n * @param {node} element - Element from which listener is deleted\n */\n\n\n Sticky.prototype.destroyScrollEvents = function destroyScrollEvents(element) {\n window.removeEventListener('scroll', element.sticky.scrollListener);\n };\n\n /**\n * Function which is fired when user scroll window. If element is active, function is invoking setPosition function\n * @function\n * @param {node} element - Element for which event function is fired\n */\n\n\n Sticky.prototype.onScrollEvents = function onScrollEvents(element) {\n if (element.sticky.active) {\n this.setPosition(element);\n }\n };\n\n /**\n * Main function for the library. Here are some condition calculations and css appending for sticky element when user scroll window\n * @function\n * @param {node} element - Element that will be positioned if it's active\n */\n\n\n Sticky.prototype.setPosition = function setPosition(element) {\n this.css(element, { position: '', width: '', top: '', left: '' });\n\n if (this.vp.height < element.sticky.rect.height || !element.sticky.active) {\n return;\n }\n\n if (!element.sticky.rect.width) {\n element.sticky.rect = this.getRectangle(element);\n }\n\n if (element.sticky.wrap) {\n this.css(element.parentNode, {\n display: 'block',\n width: element.sticky.rect.width + 'px',\n height: element.sticky.rect.height + 'px'\n });\n }\n\n if (element.sticky.rect.top === 0 && element.sticky.container === this.body) {\n this.css(element, {\n position: 'fixed',\n top: element.sticky.rect.top + 'px',\n left: element.sticky.rect.left + 'px',\n width: element.sticky.rect.width + 'px'\n });\n } else if (this.scrollTop > element.sticky.rect.top - element.sticky.marginTop) {\n this.css(element, {\n position: 'fixed',\n width: element.sticky.rect.width + 'px',\n left: element.sticky.rect.left + 'px'\n });\n\n if (this.scrollTop + element.sticky.rect.height + element.sticky.marginTop > element.sticky.container.rect.top + element.sticky.container.offsetHeight) {\n\n if (element.sticky.stickyClass) {\n element.classList.remove(element.sticky.stickyClass);\n }\n\n this.css(element, {\n top: element.sticky.container.rect.top + element.sticky.container.offsetHeight - (this.scrollTop + element.sticky.rect.height) + 'px' });\n } else {\n if (element.sticky.stickyClass) {\n element.classList.add(element.sticky.stickyClass);\n }\n\n this.css(element, { top: element.sticky.marginTop + 'px' });\n }\n } else {\n if (element.sticky.stickyClass) {\n element.classList.remove(element.sticky.stickyClass);\n }\n\n this.css(element, { position: '', width: '', top: '', left: '' });\n\n if (element.sticky.wrap) {\n this.css(element.parentNode, { display: '', width: '', height: '' });\n }\n }\n };\n\n /**\n * Function that updates element sticky rectangle (with sticky container), then activate or deactivate element, then update position if it's active\n * @function\n */\n\n\n Sticky.prototype.update = function update() {\n var _this5 = this;\n\n this.forEach(this.elements, function (element) {\n element.sticky.rect = _this5.getRectangle(element);\n element.sticky.container.rect = _this5.getRectangle(element.sticky.container);\n\n _this5.activate(element);\n _this5.setPosition(element);\n });\n };\n\n /**\n * Destroys sticky element, remove listeners\n * @function\n */\n\n\n Sticky.prototype.destroy = function destroy() {\n var _this6 = this;\n\n this.forEach(this.elements, function (element) {\n _this6.destroyResizeEvents(element);\n _this6.destroyScrollEvents(element);\n delete element.sticky;\n });\n };\n\n /**\n * Function that returns container element in which sticky element is stuck (if is not specified, then it's stuck to body)\n * @function\n * @param {node} element - Element which sticky container are looked for\n * @return {node} element - Sticky container\n */\n\n\n Sticky.prototype.getStickyContainer = function getStickyContainer(element) {\n var container = element.parentNode;\n\n while (!container.hasAttribute('data-sticky-container') && !container.parentNode.querySelector(element.sticky.stickyContainer) && container !== this.body) {\n container = container.parentNode;\n }\n\n return container;\n };\n\n /**\n * Function that returns element rectangle & position (width, height, top, left)\n * @function\n * @param {node} element - Element which position & rectangle are returned\n * @return {object}\n */\n\n\n Sticky.prototype.getRectangle = function getRectangle(element) {\n this.css(element, { position: '', width: '', top: '', left: '' });\n\n var width = Math.max(element.offsetWidth, element.clientWidth, element.scrollWidth);\n var height = Math.max(element.offsetHeight, element.clientHeight, element.scrollHeight);\n\n var top = 0;\n var left = 0;\n\n do {\n top += element.offsetTop || 0;\n left += element.offsetLeft || 0;\n element = element.offsetParent;\n } while (element);\n\n return { top: top, left: left, width: width, height: height };\n };\n\n /**\n * Function that returns viewport dimensions\n * @function\n * @return {object}\n */\n\n\n Sticky.prototype.getViewportSize = function getViewportSize() {\n return {\n width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),\n height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0)\n };\n };\n\n /**\n * Function that updates window scroll position\n * @function\n * @return {number}\n */\n\n\n Sticky.prototype.updateScrollTopPosition = function updateScrollTopPosition() {\n this.scrollTop = (window.pageYOffset || document.scrollTop) - (document.clientTop || 0) || 0;\n };\n\n /**\n * Helper function for loops\n * @helper\n * @param {array}\n * @param {function} callback - Callback function (no need for explanation)\n */\n\n\n Sticky.prototype.forEach = function forEach(array, callback) {\n for (var i = 0, len = array.length; i < len; i++) {\n callback(array[i]);\n }\n };\n\n /**\n * Helper function to add/remove css properties for specified element.\n * @helper\n * @param {node} element - DOM element\n * @param {object} properties - CSS properties that will be added/removed from specified element\n */\n\n\n Sticky.prototype.css = function css(element, properties) {\n for (var property in properties) {\n if (properties.hasOwnProperty(property)) {\n element.style[property] = properties[property];\n }\n }\n };\n\n return Sticky;\n}();\n\n/**\n * Export function that supports AMD, CommonJS and Plain Browser.\n */\n\n\n(function (root, factory) {\n if (typeof exports !== 'undefined') {\n module.exports = factory;\n } else if (typeof define === 'function' && define.amd) {\n define([], factory);\n } else {\n root.Sticky = factory;\n }\n})(this, Sticky);\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/sticky-js/dist/sticky.compile.js\n// module id = 3\n// module chunks = 0"],"sourceRoot":""}
--------------------------------------------------------------------------------
/dist/vue-sticky-js.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory();
4 | else if(typeof define === 'function' && define.amd)
5 | define([], factory);
6 | else if(typeof exports === 'object')
7 | exports["vue-sticky-js"] = factory();
8 | else
9 | root["vue-sticky-js"] = factory();
10 | })(this, function() {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 | /******/
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 | /******/
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId])
20 | /******/ return installedModules[moduleId].exports;
21 | /******/
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ exports: {},
25 | /******/ id: moduleId,
26 | /******/ loaded: false
27 | /******/ };
28 | /******/
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 | /******/
32 | /******/ // Flag the module as loaded
33 | /******/ module.loaded = true;
34 | /******/
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 | /******/
39 | /******/
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 | /******/
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 | /******/
46 | /******/ // __webpack_public_path__
47 | /******/ __webpack_require__.p = "";
48 | /******/
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ([
54 | /* 0 */
55 | /***/ (function(module, exports, __webpack_require__) {
56 |
57 | 'use strict';
58 |
59 | var _sticky = __webpack_require__(1);
60 |
61 | var _sticky2 = _interopRequireDefault(_sticky);
62 |
63 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
64 |
65 | module.exports = {
66 | install: function install(Vue) {
67 | Vue.directive('sticky', _sticky2.default);
68 | },
69 |
70 |
71 | stickyDirective: _sticky2.default
72 | };
73 |
74 | /***/ }),
75 | /* 1 */
76 | /***/ (function(module, exports, __webpack_require__) {
77 |
78 | 'use strict';
79 |
80 | Object.defineProperty(exports, "__esModule", {
81 | value: true
82 | });
83 |
84 | var _stickyJs = __webpack_require__(2);
85 |
86 | var _stickyJs2 = _interopRequireDefault(_stickyJs);
87 |
88 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
89 |
90 | exports.default = {
91 | bind: function bind(val) {
92 | this.el.parentElement.setAttribute('data-sticky-container', '');
93 | this.el.className += ' sticky';
94 | },
95 | update: function update(val) {
96 | if (val) {
97 | if (val.marginTop) {
98 | this.el.setAttribute('data-margin-top', val.marginTop);
99 | }
100 | if (val.forName) {
101 | this.el.setAttribute('data-sticky-for', val.forName);
102 | }
103 | if (val.className) {
104 | this.el.setAttribute('data-sticky-class', val.className);
105 | }
106 | }
107 |
108 | new _stickyJs2.default('.sticky');
109 | }
110 | };
111 |
112 | /***/ }),
113 | /* 2 */
114 | /***/ (function(module, exports, __webpack_require__) {
115 |
116 |
117 | var Sticky = __webpack_require__(3);
118 |
119 | module.exports = Sticky;
120 |
121 |
122 | /***/ }),
123 | /* 3 */
124 | /***/ (function(module, exports, __webpack_require__) {
125 |
126 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
127 |
128 | /**
129 | * Sticky.js
130 | * Library for sticky elements written in vanilla javascript. With this library you can easily set sticky elements on your website. It's also responsive.
131 | *
132 | * @version 1.2.0
133 | * @author Rafal Galus
134 | * @website https://rgalus.github.io/sticky-js/
135 | * @repo https://github.com/rgalus/sticky-js
136 | * @license https://github.com/rgalus/sticky-js/blob/master/LICENSE
137 | */
138 |
139 | var Sticky = function () {
140 | /**
141 | * Sticky instance constructor
142 | * @constructor
143 | * @param {string} selector - Selector which we can find elements
144 | * @param {string} options - Global options for sticky elements (could be overwritten by data-{option}="" attributes)
145 | */
146 | function Sticky() {
147 | var selector = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
148 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
149 |
150 | _classCallCheck(this, Sticky);
151 |
152 | this.selector = selector;
153 | this.elements = [];
154 |
155 | this.version = '1.2.0';
156 |
157 | this.vp = this.getViewportSize();
158 | this.body = document.querySelector('body');
159 |
160 | this.options = {
161 | wrap: options.wrap || false,
162 | marginTop: options.marginTop || 0,
163 | stickyFor: options.stickyFor || 0,
164 | stickyClass: options.stickyClass || null,
165 | stickyContainer: options.stickyContainer || 'body'
166 | };
167 |
168 | this.updateScrollTopPosition = this.updateScrollTopPosition.bind(this);
169 |
170 | this.updateScrollTopPosition();
171 | window.addEventListener('load', this.updateScrollTopPosition);
172 | window.addEventListener('scroll', this.updateScrollTopPosition);
173 |
174 | this.run();
175 | }
176 |
177 | /**
178 | * Function that waits for page to be fully loaded and then renders & activates every sticky element found with specified selector
179 | * @function
180 | */
181 |
182 |
183 | Sticky.prototype.run = function run() {
184 | var _this = this;
185 |
186 | // wait for page to be fully loaded
187 | var pageLoaded = setInterval(function () {
188 | if (document.readyState === 'complete') {
189 | clearInterval(pageLoaded);
190 |
191 | var elements = document.querySelectorAll(_this.selector);
192 | _this.forEach(elements, function (element) {
193 | return _this.renderElement(element);
194 | });
195 | }
196 | }, 10);
197 | };
198 |
199 | /**
200 | * Function that assign needed variables for sticky element, that are used in future for calculations and other
201 | * @function
202 | * @param {node} element - Element to be rendered
203 | */
204 |
205 |
206 | Sticky.prototype.renderElement = function renderElement(element) {
207 | var _this2 = this;
208 |
209 | // create container for variables needed in future
210 | element.sticky = {};
211 |
212 | // set default variables
213 | element.sticky.active = false;
214 |
215 | element.sticky.marginTop = parseInt(element.getAttribute('data-margin-top')) || this.options.marginTop;
216 | element.sticky.stickyFor = parseInt(element.getAttribute('data-sticky-for')) || this.options.stickyFor;
217 | element.sticky.stickyClass = element.getAttribute('data-sticky-class') || this.options.stickyClass;
218 | element.sticky.wrap = element.hasAttribute('data-sticky-wrap') ? true : this.options.wrap;
219 | // @todo attribute for stickyContainer
220 | // element.sticky.stickyContainer = element.getAttribute('data-sticky-container') || this.options.stickyContainer;
221 | element.sticky.stickyContainer = this.options.stickyContainer;
222 |
223 | element.sticky.container = this.getStickyContainer(element);
224 | element.sticky.container.rect = this.getRectangle(element.sticky.container);
225 |
226 | element.sticky.rect = this.getRectangle(element);
227 |
228 | // fix when element is image that has not yet loaded and width, height = 0
229 | if (element.tagName.toLowerCase() === 'img') {
230 | element.onload = function () {
231 | return element.sticky.rect = _this2.getRectangle(element);
232 | };
233 | }
234 |
235 | if (element.sticky.wrap) {
236 | this.wrapElement(element);
237 | }
238 |
239 | // activate rendered element
240 | this.activate(element);
241 | };
242 |
243 | /**
244 | * Wraps element into placeholder element
245 | * @function
246 | * @param {node} element - Element to be wrapped
247 | */
248 |
249 |
250 | Sticky.prototype.wrapElement = function wrapElement(element) {
251 | element.insertAdjacentHTML('beforebegin', '');
252 | element.previousSibling.appendChild(element);
253 | };
254 |
255 | /**
256 | * Function that activates element when specified conditions are met and then initalise events
257 | * @function
258 | * @param {node} element - Element to be activated
259 | */
260 |
261 |
262 | Sticky.prototype.activate = function activate(element) {
263 | if (element.sticky.rect.top + element.sticky.rect.height < element.sticky.container.rect.top + element.sticky.container.rect.height && element.sticky.stickyFor < this.vp.width && !element.sticky.active) {
264 | element.sticky.active = true;
265 | }
266 |
267 | if (this.elements.indexOf(element) < 0) {
268 | this.elements.push(element);
269 | }
270 |
271 | if (!element.sticky.resizeEvent) {
272 | this.initResizeEvents(element);
273 | element.sticky.resizeEvent = true;
274 | }
275 |
276 | if (!element.sticky.scrollEvent) {
277 | this.initScrollEvents(element);
278 | element.sticky.scrollEvent = true;
279 | }
280 |
281 | this.setPosition(element);
282 | };
283 |
284 | /**
285 | * Function which is adding onResizeEvents to window listener and assigns function to element as resizeListener
286 | * @function
287 | * @param {node} element - Element for which resize events are initialised
288 | */
289 |
290 |
291 | Sticky.prototype.initResizeEvents = function initResizeEvents(element) {
292 | var _this3 = this;
293 |
294 | element.sticky.resizeListener = function () {
295 | return _this3.onResizeEvents(element);
296 | };
297 | window.addEventListener('resize', element.sticky.resizeListener);
298 | };
299 |
300 | /**
301 | * Removes element listener from resize event
302 | * @function
303 | * @param {node} element - Element from which listener is deleted
304 | */
305 |
306 |
307 | Sticky.prototype.destroyResizeEvents = function destroyResizeEvents(element) {
308 | window.removeEventListener('resize', element.sticky.resizeListener);
309 | };
310 |
311 | /**
312 | * Function which is fired when user resize window. It checks if element should be activated or deactivated and then run setPosition function
313 | * @function
314 | * @param {node} element - Element for which event function is fired
315 | */
316 |
317 |
318 | Sticky.prototype.onResizeEvents = function onResizeEvents(element) {
319 | this.vp = this.getViewportSize();
320 |
321 | element.sticky.rect = this.getRectangle(element);
322 | element.sticky.container.rect = this.getRectangle(element.sticky.container);
323 |
324 | if (element.sticky.rect.top + element.sticky.rect.height < element.sticky.container.rect.top + element.sticky.container.rect.height && element.sticky.stickyFor < this.vp.width && !element.sticky.active) {
325 | element.sticky.active = true;
326 | } else if (element.sticky.rect.top + element.sticky.rect.height >= element.sticky.container.rect.top + element.sticky.container.rect.height || element.sticky.stickyFor >= this.vp.width && element.sticky.active) {
327 | element.sticky.active = false;
328 | }
329 |
330 | this.setPosition(element);
331 | };
332 |
333 | /**
334 | * Function which is adding onScrollEvents to window listener and assigns function to element as scrollListener
335 | * @function
336 | * @param {node} element - Element for which scroll events are initialised
337 | */
338 |
339 |
340 | Sticky.prototype.initScrollEvents = function initScrollEvents(element) {
341 | var _this4 = this;
342 |
343 | element.sticky.scrollListener = function () {
344 | return _this4.onScrollEvents(element);
345 | };
346 | window.addEventListener('scroll', element.sticky.scrollListener);
347 | };
348 |
349 | /**
350 | * Removes element listener from scroll event
351 | * @function
352 | * @param {node} element - Element from which listener is deleted
353 | */
354 |
355 |
356 | Sticky.prototype.destroyScrollEvents = function destroyScrollEvents(element) {
357 | window.removeEventListener('scroll', element.sticky.scrollListener);
358 | };
359 |
360 | /**
361 | * Function which is fired when user scroll window. If element is active, function is invoking setPosition function
362 | * @function
363 | * @param {node} element - Element for which event function is fired
364 | */
365 |
366 |
367 | Sticky.prototype.onScrollEvents = function onScrollEvents(element) {
368 | if (element.sticky.active) {
369 | this.setPosition(element);
370 | }
371 | };
372 |
373 | /**
374 | * Main function for the library. Here are some condition calculations and css appending for sticky element when user scroll window
375 | * @function
376 | * @param {node} element - Element that will be positioned if it's active
377 | */
378 |
379 |
380 | Sticky.prototype.setPosition = function setPosition(element) {
381 | this.css(element, { position: '', width: '', top: '', left: '' });
382 |
383 | if (this.vp.height < element.sticky.rect.height || !element.sticky.active) {
384 | return;
385 | }
386 |
387 | if (!element.sticky.rect.width) {
388 | element.sticky.rect = this.getRectangle(element);
389 | }
390 |
391 | if (element.sticky.wrap) {
392 | this.css(element.parentNode, {
393 | display: 'block',
394 | width: element.sticky.rect.width + 'px',
395 | height: element.sticky.rect.height + 'px'
396 | });
397 | }
398 |
399 | if (element.sticky.rect.top === 0 && element.sticky.container === this.body) {
400 | this.css(element, {
401 | position: 'fixed',
402 | top: element.sticky.rect.top + 'px',
403 | left: element.sticky.rect.left + 'px',
404 | width: element.sticky.rect.width + 'px'
405 | });
406 | } else if (this.scrollTop > element.sticky.rect.top - element.sticky.marginTop) {
407 | this.css(element, {
408 | position: 'fixed',
409 | width: element.sticky.rect.width + 'px',
410 | left: element.sticky.rect.left + 'px'
411 | });
412 |
413 | if (this.scrollTop + element.sticky.rect.height + element.sticky.marginTop > element.sticky.container.rect.top + element.sticky.container.offsetHeight) {
414 |
415 | if (element.sticky.stickyClass) {
416 | element.classList.remove(element.sticky.stickyClass);
417 | }
418 |
419 | this.css(element, {
420 | top: element.sticky.container.rect.top + element.sticky.container.offsetHeight - (this.scrollTop + element.sticky.rect.height) + 'px' });
421 | } else {
422 | if (element.sticky.stickyClass) {
423 | element.classList.add(element.sticky.stickyClass);
424 | }
425 |
426 | this.css(element, { top: element.sticky.marginTop + 'px' });
427 | }
428 | } else {
429 | if (element.sticky.stickyClass) {
430 | element.classList.remove(element.sticky.stickyClass);
431 | }
432 |
433 | this.css(element, { position: '', width: '', top: '', left: '' });
434 |
435 | if (element.sticky.wrap) {
436 | this.css(element.parentNode, { display: '', width: '', height: '' });
437 | }
438 | }
439 | };
440 |
441 | /**
442 | * Function that updates element sticky rectangle (with sticky container), then activate or deactivate element, then update position if it's active
443 | * @function
444 | */
445 |
446 |
447 | Sticky.prototype.update = function update() {
448 | var _this5 = this;
449 |
450 | this.forEach(this.elements, function (element) {
451 | element.sticky.rect = _this5.getRectangle(element);
452 | element.sticky.container.rect = _this5.getRectangle(element.sticky.container);
453 |
454 | _this5.activate(element);
455 | _this5.setPosition(element);
456 | });
457 | };
458 |
459 | /**
460 | * Destroys sticky element, remove listeners
461 | * @function
462 | */
463 |
464 |
465 | Sticky.prototype.destroy = function destroy() {
466 | var _this6 = this;
467 |
468 | this.forEach(this.elements, function (element) {
469 | _this6.destroyResizeEvents(element);
470 | _this6.destroyScrollEvents(element);
471 | delete element.sticky;
472 | });
473 | };
474 |
475 | /**
476 | * Function that returns container element in which sticky element is stuck (if is not specified, then it's stuck to body)
477 | * @function
478 | * @param {node} element - Element which sticky container are looked for
479 | * @return {node} element - Sticky container
480 | */
481 |
482 |
483 | Sticky.prototype.getStickyContainer = function getStickyContainer(element) {
484 | var container = element.parentNode;
485 |
486 | while (!container.hasAttribute('data-sticky-container') && !container.parentNode.querySelector(element.sticky.stickyContainer) && container !== this.body) {
487 | container = container.parentNode;
488 | }
489 |
490 | return container;
491 | };
492 |
493 | /**
494 | * Function that returns element rectangle & position (width, height, top, left)
495 | * @function
496 | * @param {node} element - Element which position & rectangle are returned
497 | * @return {object}
498 | */
499 |
500 |
501 | Sticky.prototype.getRectangle = function getRectangle(element) {
502 | this.css(element, { position: '', width: '', top: '', left: '' });
503 |
504 | var width = Math.max(element.offsetWidth, element.clientWidth, element.scrollWidth);
505 | var height = Math.max(element.offsetHeight, element.clientHeight, element.scrollHeight);
506 |
507 | var top = 0;
508 | var left = 0;
509 |
510 | do {
511 | top += element.offsetTop || 0;
512 | left += element.offsetLeft || 0;
513 | element = element.offsetParent;
514 | } while (element);
515 |
516 | return { top: top, left: left, width: width, height: height };
517 | };
518 |
519 | /**
520 | * Function that returns viewport dimensions
521 | * @function
522 | * @return {object}
523 | */
524 |
525 |
526 | Sticky.prototype.getViewportSize = function getViewportSize() {
527 | return {
528 | width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
529 | height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
530 | };
531 | };
532 |
533 | /**
534 | * Function that updates window scroll position
535 | * @function
536 | * @return {number}
537 | */
538 |
539 |
540 | Sticky.prototype.updateScrollTopPosition = function updateScrollTopPosition() {
541 | this.scrollTop = (window.pageYOffset || document.scrollTop) - (document.clientTop || 0) || 0;
542 | };
543 |
544 | /**
545 | * Helper function for loops
546 | * @helper
547 | * @param {array}
548 | * @param {function} callback - Callback function (no need for explanation)
549 | */
550 |
551 |
552 | Sticky.prototype.forEach = function forEach(array, callback) {
553 | for (var i = 0, len = array.length; i < len; i++) {
554 | callback(array[i]);
555 | }
556 | };
557 |
558 | /**
559 | * Helper function to add/remove css properties for specified element.
560 | * @helper
561 | * @param {node} element - DOM element
562 | * @param {object} properties - CSS properties that will be added/removed from specified element
563 | */
564 |
565 |
566 | Sticky.prototype.css = function css(element, properties) {
567 | for (var property in properties) {
568 | if (properties.hasOwnProperty(property)) {
569 | element.style[property] = properties[property];
570 | }
571 | }
572 | };
573 |
574 | return Sticky;
575 | }();
576 |
577 | /**
578 | * Export function that supports AMD, CommonJS and Plain Browser.
579 | */
580 |
581 |
582 | (function (root, factory) {
583 | if (true) {
584 | module.exports = factory;
585 | } else if (typeof define === 'function' && define.amd) {
586 | define([], factory);
587 | } else {
588 | root.Sticky = factory;
589 | }
590 | })(this, Sticky);
591 |
592 | /***/ })
593 | /******/ ])
594 | });
595 | ;
596 | //# sourceMappingURL=vue-sticky-js.js.map
--------------------------------------------------------------------------------