UNPKG

17 kBMarkdownView Raw
1[![CircleCI Status](https://img.shields.io/circleci/project/github/stipsan/scroll-into-view-if-needed.svg?style=flat-square)](https://circleci.com/gh/stipsan/scroll-into-view-if-needed)
2[![npm stat](https://img.shields.io/npm/dm/scroll-into-view-if-needed.svg?style=flat-square)](https://npm-stat.com/charts.html?package=scroll-into-view-if-needed)
3[![npm version](https://img.shields.io/npm/v/scroll-into-view-if-needed.svg?style=flat-square)](https://www.npmjs.com/package/scroll-into-view-if-needed)
4[![gzip size][gzip-badge]][unpkg-dist]
5[![size][size-badge]][unpkg-dist]
6[![module formats: umd, cjs, and es][module-formats-badge]][unpkg-dist]
7[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release)
8[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?style=flat-square&badge_key=ejZ6OUtTaS9rZFFOYzlkeHlwTzMwSWxpR0FzWFcwOW5TS3ROTmlSdXMrVT0tLVhrVk9La2lCb1o4Y05mcmNXVnAvdkE9PQ==--d17668b8aba5091e4ef3a58927b8209e50b0a788)](https://www.browserstack.com/automate/public-build/ejZ6OUtTaS9rZFFOYzlkeHlwTzMwSWxpR0FzWFcwOW5TS3ROTmlSdXMrVT0tLVhrVk9La2lCb1o4Y05mcmNXVnAvdkE9PQ==--d17668b8aba5091e4ef3a58927b8209e50b0a788)
9
10![scroll-into-view-if-needed](https://user-images.githubusercontent.com/81981/39476436-34a4f3ae-4d5c-11e8-9d1c-7fa2fa6288a0.png)
11
12This used to be a [ponyfill](https://ponyfill.com) for
13`Element.scrollIntoViewIfNeeded`. Since then the CSS working group have decided to implement its features in `Element.scrollIntoView` as the option `scrollMode: "if-needed"`. Thus this library got rewritten to implement that spec instead of the soon to be deprecated one.
14
15## [Demo](https://scroll-into-view-if-needed.netlify.com)
16
17## Install
18
19```bash
20yarn add scroll-into-view-if-needed
21```
22
23The UMD build is also available on [unpkg](https://unpkg.com/scroll-into-view-if-needed/umd/):
24
25```html
26<script src="https://unpkg.com/scroll-into-view-if-needed/umd/scroll-into-view-if-needed.min.js"></script>
27```
28
29You can find the library on `window.scrollIntoView`.
30
31## Usage
32
33```js
34// es6 import
35import scrollIntoView from 'scroll-into-view-if-needed'
36// or es5
37const scrollIntoView = require('scroll-into-view-if-needed')
38
39const node = document.getElementById('hero')
40
41// similar behavior as Element.scrollIntoView({block: "nearest", inline: "nearest"})
42// only that it is a no-op if `node` is already visible
43// see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
44// same behavior as Element.scrollIntoViewIfNeeded()
45// see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded
46scrollIntoView(node, {
47 scrollMode: 'if-needed',
48 block: 'nearest',
49 inline: 'nearest',
50})
51
52// same behavior as Element.scrollIntoViewIfNeeded(true) without the "IfNeeded" behavior
53// see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded
54scrollIntoView(node, { block: 'center', inline: 'center' })
55// scrollMode is "always" by default
56
57// smooth scroll if the browser supports it and if the element isn't visible
58scrollIntoView(node, { behavior: 'smooth', scrollMode: 'if-needed' })
59```
60
61### Ponyfill smooth scrolling
62
63What does ponyfilling smooth scrolling mean, and why is it implemented in [`smooth-scroll-into-view-if-needed`](https://github.com/stipsan/smooth-scroll-into-view-if-needed) instead?
64The answer is bundlesize. If this package adds smooth scrolling to browsers that's missing it then the overall bundlesize increases regardless of wether you use this feature or not.
65
66Put it this way:
67
68```js
69import scrollIntoView from 'scroll-into-view-if-needed'
70// Even if all you do is this
71scrollIntoView(node, { scrollMode: 'if-needed' })
72// You would end up with the same bundlesize as people who need
73// smooth scrolling to work in browsers that don't support it natively
74scrollIntoView(node, { behavior: 'smooth', scrollMode: 'if-needed' })
75```
76
77That's why only native smooth scrolling is supported out of the box. There are two common ways you can smooth scroll browsers that don't support it natively. Below is all three, which one is best for you depends on what is the most important to your use case:: load time, consistency or quality.
78
79##### Load time
80
81In many scenarios smooth scrolling can be used as a progressive enhancement. If the user is on a browser that don't implement smooth scrolling it'll simply scroll instantly and your bundlesize is only as large as it has to be.
82
83```js
84import scrollIntoView from 'scroll-into-view-if-needed'
85
86scrollIntoView(node, { behavior: 'smooth' })
87```
88
89##### Consistency
90
91If a consistent smooth scrolling experience is a priority and you really don't want any surprises between different browsers and enviroments. In other words don't want to be affected by how a vendor might implement native smooth scrolling, then [`smooth-scroll-into-view-if-needed`](https://github.com/stipsan/smooth-scroll-into-view-if-needed) is your best option. It ensures the same smooth scrolling experience for every browser.
92
93```js
94import smoothScrollIntoView from 'smooth-scroll-into-view-if-needed'
95
96smoothScrollIntoView(node, { behavior: 'smooth' })
97```
98
99##### Quality
100
101If you want to use native smooth scrolling when it's available, and fallback to the smooth scrolling ponyfill:
102
103```js
104import scrollIntoView from 'scroll-into-view-if-needed'
105import smoothScrollIntoView from 'smooth-scroll-into-view-if-needed'
106
107const scrollIntoViewSmoothly =
108 'scrollBehavior' in document.documentElement.style
109 ? scrollIntoView
110 : smoothScrollIntoView
111
112scrollIntoViewSmoothly(node, { behavior: 'smooth' })
113```
114
115## API
116
117### scrollIntoView(target, [options])
118
119> New API introduced in `v1.3.0`
120
121### options
122
123Type: `Object`
124
125#### behavior
126
127Type: `'auto' | 'smooth' | Function`<br> Default: `'auto'`
128
129> Introduced in `v2.1.0`
130
131##### `'auto'`
132
133The auto option unlocks a few interesting opportunities.
134The browser will decide based on user preferences wether it should smooth scroll or not.
135On top of that you can control/override scrolling behavior through the [`scroll-behavior`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior) CSS property.
136
137Some people get [motion sick from animations](https://css-tricks.com/smooth-scrolling-accessibility/#article-header-id-5). You can use CSS to turn off smooth scrolling in those cases to avoid making them dizzy:
138
139```css
140html,
141.scroll-container {
142 overflow: scroll;
143}
144
145html,
146.scroll-container {
147 scroll-behavior: smooth;
148}
149@media (prefers-reduced-motion) {
150 html,
151 .scroll-container {
152 scroll-behavior: auto;
153 }
154}
155```
156
157##### `'smooth'`
158
159Using `behavior: 'smooth'` is the easiest way to smooth scroll an element as it does not require any CSS, just a browser that implements it. [More information.](#ponyfill-smooth-scrolling)
160
161##### `Function`
162
163When given a function then this library will only calculate what should be scrolled and leave it up to you to perform the actual scrolling.
164
165The callback is given an array over actions. Each action contain a reference to an element that should be scrolled, with its top and left scrolling coordinates.
166What you return is passed through, allowing you to implement a Promise interface if you want to (check [`smooth-scroll-into-view-if-needed`](https://github.com/stipsan/smooth-scroll-into-view-if-needed) to see an example of that).
167
168```js
169import scrollIntoView from 'scroll-into-view-if-needed'
170const node = document.getElementById('hero')
171
172scrollIntoView(node, {
173 // Your scroll actions will always be an array, even if there is nothing to scroll
174 behavior: actions =>
175 // list is sorted from innermost (closest parent to your target) to outermost (often the document.body or viewport)
176 actions.forEach(({ el, top, left }) => {
177 // implement the scroll anyway you want
178 el.scrollTop = top
179 el.scrollLeft = left
180
181 // If you need the relative scroll coordinates, for things like window.scrollBy style logic or whatever, just do the math
182 const offsetTop = el.scrollTop - top
183 const offsetLeft = el.scrollLeft - left
184 }),
185 // all the other options (scrollMode, block, inline) still work, so you don't need to reimplement them (unless you really really want to)
186})
187```
188
189Check the demo to see an [example with popmotion and a spring transition](https://scroll-into-view-if-needed.netlify.com/#custom-transition).
190
191> If you only need the custom behavior you might be better off by using the compute library directly: https://github.com/stipsan/compute-scroll-into-view
192
193#### [block](https://scroll-into-view-if-needed.netlify.com/#scroll-alignment)
194
195Type: `'start' | 'center' | 'end' | 'nearest'`<br> Default: `'center'`
196
197> Introduced in `v2.1.0`
198
199[More info.](https://github.com/stipsan/compute-scroll-into-view#block)
200
201#### [inline](https://scroll-into-view-if-needed.netlify.com/#scroll-alignment)
202
203Type: `'start' | 'center' | 'end' | 'nearest'`<br> Default: `'nearest'`
204
205> Introduced in `v2.1.0`
206
207[More info.](https://github.com/stipsan/compute-scroll-into-view#inline)
208
209#### [scrollMode](https://scroll-into-view-if-needed.netlify.com/#scrolling-if-needed)
210
211Type: `'always' | 'if-needed'`<br> Default: `'always'`
212
213> Introduced in `v2.1.0`
214
215[More info.](https://github.com/stipsan/compute-scroll-into-view#scrollmode)
216
217#### [boundary](https://scroll-into-view-if-needed.netlify.com/#limit-propagation)
218
219Type: `Element | Function`
220
221> `Function` introduced in `v2.1.0`, `Element` introduced in `v1.1.0`
222
223[More info.](https://github.com/stipsan/compute-scroll-into-view#boundary)
224
225#### skipOverflowHiddenElements
226
227Type: `Boolean`<br> Default: `false`
228
229> Introduced in `v2.2.0`
230
231[More info.](https://github.com/stipsan/compute-scroll-into-view#skipoverflowhiddenelements)
232
233# TypeScript support
234
235When the library itself is built on TypeScript there's no excuse for not publishing great library definitions!
236
237This goes beyond just checking if you misspelled `behavior: 'smoooth'` to the return type of a custom behavior:
238
239```typescript
240const scrolling = scrollIntoView(document.body, {
241 behavior: actions => {
242 return new Promise(
243 ...
244 )
245 },
246})
247// TypeScript understands that scrolling is a Promise, you can safely await on it
248scrolling.then(() => console.log('done scrolling'))
249```
250
251You can optionally use a generic to ensure that `options.behavior` is the expected type.
252It can be useful if the custom behavior is implemented in another module:
253
254```typescript
255const customBehavior = actions => {
256 return new Promise(
257 ...
258 )
259 }
260
261const scrolling = scrollIntoView<Promise<any>>(document.body, {
262 behavior: customBehavior
263})
264// throws if customBehavior does not return a promise
265```
266
267The options are available for you if you are wrapping this libary in another abstraction (like a React component):
268
269```typescript
270import scrollIntoView, { Options } from 'scroll-into-view-if-needed'
271
272interface CustomOptions extends Options {
273 useBoundary?: boolean
274}
275
276function scrollToTarget(selector, options: Options = {}) {
277 const { useBoundary = false, ...scrollOptions } = options
278 return scrollIntoView(document.querySelector(selector), scrollOptions)
279}
280```
281
282# Breaking API changes from v1
283
284Since v1 ponyfilled Element.scrollIntoViewIfNeeded, while v2 ponyfills Element.scrollIntoView, there are breaking changes from the differences in their APIs.
285
286The biggest difference is that the new behavior follows the spec, so the "if-needed" behavior is **not enabled by default:**
287
288##### v1
289
290```js
291import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
292
293// Only scrolls into view if needed, and to the nearest edge
294scrollIntoViewIfNeeded(target)
295```
296
297##### v2
298
299```js
300import scrollIntoView from 'scroll-into-view-if-needed'
301
302// Must provide these options to behave the same way as v1 default
303scrollIntoView(target, { block: 'nearest', scrollMode: 'if-needed' })
304```
305
306#### centerIfNeeded
307
308The old `Element.scrollIntoView` api only had two settings, align to top or bottom. [`Element.scrollIntoViewIfNeeded`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded) had two more, align to the center or nearest edge.
309The `Element.scrollIntoView` spec now supports these two modes as `block: 'center'` and `block: 'nearest'`.
310Breaking changes sucks, but on the plus side your code is now more portable and will make this library easier to delete from your codebase on the glorious day browser support is good enough.
311
312##### v1
313
314```js
315import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
316
317// v1.3.x and later
318scrollIntoViewIfNeeded(target, { centerIfNeeded: true })
319scrollIntoViewIfNeeded(target, { centerIfNeeded: false })
320// v1.2.x and earlier
321scrollIntoViewIfNeeded(target, true)
322scrollIntoViewIfNeeded(target, false)
323```
324
325##### v2
326
327```js
328import scrollIntoView from 'scroll-into-view-if-needed'
329
330scrollIntoView(target, { block: 'center' })
331scrollIntoView(target, { block: 'nearest' })
332```
333
334#### duration
335
336[More information.](#ponyfill-smooth-scrolling)
337
338##### v1
339
340```js
341import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
342
343scrollIntoViewIfNeeded(target, { duration: 300 })
344```
345
346##### v2
347
348```js
349import scrollIntoView from 'scroll-into-view-if-needed'
350// or
351import scrollIntoView from 'smooth-scroll-into-view-if-needed'
352
353scrollIntoView(target, { behavior: 'smooth' })
354```
355
356#### easing
357
358This feature is removed, but you can achieve the same thing by implementing [`behavior: Function`](#function).
359
360#### handleScroll
361
362This is replaced with [`behavior: Function`](#function) with one key difference. Instead of firing once per element that should be scrolled, the new API only fire once and instead give you an array so you can much easier batch and scroll multiple elements at the same time. Or sync scrolling with another element if that's the kind of stuff you're into, I don't judge.
363
364```diff
365-import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
366+import scrollIntoView from 'scroll-into-view-if-needed'
367
368-scrollIntoViewIfNeeded(node, {handleScroll: (el, {scrollTop, scrollLeft}) => {
369- el.scrollTop = scrollTop
370- el.scrollLeft = scrollLeft
371-}})
372+scrollIntoView(node, {behavior: actions.forEach(({el, top, left}) => {
373+ el.scrollTop = top
374+ el.scrollLeft = left
375+})})
376```
377
378#### offset
379
380This was always a buggy feature and warned against using in v1 as it might get dropped.
381It's much safer to use CSS wrapper elements for this kind of thing.
382
383### scrollIntoViewIfNeeded(target, [centerIfNeeded], [animateOptions], [finalElement], [offsetOptions])
384
385This API signature were warned to be dropped in `v2.0.0`, and it was.
386
387# Related packages
388
389- [compute-scroll-into-view](https://www.npmjs.com/package/compute-scroll-into-view) - the engine used by this library.
390- [smooth-scroll-into-view-if-needed](https://www.npmjs.com/package/smooth-scroll-into-view-if-needed) – ponyfills smooth scrolling.
391- [react-scroll-into-view-if-needed](https://www.npmjs.com/package/react-scroll-into-view-if-needed) – A thin wrapper to scroll your component into view.
392- [scroll-polyfill](https://www.npmjs.com/package/scroll-polyfill) – polyfills smooth scrolling.
393- [Don't be shy, add yours!](https://github.com/stipsan/scroll-into-view-if-needed/edit/master/README.md)
394
395# Who's using this
396
397- [zeit.co/docs](https://github.com/zeit/docs) – Documentation of ZEIT Now and other services.
398- [Selenium IDE](https://github.com/SeleniumHQ/selenium-ide) – An integrated development environment for Selenium scripts.
399- [Box UI Elements](https://github.com/box/box-ui-elements) – Box UI Elements are pre-built UI components that allow developers to add elements of the main Box web application into their own applications.
400- [react-responsive-ui](https://github.com/catamphetamine/react-responsive-ui) – Responsive React UI components.
401- [Mineral UI](https://github.com/mineral-ui/mineral-ui) –
402 A design system and React component library for the web that lets you quickly build high-quality, accessible apps.
403- [Covalent](https://github.com/Teradata/covalent) – Teradata UI Platform built on Angular Material.
404- [docs.expo.io](https://github.com/expo/expo-docs) – Documentation for Expo, its SDK, client and services.
405- [Add yourself to the list 😉](https://github.com/stipsan/scroll-into-view-if-needed/edit/master/README.md)
406
407[gzip-badge]: http://img.badgesize.io/https://unpkg.com/scroll-into-view-if-needed/umd/scroll-into-view-if-needed.min.js?compression=gzip&label=gzip%20size&style=flat-square
408[size-badge]: http://img.badgesize.io/https://unpkg.com/scroll-into-view-if-needed/umd/scroll-into-view-if-needed.min.js?label=size&style=flat-square
409[unpkg-dist]: https://unpkg.com/scroll-into-view-if-needed/umd/
410[module-formats-badge]: https://img.shields.io/badge/module%20formats-umd%2C%20cjs%2C%20es-green.svg?style=flat-square
411
412## Sponsors
413
414Thanks to [BrowserStack](https://www.browserstack.com) for sponsoring cross browser and device testing 😄
415
416<a href="https://www.browserstack.com" target="_blank"><img src="https://www.browserstack.com/images/layout/logo.svg"></a>