diff --git a/core/selection.js b/core/selection.js index 93cbd32cfb..1730688a44 100644 --- a/core/selection.js +++ b/core/selection.js @@ -4,6 +4,7 @@ import equal from 'deep-equal'; import Emitter from './emitter'; import logger from './logger'; import './shadow-selection-polyfill'; +import { ShadowSelection } from './shadow-selection-polyfill'; const debug = logger('quill:selection'); @@ -33,9 +34,16 @@ class Selection { setTimeout(this.update.bind(this, Emitter.sources.USER), 1); } }); - this.emitter.on(Emitter.events.SCROLL_BEFORE_UPDATE, () => { + this.emitter.on(Emitter.events.SCROLL_BEFORE_UPDATE, (_, mutations) => { if (!this.hasFocus()) return; const native = this.getNativeRange(); + // We might need to hack the offset on Safari, when we are dealing with the first character of a row. + // This likely happens because of a race condition between quill's update method being called before the + // selectionchange event being fired in the selection polyfill. + const hackOffset = (native.start.offset === 0 && + native.start.offset === native.end.offset && + this.rootDocument.getSelection() instanceof ShadowSelection && + mutations.some((a) => a.type === 'characterData' && a.oldValue === '')) ? 1 : 0; if (native == null) return; if (native.start.node === this.cursor.textNode) return; // cursor.restore() will handle this.emitter.once(Emitter.events.SCROLL_UPDATE, () => { @@ -46,9 +54,9 @@ class Selection { ) { this.setNativeRange( native.start.node, - native.start.offset, + native.start.offset + hackOffset, native.end.node, - native.end.offset, + native.end.offset + hackOffset, ); } this.update(Emitter.sources.SILENT); diff --git a/core/shadow-selection-polyfill.js b/core/shadow-selection-polyfill.js index 3593ee4798..a67c11eeef 100644 --- a/core/shadow-selection-polyfill.js +++ b/core/shadow-selection-polyfill.js @@ -4,7 +4,7 @@ const SUPPORTS_BEFORE_INPUT = typeof window.InputEvent.prototype.getTargetRanges const IS_FIREFOX = window.navigator.userAgent.toLowerCase().indexOf('firefox') > -1; let processing = false; -class ShadowSelection { +export class ShadowSelection { constructor() { this._ranges = []; }