@neftaly/editcontext-polyfill
v0.1.1
Published
Polyfill for the EditContext API
Maintainers
Readme
editcontext-polyfill
Polyfill for the EditContext API (caniuse), a replacement for contenteditable.
This polyfill was written by a LLM (Claude Opus 4.6) using TDD & fuzzing, against Chrome's native EditContext implementation. The test suite includes ports of the Web Platform Tests.
Browser support
| Browser | Support | | -------------- | ------------------------------ | | Chrome / Edge | 121+ native (no polyfill) | | Firefox | 125+ via polyfill | | Safari | 15.4+ via polyfill |
Install
npm install @neftaly/editcontext-polyfillOr via script tag:
<script src="https://cdn.jsdelivr.net/gh/neftaly/editcontext-polyfill/dist/editcontext-polyfill.iife.js"></script>By default, the polyfill is bypassed for browsers with native EditContext support. To force it on regardless:
<script src="editcontext-polyfill.iife.js" data-force></script>Or with the ESM API:
import { install } from "@neftaly/editcontext-polyfill";
install({ force: true });Usage
import "@neftaly/editcontext-polyfill";
const el = document.querySelector("#editor");
const ec = new EditContext({ text: "" });
el.editContext = ec;
ec.addEventListener("textupdate", (e) => {
console.log(e.text, e.updateRangeStart, e.updateRangeEnd);
// Re-render your editor view here
});
el.focus();Compatibility
This polyfill uses a hidden textarea for input capture. Most of the EditContext API works identically to Chrome's native implementation, with some exceptions.
Unsupported features
updateControlBounds()is a no-op. Logs a one-time warning in the IIFE build (warnings are removed from the ESM build at compile time).- CSS
:focuson<canvas>doesn't work (the hidden textarea is appended todocument.body, not the canvas). Toggle a CSS class in yourfocus/blurhandlers instead. All other elements support:focusvia shadow DOM. isTrustedon re-dispatched events (keyboard, clipboard, composition) isfalse.
Approximate features
textformatupdateis dispatched during composition with a default format (solid thin underline over the full composition range). The polyfill cannot access OS-level IME format data, so individual clause styling is not available.characterboundsupdateis dispatched during composition for the full composition range. IME popups useupdateSelectionBounds()position instead of per-character bounds.
Architectural limitations
The hidden textarea architecture means some behaviors cannot match Chrome native:
- IME composition +
updateSelection: IfupdateSelection()is called programmatically and then an IME composition begins immediately, the hidden textarea's cursor may not match the EditContext's selection, causing composition text to land at the wrong position. - Sequential IME composition edge cases: Rapid sequential
imeSetCompositioncalls can occasionally desync the textarea's native IME tracking from the polyfill's internal state, causing intermediate composition text to not be fully replaced. The polyfill skipstextarea.valuesync during composition to mitigate this, but edge cases remain. compositionend.dataon blur: When blur interrupts an active IME composition, Chrome's nativecompositionend.databehavior is inconsistent (sometimes empty string, sometimes the composed text). The polyfill may not match in all cases.
Notes
- Firefox insertLineBreak: On
Enterkey, Firefox's normalinsertLineBreakevent is blocked. Instead, aninsertParagraphevent is fired, to match Chrome.Shift+Enterbehaves the same (insertLineBreak). - Detach+reattach focus: Setting
el.editContext = nullthenel.editContext = new EditContext()across separate calls does not restore focus. Chrome retains element focus natively, but the polyfill loses it when the hidden textarea is destroyed on detach. Swapping contexts in a single call (el.editContext = newCtx) works correctly.
Testing
tests/unit/— pure unit tests forEditContextStatetransitions, runnable without a browser.tests/api/— deterministic tests for each EditContext method and event, run on bothchromium-nativeandchromium-polyfill.tests/wpt/— ports of the Web Platform Tests for EditContext.tests/fuzz/— seeded fuzzers (pnpm test:fuzz) that run the same random action sequence on Chrome native and the polyfill, then compare final state and event logs. Includes an IME composition fuzzer (pnpm test:fuzz:ime) that uses CDPInput.imeSetComposition.
