Skip to content

Commit

Permalink
Merge branch 'feature/constructor-options'
Browse files Browse the repository at this point in the history
  • Loading branch information
memob0x committed Jun 28, 2022
2 parents 8692e8c + 04eec48 commit e9c4eaf
Show file tree
Hide file tree
Showing 38 changed files with 11,618 additions and 11,007 deletions.
77 changes: 74 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,79 @@
"ecmaVersion": 13,
"sourceType": "module"
},
"rules": {},
"overrides": [
"rules": {
"import/no-unused-modules": [
1,

{
"unusedExports": true,
"src": ["./src"]
}
],

"import/no-unresolved": [2, {}],

"jsdoc/check-access": 1, // Recommended
"jsdoc/check-alignment": 1, // Recommended
// "jsdoc/check-examples": 1,
"jsdoc/check-indentation": 1,
"jsdoc/check-line-alignment": 1,
"jsdoc/check-param-names": 1, // Recommended
"jsdoc/check-property-names": 1, // Recommended
"jsdoc/check-syntax": 1,
"jsdoc/check-tag-names": 1, // Recommended
"jsdoc/check-types": 1, // Recommended
"jsdoc/check-values": 1, // Recommended
"jsdoc/empty-tags": 1, // Recommended
"jsdoc/implements-on-classes": 1, // Recommended
"jsdoc/match-description": 1,
"jsdoc/multiline-blocks": 1, // Recommended
"jsdoc/newline-after-description": 1, // Recommended
"jsdoc/no-bad-blocks": 1,
"jsdoc/no-defaults": 1,
// "jsdoc/no-missing-syntax": 1,
"jsdoc/no-multi-asterisks": 1, // Recommended
// "jsdoc/no-restricted-syntax": 1,
// "jsdoc/no-types": 1,
"jsdoc/no-undefined-types": 1, // Recommended
"jsdoc/require-asterisk-prefix": 1,
// "jsdoc/require-description": 1,
"jsdoc/require-description-complete-sentence": 1,
// "jsdoc/require-example": 1,
// "jsdoc/require-file-overview": 1,
"jsdoc/require-hyphen-before-param-description": 1,
"jsdoc/require-jsdoc": 1, // Recommended
"jsdoc/require-param": 1, // Recommended
"jsdoc/require-param-description": 1, // Recommended
"jsdoc/require-param-name": 1, // Recommended
"jsdoc/require-param-type": 1, // Recommended
"jsdoc/require-property": 1, // Recommended
"jsdoc/require-property-description": 1, // Recommended
"jsdoc/require-property-name": 1, // Recommended
"jsdoc/require-property-type": 1, // Recommended
"jsdoc/require-returns": 1, // Recommended
"jsdoc/require-returns-check": 1, // Recommended
"jsdoc/require-returns-description": 1, // Recommended
"jsdoc/require-returns-type": 1, // Recommended
"jsdoc/require-throws": 1,
"jsdoc/require-yields": 1, // Recommended
"jsdoc/require-yields-check": 1, // Recommended
"jsdoc/tag-lines": 1, // Recommended
"jsdoc/valid-types": 1 // Recommended
},
"overrides": [{
"files": [
"./*.js"
],
"env": {
"node": true
},
"settings": {
"import/resolver": {
"node": {}
}
}
},
{
"files": [
"*.spec.js"
Expand All @@ -25,6 +96,6 @@
"rules": {
"no-unused-expressions": "off"
}
}
}
]
}
35 changes: 35 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "unit tests",
"program": "${workspaceFolder}/node_modules/.bin/mocha",
"args": [
"--colors",
"--experimental-specifier-resolution=node",
"${workspaceFolder}/test/unit/*.spec.*",
],
"internalConsoleOptions": "openOnSessionStart",
"skipFiles": [
"<node_internals>/**"
]
},
{
"type": "node",
"request": "launch",
"name": "e2e tests",
"program": "${workspaceFolder}/node_modules/.bin/mocha",
"args": [
"--colors",
"--experimental-specifier-resolution=node",
"${workspaceFolder}/test/e2e/*.spec.*",
],
"internalConsoleOptions": "openOnSessionStart",
"skipFiles": [
"<node_internals>/**"
]
},
]
}
9 changes: 5 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"eslint.validate": [
"javascript"
],
"eslint.validate": ["javascript"],
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
}
"eslint.format.enable": true,
"eslint.alwaysShowStatus": true,
}
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2021 Daniele Fioroni
Copyright (c) 2022 Daniele Fioroni

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
123 changes: 66 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ A small (~4K gzipped) unobtrusive script aimed to encourage a **CSS-first** appr
![with scrollbar gap compensation](https://github.com/memob0x/scroll-padlock/blob/master/docs/with-gap-compensation.gif?raw=true)

## Try it out

Here's some example projects for the most common setups:

* React ([Preview](https://vouoz.csb.app/) - [Browse](https://codesandbox.io/s/scroll-padlock-react-vouoz))
* Angular ([Preview](https://xuudg.csb.app/) - [Browse](https://codesandbox.io/s/scroll-padlock-angular-xuudg))
* Vue ([Preview](https://6ewti.csb.app/) - [Browse](https://codesandbox.io/s/scroll-padlock-vue-6ewti))
* Vanilla ([Preview](https://rgzrb.csb.app/) - [Browse](https://codesandbox.io/s/scroll-padlock-vanilla-rgzrb))
- React ([Preview](https://vouoz.csb.app/) - [Browse](https://codesandbox.io/s/scroll-padlock-react-vouoz))
- Angular ([Preview](https://xuudg.csb.app/) - [Browse](https://codesandbox.io/s/scroll-padlock-angular-xuudg))
- Vue ([Preview](https://6ewti.csb.app/) - [Browse](https://codesandbox.io/s/scroll-padlock-vue-6ewti))
- Vanilla ([Preview](https://rgzrb.csb.app/) - [Browse](https://codesandbox.io/s/scroll-padlock-vanilla-rgzrb))

## Inclusion

Expand All @@ -34,27 +35,30 @@ The source code is entirely written in [standard ECMAScript](https://tc39.es/) w
All major budle formats are supported, including [umd](https://github.com/umdjs/umd), [iife](https://developer.mozilla.org/en-US/docs/Glossary/IIFE), [amd](https://en.wikipedia.org/wiki/Asynchronous_module_definition), [cjs](https://en.wikipedia.org/wiki/CommonJS), [esm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and [SystemJS](https://github.com/systemjs/systemjs); also, a minified version and a transpiled version through babel are available for each of those.

### Node:

```javascript
import ScrollPadlock from 'scroll-padlock/dist/es/scroll-padlock.js';
import ScrollPadlock from "scroll-padlock/dist/es/scroll-padlock.js";

const scrollPadlock = new ScrollPadlock();
```

### Browser (modules):

```html
<script type="module">
import ScrollPadlock from 'path/to/scroll-padlock/dist/es/scroll-padlock.min.js';
import ScrollPadlock from "path/to/scroll-padlock/dist/es/scroll-padlock.min.js";
const scrollPadlock = new ScrollPadlock();
const scrollPadlock = new ScrollPadlock();
</script>
```

### Browser (globals):

```html
<script src="path/to/scroll-padlock/dist/iife/scroll-padlock.min.js"></script>

<script>
var scrollPadlock = new ScrollPadlock();
var scrollPadlock = new ScrollPadlock();
</script>
```

Expand All @@ -68,13 +72,16 @@ An instance requires the **html element** which scroll needs to be controlled an

```javascript
// The css class with the rules that would lock the page scroll
const SCROLL_LOCKED_CSS_CLASS_NAME = 'your-locked-class';
const SCROLL_LOCKED_CSS_CLASS_NAME = "your-locked-class";

// The element which scroll state needs to be controlled
const { documentElement } = document;

// Creates the instance
const instance = new ScrollPadlock(documentElement, SCROLL_LOCKED_CSS_CLASS_NAME);
const instance = new ScrollPadlock(
documentElement,
SCROLL_LOCKED_CSS_CLASS_NAME
);
```

At this point, the lock state can be changed simply **toggling that CSS class**; since the CSS class change is internally observed, the class change itself can be done through native DOM API, a virtual DOM library, another DOM manipulation script, etc...
Expand All @@ -88,21 +95,22 @@ classList.add(SCROLL_LOCKED_CSS_CLASS_NAME);
// Unlocks the body scroll
classList.remove(SCROLL_LOCKED_CSS_CLASS_NAME);
```

## CSS Rules Examples

The following ruleset alone is enough to ensure a cross-browser body scroll lock for a standard vertical-scroll page:

```css
html.your-locked-class {
/* Position-fixed hack, locks iOS too */
position: fixed;
width: 100%;
/* Position-fixed hack, locks iOS too */
position: fixed;
width: 100%;

/* Avoids scroll to top */
top: calc(var(--scroll-padlock-scroll-top) * -1);
/* Avoids scroll to top */
top: calc(var(--scroll-padlock-scroll-top) * -1);

/* Reserves space for scrollbar */
padding-right: var(--scroll-padlock-scrollbar-width);
/* Reserves space for scrollbar */
padding-right: var(--scroll-padlock-scrollbar-width);
}
```

Expand All @@ -111,42 +119,42 @@ Some [browser recognition logic](https://gist.github.com/memob0x/0869e759887441b
```css
/* iOS only */
html.your-locked-class.ios {
/* iOS fixed position hack */
position: fixed;
width: 100%;
/* iOS fixed position hack */
position: fixed;
width: 100%;

/* Avoids scroll to top */
top: calc(var(--scroll-padlock-scroll-top) * -1);
/* Avoids scroll to top */
top: calc(var(--scroll-padlock-scroll-top) * -1);
}

/* Standard browsers only */
html.your-locked-class.not-ios,
html.your-locked-class.not-ios body {
/* Standard way to lock scroll */
overflow: hidden;
/* Standard way to lock scroll */
overflow: hidden;
}

html.your-locked-class.not-ios body {
/* Reserves space for scrollbar */
/* (iOS has overlay scrollbars, this rule would have no effect there anyway) */
padding-right: var(--scroll-padlock-scrollbar-width);
/* Reserves space for scrollbar */
/* (iOS has overlay scrollbars, this rule would have no effect there anyway) */
padding-right: var(--scroll-padlock-scrollbar-width);
}
```

## CSS Variables

This is the complete list of CSS variables set by this library on the given elements.

* `--scroll-padlock-scroll-top`: the number of pixels that the target is scrolled vertically.
* `--scroll-padlock-scroll-left`: the number of pixels that the target is scrolled horizontally.
* `--scroll-padlock-scrollbar-width`: the target's vertical scrollbar size.
* `--scroll-padlock-scrollbar-height`: the target's horizontal scrollbar size.
* `--scroll-padlock-outer-width`: the target's width including the scrollbar size.
* `--scroll-padlock-outer-height`: the target's height including the scrollbar size.
* `--scroll-padlock-inner-width`: the target's width without the scrollbar size.
* `--scroll-padlock-inner-height`: the target's height without the scrollbar size.
* `--scroll-padlock-scroll-width`: the target's content width.
* `--scroll-padlock-scroll-height`: the target's content height.
- `--scroll-padlock-scroll-top`: the number of pixels that the target is scrolled vertically.
- `--scroll-padlock-scroll-left`: the number of pixels that the target is scrolled horizontally.
- `--scroll-padlock-scrollbar-width`: the target's vertical scrollbar size.
- `--scroll-padlock-scrollbar-height`: the target's horizontal scrollbar size.
- `--scroll-padlock-outer-width`: the target's width including the scrollbar size.
- `--scroll-padlock-outer-height`: the target's height including the scrollbar size.
- `--scroll-padlock-inner-width`: the target's width without the scrollbar size.
- `--scroll-padlock-inner-height`: the target's height without the scrollbar size.
- `--scroll-padlock-scroll-width`: the target's content width.
- `--scroll-padlock-scroll-height`: the target's content height.

## API

Expand Down Expand Up @@ -174,21 +182,21 @@ instance.scroll = { top, left };

// Gets the current instance element layout sizes
const {
// The target's width and height including the scrollbar size
outerWidth,
outerHeight,
// The target's width and height including the scrollbar size
outerWidth,
outerHeight,

// The target's width and height without the scrollbar size
innerWidth,
innerHeight,
// The target's width and height without the scrollbar size
innerWidth,
innerHeight,

// The target's content width and height
scrollWidth,
scrollHeight,
// The target's content width and height
scrollWidth,
scrollHeight,

// The target's vertical and horizontal scrollbar size
scrollbarWidth,
scrollbarHeight
// The target's vertical and horizontal scrollbar size
scrollbarWidth,
scrollbarHeight,
} = instanse.layout;
```

Expand All @@ -212,20 +220,20 @@ If positioned elements "jumps" on a parent lock state change, the same CSS varia
```css
/* A right-positioned child */
.positioned-element {
position: fixed;
right: 0;
position: fixed;
right: 0;
}

/* The same right-positioned element, */
/* not affected by its own container scrollbars disappearance */
.your-locked-class .positioned-element {
right: var(--scroll-padlock-scrollbar-width);
right: var(--scroll-padlock-scrollbar-width);
}
```

## iOS Bars and Keyboard Tray

There might still be an iOS edge case when locking body scroll with _position: fixed_ technique.
There might still be an iOS edge case when locking body scroll with _position: fixed_ technique.

When the page is scrolled the **system bars** become smaller; at that point, when focusing an input element programmatically, the keyboard tray is triggered and the bars become larger again; that, probably when some animations are taking place, can cause the following visual artifacts.

Expand All @@ -243,12 +251,13 @@ const isIOS = someWayToDetectAppleIOS();
// on an a form field make the keyboard tray to show
// and that triggers, along with the visual artifact itself, a "resize" event
window.addEventListener("resize", () => {
if (isIOS && classList.contains(SCROLL_LOCKED_CSS_CLASS_NAME)) {
// "Re-aligns" the iOS keyboard sub-window
window.scrollTo(0, 0);
}
if (isIOS && classList.contains(SCROLL_LOCKED_CSS_CLASS_NAME)) {
// "Re-aligns" the iOS keyboard sub-window
window.scrollTo(0, 0);
}
});
```

The problem should be solved at this point.

![ios bug](https://github.com/memob0x/scroll-padlock/blob/master/docs/ios-fix.gif?raw=true)
Expand Down
Loading

0 comments on commit e9c4eaf

Please sign in to comment.