Skip to content

Instantly share code, notes, and snippets.

@oliverfoster
Created May 2, 2023 18:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oliverfoster/f5332337c96256fd5daf5b0fb156580e to your computer and use it in GitHub Desktop.
Save oliverfoster/f5332337c96256fd5daf5b0fb156580e to your computer and use it in GitHub Desktop.
bionic reading javascript
(() => {
// function for adding bionic style to a node
function process (n) {
if (n._isBionic) return;
const ns = [...n.childNodes]
.filter(n => n.nodeType === n.TEXT_NODE)
.filter(n => n.nodeValue.trim());
if (!ns.length) return;
ns.forEach(function apply(n) {
const pn = n.parentNode;
pn._isBionic = true;
const v = String(n.nodeValue);
const ws = [...v.matchAll(/\s/g)];
let last = 0;
const em = ws.reduce((parts, entry) => {
const next = entry.index;
parts.push(v.substring(last, next + 1));
last = next + 1;
return parts;
}, []);
const end = v.substring(last, v.length);
if (end) em.push(end);
const ch = [];
em.map(t => {
const l = t.trim().length;
if (!l) return [document.createTextNode(t)];
const el = Math.min(Math.ceil(l / 2), 5);
const s = document.createElement('b');
s.innerText = t.substring(0, el);
t = t.substring(el);
return [
s,
document.createTextNode(t)
];
}).forEach(r => {
r.forEach(w => {
w._isBionic = true;
ch.push(w);
pn.insertBefore(w, n);
});
});
pn._bionics = pn._bionics ?? [];
pn._bionics.push({
on: n,
ch
});
pn.removeChild(n);
});
}
// function for removing bionic style
function unprocess (n) {
if (!n._isBionic) return;
const parentElement = n.parentNode;
if (!parentElement) return;
parentElement._bionics?.forEach(config => {
try {
const { on, ch } = config;
parentElement.insertBefore(on, ch[0]);
ch.forEach(n => parentElement.removeChild(n));
} catch (er) {
}
});
delete n._isBionic;
delete parentElement._bionics;
}
// Listen for new nodes and convert to bionic
const mu = new MutationObserver(l => {
l.forEach(i => {
if (!i.addedNodes.length) return;
[...i.addedNodes].forEach(n => {
if (n.nodeType !== Node.ELEMENT_NODE) return;
if (n.matches('script, style, svg')) return;
[n, ...n.querySelectorAll('*')].forEach(process);
});
});
});
mu.observe(document.body, { subtree: true, childList: true });
// process all nodes in the document on execution
[...document.querySelectorAll('body *:not(script, style, svg)')].forEach(process);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment