contentEditable
areas.
Polyfill for the shadowRoot.getSelection()
method for Safari 10+.
See a demo!
Safari supports .attachShadow()
to create a Shadow Root, but does not support retrieving user selection within this root.
You can safely use this code with other browsers (Firefox and Chromium), where it'll use the native version.
Include ./shadow.js
as an ES6 module, and call its getRange
method, passing the shadow root you would like to check.
This uses a native implementation where available (on Firefox and Chromium).
Typically, you'd call this method in response to a selectionchange
event, which is a global event on the document.
However, this polyfill will cause additional selectionchange
events to fire in the course of its work—possibly hundreds.
Instead, you can listen to the -shadow-selectionchange
event (exposed as shadow.eventName
), which will safely fire only once.
import * as shadow from './node_modules/shadow-selection-polyfill/shadow.js';
const root = myElement.createShadowRoot({mode: 'open'});
root.innerHTML = `...`;
document.addEventListener(shadow.eventName, () => {
const range = shadow.getRange(root);
if (range) {
console.info('range selected within root element', range.toString());
}
});
Don't use this with .attachShadow({mode: 'closed'})
, which is generally considered an antipattern anyway.
Install via NPM as shadow-selection-polyfill
, this has no dependencies.
Depending on your transpiler, you might be able to include the polyfill with:
// naked imports
import * as shadow from `shadow-selection-polyfill`;
// require() compatibility
const shadow = require('shadow-selection-polyfill');
This polyfill basically rapidly splits Text
nodes until it can find out just how many characters from the 'edge' of a Shadow Root that a user has selected.
It uses this combined with a few other tricks to come up with a range.
The algorithm is O(n) with respect to:
- the maximum length of a text node that contains the start or end of a selection range
- the depth of your nodes
If speed is important to you, then keep individual text nodes small.
As of February 2020, Firefox does not implement getSelection()
on the Shadow Root (issue), but happily pierces the Shadow Root from the document globally.
Chromium also implements ShadowRoot.getSelection()
in an unofficial way.
You can use this library to work around differences in browser implementations, as it'll use the native version when available.
This isn't technically a polyfill, as it adds completely new functionality: it doesn't patch an existing method. There's nothing stopping us from emulating a faux-Selection, but it would probably make the code more complex than required.
This is not an official Google product.