├── diagrams ├── inview.png └── inview.xml ├── .travis.yml ├── .editorconfig ├── CHANGELOG.md ├── demo ├── demo.css ├── index.html └── demo.js ├── package.json ├── LICENSE ├── .gitignore ├── README.md └── inview.js /diagrams/inview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miguelmota/inview/HEAD/diagrams/inview.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "10" 5 | 6 | before_script: 7 | - npm install 8 | 9 | script: 10 | - npm test 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | # use line feed 8 | end_of_line = lf 9 | 10 | # ensure file ends with a newline when saving 11 | insert_final_newline = true 12 | 13 | # soft tabs 14 | indent_style = space 15 | 16 | # number of columns used for each indentation level 17 | indent_size = 2 18 | 19 | # remove any whitespace characters preceding newline characters 20 | trim_trailing_whitespace = true 21 | 22 | # character set 23 | charset = utf-8 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## [Unreleased][unreleased] 6 | ### Fixed 7 | - Fix Markdown links to tag comparison URL with footnote-style links. 8 | 9 | ## [0.0.2] - 2015-02-15 10 | ### Changed 11 | - `Inview` global to `inView` 12 | 13 | ## [0.0.3] - 2015-02-22 14 | ### Changed 15 | - Fixed exports typo 16 | 17 | ## [0.0.4] - 2015-05-01 18 | ### Changed 19 | - Reverted `inview` global to `InView` 20 | - Added `data.elementOffsetTopInViewHeight` 21 | 22 | ## [0.0.5] - 2015-05-01 23 | ### Changed 24 | - Fixed inverted height values 25 | -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | .item { 7 | border: 1px solid #ff0000; 8 | padding: 40px; 9 | font-family: monospace; 10 | font-size: 12px; 11 | } 12 | 13 | .item.inview { 14 | border: 1px solid #00ff00 15 | } 16 | 17 | .space-filler { 18 | height: 2000px; 19 | } 20 | 21 | .status { 22 | position: fixed; 23 | background: #eee; 24 | color: #000; 25 | font: monospace; 26 | font-size: 14px; 27 | padding: 5px; 28 | width: 100%; 29 | } 30 | 31 | .output { 32 | position: fixed; 33 | color: #000; 34 | font: monospace; 35 | font-size: 10px; 36 | top: 60px; 37 | left: 60px; 38 | } 39 | 40 | .success { 41 | color: #00ff00; 42 | } 43 | 44 | .fail { 45 | color: #ff0000; 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inview", 3 | "version": "0.0.15", 4 | "description": "Detect when element scrolled to view ", 5 | "main": "inview.js", 6 | "devDependencies": { 7 | "jsdoc": "^3.3.0-alpha9", 8 | "jsdoc-oblivion": "^0.0.3", 9 | "standard": "^12.0.1" 10 | }, 11 | "scripts": { 12 | "test": "echo 'TODO'", 13 | "lint": "standard --fix inview.js", 14 | "docs": "jsdoc -c config/conf.json -d docs/ -t node_modules/jsdoc-oblivion/template/ inview.js README.md" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/miguelmota/inview.git" 19 | }, 20 | "keywords": [ 21 | "inview" 22 | ], 23 | "author": { 24 | "name": "Miguel Mota", 25 | "email": "hello@miguelmota.com", 26 | "url": "https://miguelmota.com" 27 | }, 28 | "license": { 29 | "type": "MIT", 30 | "url": "https://github.com/miguelmota/inview/blob/master/LICENSE" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/miguelmota/inview/issues" 34 | }, 35 | "homepage": "https://github.com/miguelmota/inview" 36 | } 37 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT license
2 |
3 | Copyright (C) 2014 Miguel Mota
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 | of the Software, and to permit persons to whom the Software is furnished to do
10 | 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 |
--------------------------------------------------------------------------------
/demo/demo.js:
--------------------------------------------------------------------------------
1 | ;(function () {
2 | var el = document.querySelector('.item')
3 | var status = document.querySelector('.status')
4 | var output = document.querySelector('.output')
5 |
6 | function text (el, content) {
7 | el.innerText = content
8 | }
9 |
10 | function html (el, content) {
11 | el.innerHTML = content
12 | }
13 |
14 | function statusHtml (content) {
15 | html(status, content)
16 | }
17 |
18 | function log (data) {
19 | output.innerHTML = JSON.stringify(data, null, 2)
20 | }
21 |
22 | var inView = InView(el, function (isInView, data) {
23 | log(data)
24 | el.textContent = data.elementOffsetTopInViewHeight
25 | if (isInView) {
26 | if (data.elementOffsetTopInViewHeight < data.inViewHeight / 2) {
27 | statusHtml('status: in view (top half)')
28 | } else {
29 | statusHtml('status: in view (bottom half)')
30 | }
31 | } else {
32 | if (data.windowScrollTop - (data.elementOffsetTop - data.inViewHeight) > data.inViewHeight) {
33 | statusHtml('status: not in view (scroll up)')
34 | } else {
35 | statusHtml('status: not in view (scroll down)')
36 | }
37 | }
38 | })
39 | })();
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | //this will affect all the git repos
2 | git config --global core.excludesfile ~/.gitignore
3 |
4 |
5 | //update files since .ignore won't if already tracked
6 | git rm --cached
4 |
92 |
93 | ## License
94 |
95 | [MIT](LICENSE)
96 |
--------------------------------------------------------------------------------
/inview.js:
--------------------------------------------------------------------------------
1 | ;(function (root) {
2 | function throttle (fn, threshhold, scope) {
3 | threshhold || (threshhold = 100)
4 | var last,
5 | deferTimer
6 |
7 | return function () {
8 | var context = scope || this
9 |
10 | var now = +(new Date())
11 |
12 | var args = arguments
13 | if (last && now < last + threshhold) {
14 | clearTimeout(deferTimer)
15 | deferTimer = setTimeout(function () {
16 | last = now
17 | fn.apply(context, args)
18 | }, threshhold)
19 | } else {
20 | last = now
21 | fn.apply(context, args)
22 | }
23 | }
24 | }
25 |
26 | function hasClass (el, name) {
27 | return new RegExp(' ' + name + ' ').test(' ' + el.className + ' ')
28 | }
29 |
30 | function addClass (el, name) {
31 | if (!hasClass(el, name)) {
32 | el.className += ' ' + name
33 | }
34 | return el
35 | }
36 |
37 | function removeClass (el, name) {
38 | var newClass = ' ' + el.className.replace(/[\t\r\n]/g, ' ') + ' '
39 | if (hasClass(el, name)) {
40 | while (newClass.indexOf(' ' + name + ' ') >= 0) {
41 | newClass = newClass.replace(' ' + name + ' ', ' ')
42 | }
43 | el.className = newClass.replace(/^\s+|\s+$/g, '')
44 | }
45 | return el
46 | }
47 |
48 | function removeEvent (el, name, fn) {
49 | if (el.removeEventListener) {
50 | return el.removeEventListener(name, fn)
51 | } else if (el.detachEvent) {
52 | return el.detachEvent('on' + name, fn)
53 | }
54 | }
55 |
56 | function addEvent (el, name, fn) {
57 | if (el.addEventListener) {
58 | return el.addEventListener(name, fn, false)
59 | } else if (el.attachEvent) {
60 | return el.attachEvent('on' + name, fn)
61 | }
62 | }
63 |
64 | function getScrollTop () {
65 | if (typeof window.pageYOffset !== 'undefined') {
66 | return window.pageYOffset
67 | } else {
68 | var b = document.body
69 | var d = document.documentElement
70 | d = d.clientHeight ? d : b
71 | return d.scrollTop
72 | }
73 | }
74 |
75 | function isInView (obj) {
76 | var winTop = getScrollTop()
77 |
78 | var winBottom = winTop + window.innerHeight
79 |
80 | var objTop = obj.getBoundingClientRect().top + document.documentElement.scrollTop
81 |
82 | var objBottom = objTop + obj.offsetHeight
83 |
84 | var offset = 0
85 |
86 | if ((objTop <= winBottom + offset) && (objBottom >= winTop)) {
87 | return true
88 | }
89 |
90 | return false
91 | }
92 |
93 | /**
94 | * @desc Create an InView instance.
95 | *
96 | * @class
97 | * @func InView
98 | * @param {HTMLElement} element - element to detect when scrolled to view
99 | * @param {scrollCallback} scrollCallback - callback function fired on scroll event
100 | * @return {HTMLElement} - element
101 | *
102 | * @example
103 | * var el = document.querySelector('.item');
104 | *
105 | * var InView = InView(el, function(isInView, data) {
106 | * if (isInView) {
107 | * console.log('in view');
108 | * } else {
109 | * if (data.windowScrollTop - (data.elementOffsetTop - data.inViewHeight) > data.inViewHeight) {
110 | * console.log('not in view (scroll up)');
111 | * } else {
112 | * console.log('not in view (scroll down)');
113 | * }
114 | * }
115 | * });
116 | */
117 | function InView (el, callback) {
118 | var _this = this
119 | if (!(_this instanceof InView)) {
120 | return new InView(el, callback)
121 | }
122 |
123 | _this.el = el
124 | _this.callback = callback.bind(_this)
125 | _this.destroy = function () {}
126 |
127 | if (!el) {
128 | return _this
129 | }
130 |
131 | var isDestroyed = false
132 |
133 | var check = function check (e) {
134 | if (isDestroyed) return false
135 |
136 | var params = {
137 | windowScrollTop: getScrollTop(),
138 | elementOffsetTop: _this.el.offsetTop,
139 | inViewHeight: window.innerHeight,
140 | elementOffsetTopInViewHeight: window.innerHeight - (getScrollTop() - (_this.el.offsetTop - window.innerHeight))
141 | }
142 | if (isInView(_this.el)) {
143 | addClass(_this.el, 'inview')
144 | _this.callback.call(_this, true, params)
145 | } else {
146 | removeClass(_this.el, 'inview')
147 | _this.callback.call(_this, false, params)
148 | }
149 | }
150 |
151 | var throttledCheck = throttle(check, 100)
152 |
153 | addEvent(window, 'scroll', throttledCheck)
154 |
155 | _this.destroy = function () {
156 | isDestroyed = true
157 | removeEvent(window, 'scroll', throttledCheck)
158 | }
159 |
160 | throttledCheck()
161 |
162 | return _this
163 | }
164 |
165 | /**
166 | * @desc InView callback
167 | *
168 | * @callback scrollCallback
169 | * @param {boolean} isInView - is in view
170 | * @param {object} data - scroll data
171 | * @param {number} data.windowScrollTop - scrolled amount
172 | * @param {number} data.elementOffsetTop - element top offset
173 | * @param {number} data.inViewHeight - height of visible area
174 | * @param {number} data.elementOffsetTopInViewHeight - element top offset relative to height of visible area
175 | */
176 |
177 | if (typeof exports !== 'undefined') {
178 | if (typeof module !== 'undefined' && module.exports) {
179 | exports = module.exports = InView
180 | }
181 | exports.InView = InView
182 | } else {
183 | root.InView = InView
184 | }
185 | })(window);
186 |
--------------------------------------------------------------------------------