Code Monkey home page Code Monkey logo

smoothscroll-anchor-polyfill's Introduction

NPM version Build status License Documentation

 
 

smoothscroll-anchor-polyfill

⚓ Apply smooth scroll to anchor links to polyfill the CSS property scroll-behavior

 
 
 

Features

  • ✔ Smooth scroll to target when clicking an anchor
  • ✔ Smooth scroll to target on hashchange (◀/▶ buttons)
  • ✔ Updates URL with #fragment
  • ✔ Handles focus for improved accessibility
  • ✔ Doesn't break server-side rendering
  • ✔ 1.3KB gzipped

⚠ Requires smooth scroll for window.scroll() and Element.scrollIntoView() (e.g. smoothscroll-polyfill) to work!

 

Browser support

IE / Edge
IE / Edge
Firefox
Firefox
Chrome
Chrome
Safari
Safari
iOS Safari
iOS Safari
Opera
Opera
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

 

Usage

1. Set scroll-behavior: smooth in CSS

⚠ Has to be set global (on html), check the docs for limitations

 

Because CSS properties unknown to a browser can't efficiently be parsed from JavaScript, just specyfing the normal scroll-behavior property is not enough unfortunately.
You need to add an additional CSS variable so the polyfill can read it:

html {
  --scroll-behavior: smooth;
  scroll-behavior: smooth;
}

You can also use media queries, toggle classes etc. to control the smooth scroll. The following only enables smooth scroll on Desktop devices, for example:

html {
  --scroll-behavior: auto;
  scroll-behavior: auto;
}

@media screen and (min-width: 1150px) {
  html {
    --scroll-behavior: smooth;
    scroll-behavior: smooth;
  }
}

 

💡 This process can be automated using a PostCSS plugin, so you can write regular CSS and it'll be transformed to work with the polyfill automatically.
The plugin will also read your browserslist and choose the right transformation depending on if all your browsers support CSS variables or not. It just works™

 

Need to support Internet Explorer?

Legacy browsers like Internet Explorer do not support CSS variables, so you need another way to specify scroll-behavior. There are two options:

Using the inline style attribute
<html style="scroll-behavior: smooth;">
  ...
</html>
Using font-family

Alternatively, you can specify the property as the name of a custom font family. Your actual fonts will still work the way they should (plus, you can simply declare actual fonts on body). As with CSS variables (and unlike inline styles), this allows you to use normal CSS features like media queries.

<style>
  html {
    scroll-behavior: auto;
    font-family: 'scroll-behavior: auto;', 'Roboto', sans-serif;
  }
</style>

 

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.

smoothscroll-polyfill works, but you can just as well use another one or write your own implementation. Learn More

2a. From a CDN

<script src="https://unpkg.com/smoothscroll-anchor-polyfill"></script>

2b. From npm

npm install smoothscroll-anchor-polyfill

then

import 'smoothscroll-anchor-polyfill'

 

Full Documentation & Demo

The full documentation with advanced installation instructions, limitations, features like enabling and disabling the smooth scrolling and more can be found at jonaskuske.github.io/smoothscroll-anchor-polyfill.
The documentation site itself is built as a smooth scrolling one-page design, utilizing this polyfill.

 
 


PRs welcome!

 

© 2021, Jonas Kuske

smoothscroll-anchor-polyfill's People

Contributors

dependabot[bot] avatar jonaskuske avatar joshuasoileau avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

smoothscroll-anchor-polyfill's Issues

Add tests

All functionality needs unit tests before v1.0.0

Firefox Android: anchor#top doesn't trigger smooth scroll

Clicking on a tag <a href="#top"></a> jumps to top instantly instead of scrolling smoothly.

Since FF does has native support, this is most likely not a bug but a limitation of the native Scroll Behavior – but in that case it should be pointed out in the docs.

:target pseudo-class is not toggled

When the polyfill updates the URL through pushState(), CSS pseudo-classes like :target are not updated accordingly.
This is actually a browser bug as the spec is very clear about this, but we can maybe still work around it by setting location.hash after the scroll is completed (so it doesn't interrupt the smoothscroll), similar to pendingFocusChange – shouldn't be visible at all, as the hash was updated by history.pushState before already, but hopefully it force-updates :target CSS classes etc.

Support scroll containers other than viewport

Don't know if this is achievable yet, but you can use scroll-behavior on individual scroll containers, so e.g. the contents of an image carousel smoothscroll but not the viewport.

The polyfill should allow for this, too.

Expose shouldSmoothscroll() as part of public API

Giving users access to smoothscrollAnchorPolyfill.shouldSmoothscroll() would allow userland implementations for JS scroll APIs to stay in sync with the behavior detected by this polyfill.

Something along the lines of:

const scroll = scrollTo = function (opts = {}) {
  if (!opts.behavior && shouldSmoothscroll()) {
    opts.behavior = 'smooth'
  }
  const container = this instanceof Element ? this : window
  container.scroll(opts)
}

Renaming the API should be considered too, as well as maybe returning the detected behavior as a string (smooth|auto|initial|inherit|unset) instead of a simple boolean.

Support hashchange event

Right now this polyfill only works when anchor elements are clicked, not on arbitrary hashchange events.

Will require some workarounds as hashchange events are not cancelable.

Example/Possible Alternative Blink Recognition?

I'd rather not add another library just to detect the Blink layout engine.

Possible alternative detection(s) of Blink?...

if ( ((window.chrome || (window.Intl && Intl.v8BreakIterator)) &&
    'CSS' in window) ||
  /webkit\/537\.36.+chrome\/(?!27)/i.test(navigator.userAgent)
) {
  // Blink Engine
  window.__forceSmoothscrollAnchorPolyfill__ = true;
}

Support custom property --scroll-behavior

Support usage of a CSS custom property --scroll-behavior

While the current workaround using font-family is the only reliable way of putting custom information into CSS that works across all browsers, every modern browser supports custom properties.

To make this package more approachable (and not immediately confront users with a reliable but sketchy-looking CSS hack), the custom property --scroll-behavior should be both supported and recommended as the default way to set scroll behavior.

The font-family workaround (and inline styles) will continue to be supported, but it should be clearly communicated that this is only to support legacy browsers like Internet Explorer – for users that are only looking to bring smooth scrolling to modern browsers like Edge and Safari, --scroll-behavior is enough.

postcss-smoothscroll-anchor-polyfill needs to be updated in accordance with this, transpiling scroll-behavior to --scroll-behavior and only creating the font-family workaround if the users browserslist includes support for legacy browsers.

Add example implementations

Some reference implementations of how to use this in combination with e.g. jQuery or more advanced libraries like smooth-scroll-into-view-if-needed would be handy.

Like this:

examples/
 - basic.js
 - jquery.js
 - advanced.js

Update documentation

Documentation needs to be updated to reflect v1 API and describe edge cases like native hash change scrolling in Chrome. Then v1 should be ready for release 🥳

Bailout if defaultPrevented === true

<a id="next" href="#">Next slide</a>
<script>
  document.querySelector('#next').addEventListener('click', e => e.preventDefault())
</script>

In browsers with native support, clicking the anchor from the example above does not scroll the page – .preventDefault() stops the default href="#" scroll behavior.

smoothscroll-anchor-polyfill currently ignores this, its clickHandler starts scrolling even if event.defaultPrevented === true. Needs fix.

Smooth scroll on focus change

Curious if it's possible to use smoothscroll-anchor-polyfill with html:focus-within? I'd like to animate the tab/focus similar to Chrome and Firefox usage. The code below is not working. Any ideas?

html:focus-within {
  --scroll-behavior: smooth;
  scroll-behavior: smooth;
}

Polyfill removes path from URL on anchor click

Hello,

Thank you for sharing your awesome Smoothscroll Anchor Polyfill. It's fantastic, I'm really stoked on it and would love to use it moving forward.

In my testing it works really well in the "scroll-behavior:smooth challenged / problematic" Safari and IE = well done and thank you :)

I am using your polyfill on an (offline, development) Evolution CMS site and have a query relating to anchor link urls...

In the backend, an anchor link in the Evolution CMS might look like this (without the page alias in the link):

<li><a href="#anchor[+id+]">[+pagetitle+]</a></li>
or
<div><a href="#anchorTop"></a></div>

In the front end, the anchor links of these two items look like this (with the page alias put into the link so that same page anchor links work):

<li><a href="/page-1/#anchor121">My Page Name</a></li>
and
<div><a href="/page-1/#anchorTop"></a></div>

So the CMS adds the page alias into the anchor link . Ordinarily that's not a problem with an anchor link and you can scroll up and down a page without reloading the page.

But what seems to be happening when an anchor link is clicked with the Smoothscroll Anchor Polyfill active is that the page alias is removed from the anchor links and when clicking another anchor link on that page, the page actually reloads.

Eg This anchor link...

<div><a href="/page-1/#anchorTop"></a></div>

should have a url like...

mydomain.com/page-1/#anchorTop

but the page url actually becomes this when clicking that anchor link...

mydomain.com/#anchorTop

If I click another anchor link on that same page, the page reloads and the url becomes...

mydomain.com/page-1/#different-anchor

etc

So I am in some sort of weird loop with the url changeing and the page reloading with the Smoothscroll Anchor Polyfill active.
eg

clicking an anchor link with the following url
	mydomain.com/page-1/#anchorTop
scrolls to the anchor with page alias removed
	mydomain.com/#anchorTop
clicking another anchor on that same page results in a page reload before going to that new anchor
	mydomain.com/#page-1/my-different-anchor
clicking a different anchor link on that same page scrolls to the anchor with page alias removed
	mydomain.com/#my-other-anchor
etc and now I am dizzy :)

If I remove the two Smoothscroll Anchor Polyfill javascripts from the page, this issue no longer happens - I can smooth scroll up and down a page as expected and as many times as I like and the page is never reloaded (but obviously I would not get the great benefits of your Polyfill with Safari and IE).

Have you got any tricks to stop your Polyfill script from removing the page alias from clicked anchor links?
(I am not a programmer so please go easy on me with any code ;) )

Many thanks in advance.

Only run polyfill if CSS requires it

This polyfill is meant to replicate scroll-behavior but right now it enables smooth scroll even if the respective scroll-behavior property is not set to smooth. Thus it is not automatically in sync with the CSS, if you load the polyfill but forget to set the actual CSS property, old browsers will get smooth scroll (powered by the polyfill) while browsers with native support will have instant, jumping scrolling – this is bad.

Since browsers don't parse CSS properties they don't know, we can't (efficiently) check whether scroll-behavior is actually set, so we need other ways to enable the polyfill:

Use inline styles:

<!-- Can be parsed from JS even if browser doesn't support it -->
<html style="scroll-behavior: scroll;">
...
<script src="https://unpkg.com/smoothscroll-anchor-polyfill"></script>
<script> SmoothscrollAnchorPolyfill.polyfill(); </script>

Use font-family as a workaround, as custom font names can be easily parsed from JS:

<style>
  html {
    scroll-behavior: smooth;
    /* Specify as font-family so browsers without native support can see it  */
    font-family: 'scroll-behavior: smooth', sans-serif;
  }
</style>
<script src="https://unpkg.com/smoothscroll-anchor-polyfill"></script>

The font-family workaround can be automated by a PostCSS plugin which detects the scroll-behavior property and adjusts the font-family accordingly. This way, developers using a build system can write normal CSS and not think about the polyfill, it just works ™️


This alone should not work any longer:

<script src="https://unpkg.com/smoothscroll-anchor-polyfill"></script>

ESM support

Offer two additional builds, a full ES module build for use in bundlers (easier tree shaking) and a minified ES module build for use directly from the browser using import polyfill from (fake link!) 'https://unpkg.com/smoothscroll-anchor-polyfill/esm'

doesnt work in IE11

Tried the font-family hack and the inline style on , IE11 just jumps to named anchors immediately.

Bail out when query params don't match

If the current location is foo.com?page_id=11 and a link points at foo.com?page_id=7#comments, smoothscroll-anchor-polyfill intercepts the navigation and tries to scroll to #comments, even though a full page load would be needed due to the different query param page_id. This breaks websites relying on the query string to load different pages (or behave differently on page load).

To fix this, a comparison of location.search needs to be added to isAnchorToLocalElement().

Demo doesn't work as-is on Safari 14.0.3

According to https://caniuse.com/?search=smooth-scroll, scroll-behaviour is still not supported on Safari.

When i view the demo at https://jonaskuske.github.io/smoothscroll-anchor-polyfill/ it doesn't work in Safari 14.0.3 (on macOS 11.2.3 (20D91)), however if I add style="scroll-behavior: smooth" to the HTML tag in inspector on the hosted demo, it works. I'm not really sure why this is the case, because obviously Safari has supported CSS variables for a while.

Improve README

Improve the README by adding a better Getting started/Usage section and highlighting the docs

Handle prefers-reduced-motion

Firefox has shipped support for the Level 5 Media Query prefers-reduced-motion in v63.
It is now the first browser to support both prefers-reduced-motion and scroll-behavior and should thus act as reference for the interplay between the two properties.
We should explore and match the scroll behavior found in Firefox if the user prefers reduced motion.
This might be a breaking change in case we end up auto-disabling smooth scroll for reduced motion.

If other vendors ship different interpretations in the future, we might have to reevaluate.

Support scroll-margin

I can think of it to be with a css variable.

Currently scroll-margin. It doesn't work in Safari, so to solve that need I came up with that option. Thank you for your contribution to the community.

Doesn't work with <a name="named-anchor">

Instead of using ids, I'm using anchors with name attribute, e.g.

<nav>
	<a href="#heading">Heading</a>
	<a href="#footer">Footer</a>
</nav>

<a name="heading"></a>
<h2>Heading</h2>

<a name="footer"></a>
<footer>Page footer</footer>

This polyfill won't look for a[name="heading"] when clicking the related nav link, so smooth scrolling doesn't happen, where it does if I change name="heading" to id="heading".

Love this otherwise =)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.