Skip to content

Commit

Permalink
Merge pull request #19 from jonaskuske/v1.2.0
Browse files Browse the repository at this point in the history
v1.2.0
  • Loading branch information
jonaskuske committed Dec 14, 2018
2 parents 7db5151 + 3e5a34f commit 0565c4c
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 34 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.0] - 2018-12-14
### Added
- ES Module versions are now provided as `index.mjs` and `index.min.mjs`!
- Firefox bug where `<a href="#top">` doesn't smooth scroll now mentioned in docs
### Changed
- Update docs to mention Firefox supporting both `scroll-behavior` and `prefers-reduced-motion`
### Fixed
- Support for (URL-encoded) special characters in fragments, e.g. "#👍🏻" (which the browser will encode to "#%F0%9F%91%8D%F0%9F%8F%BB")

## [1.1.3] - 2018-12-10
### Changed
- Updated README to include bundle size and SSR-compatibility as features
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
| --------- | --------- | --------- | --------- | --------- | --------- |
| IE9+, Edge| native| native*| last 2 versions| last 2 versions| native*

> \* hashchange navigation triggered by forwards/backwards buttons isn't smooth despite native support. [Learn more](https://jonaskuske.github.io/smoothscroll-anchor-polyfill#hashchange-blink)
> \* hashchange navigation triggered by forwards/backwards buttons isn't smooth despite native support. [Learn more](https://jonaskuske.github.io/smoothscroll-anchor-polyfill#native-inconsistencies)
&nbsp;

Expand Down Expand Up @@ -68,7 +68,7 @@ Alternatively, you can specify the property as the name of a custom font family.
}
</style>
```
> This process can also be automated using a [PostCSS plugin](https://github.com/jonaskuske/postcss-smoothscroll-anchor-polyfill), so you can write regular CSS and don't have to bother with font-families. It just works™
> This process can be automated using a [PostCSS plugin](https://github.com/jonaskuske/postcss-smoothscroll-anchor-polyfill), so you can write regular CSS and don't have to bother with font-families. It just works™
### 2. Install the polyfill
Because this polyfill only wires up anchor links to use the browser's native `window.scroll()` and `element.scrollIntoView()` methods, you'll need to load a polyfill providing smooth scroll to these methods in addition to the steps outlined below.
Expand Down
19 changes: 13 additions & 6 deletions docs/index.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
.smooth-scroll {
scroll-behavior: smooth;
/* Additionally specified in custom font-family so polyfill can parse it */
font-family: 'scroll-behavior:smooth';
}

@media (prefers-reduced-motion: reduce) {
.smooth-scroll {
scroll-behavior: auto;
font-family: "scroll-behavior: auto";
}
}

body {
margin: 0;
font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', 'Segoe UI Emoji', 'Apple Color Emoji', 'Noto Color Emoji', sans-serif;
Expand Down Expand Up @@ -60,12 +73,6 @@ section, .fullscreen {
padding-top: 5rem;
}

.smooth-scroll {
scroll-behavior: smooth;
/* Additionally specified in custom font-family so polyfill can parse it */
font-family: 'scroll-behavior:smooth';
}

.fullscreen h1 {
margin-top: 13rem;
}
Expand Down
54 changes: 32 additions & 22 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<li><a class="button" href="#docs">Docs</a></li>
<li><a class="button" href="#legal">Legal</a></li>
<li></li>
<li><a href="#top">⬆ to Top</a></li>
<li><a href="#">⬆ to Top</a></li>
</ul>
</nav>
</header>
Expand Down Expand Up @@ -176,6 +176,8 @@ <h4 id="install-npm"><span>Option 2: With npm</span></h3>
// (Unlike this package, smoothscroll-polyfill needs to be actively invoked: )
smoothscrollPolyfill.polyfill();
</code></pre>
<p>👉🏻 The polyfill is also provided in ES module format as <code>index.mjs</code>
and <code>index.min.mjs</code>.</p>
<h3 id="code-splitting"><span>Advanced installation (with Code
Splitting)</span></h3>
<p>If you're using a build system with support for code splitting
Expand Down Expand Up @@ -306,22 +308,23 @@ <h4 id="no-javascript-scroll"><code>scroll-behavior</code> doesn't
tags and through <code>hashchange</code> events. You'll still have to
pass <code>{ behavior: 'smooth' }</code> when using APIs like <code>window.scroll()</code>
unless your polyfill for these APIs has it's own CSS property check.</p>
<h4 id="hashchange-blink"><span>Scrolling triggered by <code>hashchange</code>
is not smooth in Chrome</span></h4>
<p>This actually doesn't have anything to do with this polyfill –
it's a limitation of Blink's native implementation (so it affects
other Blink-based browsers like Opera, too). While 'normal'
scrolling is smooth, if you click a couple of links and then
navigate back and forth using the browser's forwards/backwards
buttons (which triggers a <code>hashchange</code>
<h4 id="native-inconsistencies"><span>Inconsistencies in native
implementations</span></h4>
<p><b>Blink (e.g. Chrome, Opera):</b><br>
While 'normal' scrolling is smooth, if you click a couple of links
and then navigate back and forth using the browser's
forwards/backwards buttons (which triggers a <code>hashchange</code>
everytime), it jumps from anchor to anchor instead of scrolling
smoothly. If this is important to you, you can fix it by
detecting the Blink engine and force-enabling this polyfill. Load
<a href="https://github.com/isocroft/browsengine">browsengine.js</a>,
smoothly.<br>If this is important to you, you can fix it by detecting
the Blink engine and force-enabling this polyfill. Load <a href="https://github.com/isocroft/browsengine">browsengine.js</a>,
then do (<i>before</i> the polyfill runs):</p>
<pre><code>if (window.webpage.engine.blink) {
window.__forceSmoothscrollAnchorPolyfill__ = true;
window.__forceSmoothscrollAnchorPolyfill__ = true;
}</code></pre>
<p><b>Gecko (Firefox):</b><br>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=279629#c27">Anchors
pointing to <code>#top</code> don't smooth scroll.</a> Use
anchors pointing at <code>#</code> for an easy fix.</p>
<h3 id="faq"><span>FAQ</span></h3>
<h4 id="ssr"><span>Will this break Server Side Rendering?</span></h4>
<p>No.</p>
Expand All @@ -334,15 +337,22 @@ <h4 id="event-delegation"><span>Will this work if anchors are inserted
<h4 id="reduced-motion"><span>Does this support <code>prefers-reduced-motion</code>?</span></h4>
<p><code>prefers-reduced-motion</code> is a relatively new CSS media
query that hints at whether a client prefers less motion, which can
be important for people with certain illnesses. But Safari is the
only browser that has implemented it yet, and it doesn't support the
Scroll Behavior spec, so there is no reference for the interplay of
<code>prefers-reduced-motion</code> and Scroll Behavior yet. For this
reason, it is not implemented (yet) in this polyfill.<br>
However, it is relatively safe to assume that <code>prefers-reduced-motion</code>
will disable <code>scroll-behavior: smooth</code> so this can
absolutely be discussed – please file an <a href="https://github.com/jonaskuske/smoothscroll-anchor-polyfill/issues">issue
on GitHub</a> if it affects your project.</p>
be important for people with certain illnesses. Firefox currently is
the only browser to support both <code>scroll-behavior</code> and
<code>prefers-reduced-motion</code> and thus acts as a reference for
the interplay between these two properties – and in Firefox, smooth
scrolling is <b>not</b> disabled automatically if this media query
matches.<br>
So no, this polyfill does not automatically disable itself if the
client prefers less motion, but yes, it supports <code>prefers-reduced-motion</code>
the same way Firefox does – via a media query:
<pre><code>@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
font-family: "scroll-behavior: auto";
}
}</code></pre>
</p>
</section>
<!-- -->
<!-- Way too big legal section -->
Expand Down
16 changes: 15 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

(function(global, Factory) {
var SmoothscrollAnchorPolyfill = new Factory();
if (typeof exports === 'object' && typeof module !== 'undefined') {
var isESModule = typeof global !== 'undefined' && global.__ESM_MOCK__;
if (!isESModule && typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = SmoothscrollAnchorPolyfill;
} else {
global.SmoothscrollAnchorPolyfill = SmoothscrollAnchorPolyfill;
Expand Down Expand Up @@ -176,6 +177,7 @@
*/
function getScrollTarget(hash) {
if (typeof hash !== 'string') return null;
hash = decodeHash(hash);

// Retrieve target if an id is specified in the hash, otherwise use body.
// If hash is "#top" and no target with id "top" was found, also use body
Expand All @@ -185,6 +187,18 @@
return target;
}

/**
* Takes a URL-encoded hash and returns the decoded version.
* @param {string} hash Hash to decode
*/
function decodeHash(hash) {
try {
// "#%F0%9F%91%8D%F0%9F%8F%BB" -> "#👍🏻"
hash = decodeURIComponent(hash);
} catch (e) { /* */ }
return hash;
}

/**
* Walks up the DOM starting from "element" until an element satisfies "validate()"
* @param {HTMLElement} element The element from where to start validating
Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"name": "smoothscroll-anchor-polyfill",
"version": "1.1.3",
"version": "1.2.0",
"description": "Apply smooth scroll to anchor links to replicate CSS scroll-behavior",
"main": "dist/index.js",
"unpkg": "dist/index.min.js",
"module": "dist/index.mjs",
"files": [
"dist/*",
"index.js",
Expand All @@ -17,9 +18,11 @@
],
"scripts": {
"test": "jest --verbose",
"minify": "npx terser index.js -m -o dist/index.min.js --comments",
"build": "npx copyfiles index.js dist && node scripts/esm.js",
"minify": "npx terser dist/index.js -m -o dist/index.min.js --comments",
"minify:esm": "npx terser dist/index.mjs -m -o dist/index.min.mjs --comments --module",
"version": "npx replace '__VERSION__' $npm_package_version dist/*",
"prepublishOnly": "npm run minify && npx copyfiles index.js dist && npm run version"
"prepublishOnly": "npm run build && npm run minify && npm run minify:esm && npm run version"
},
"repository": {
"type": "git",
Expand Down
26 changes: 26 additions & 0 deletions scripts/esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const fs = require('fs')
const path = require('path')

// Get original, non-ESM code
const originalCode = fs.readFileSync(
path.resolve(__dirname, '../', 'index.js'),
{ encoding: 'utf8' }
)

// Transform CommonJS/universal code into ESM code.
// We provide an Object as thisArg and tell the script to bind
// to it (even if run in CommonJS env) through a special property.
// Then we export the polyfill now bound to the provided Object.
const esmCode = `const esmMock = { __ESM_MOCK__: true };
(function() {
${originalCode}
}).call(esmMock);
const { SmoothscrollAnchorPolyfill } = esmMock;
const { destroy, polyfill } = SmoothscrollAnchorPolyfill;
export { destroy, polyfill };
export default SmoothscrollAnchorPolyfill;`

// Write ESM code to directory "dist"
fs.writeFileSync(path.resolve(__dirname, '../', 'dist', 'index.mjs'), esmCode, 'utf8')
17 changes: 17 additions & 0 deletions test/browser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,23 @@ describe('Scroll targeting', () => {
expect(spy).toHaveBeenCalled()
})

it('Supports (URL-encoded) special characters in the hash', () => {
document.documentElement.setAttribute('style', 'scroll-behavior:smooth')

const specialCharacters = 'emöji―💕'

// hash automatically encoded to #em%C3%B6ji%E2%80%95%F0%9F%92%95
const anchor = insertElement('a', { href: `#${specialCharacters}` })
const target = insertElement('div', { id: specialCharacters })

const spy = jest.spyOn(target, 'scrollIntoView')
polyfill()

anchor.click()
expect(spy).toHaveBeenCalled()
expect(location.hash).toMatch(`#${encodeURIComponent(specialCharacters)}`)
})

it('Scrolls to element instead of top if hash "#top" targets an ID', () => {
document.documentElement.setAttribute('style', 'scroll-behavior:smooth')

Expand Down

0 comments on commit 0565c4c

Please sign in to comment.