Skip to content

Commit 3961e3a

Browse files
committed
fix(events): sync ranges on focus
1 parent c2107cc commit 3961e3a

File tree

3 files changed

+99
-3
lines changed

3 files changed

+99
-3
lines changed

spec/create-default-event.spec.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import $ from 'jquery'
2+
import rangy from 'rangy'
3+
4+
import Cursor from '../src/cursor'
5+
import Editable from '../src/core'
6+
7+
describe('Default Events', () => {
8+
let $elem, editable, focus, blur
9+
10+
// create a Cursor object and set the selection to it
11+
function createCursor (range) {
12+
const cursor = new Cursor($elem[0], range)
13+
cursor.setSelection()
14+
return cursor
15+
}
16+
17+
function createRangeAtEnd (node) {
18+
const range = rangy.createRange()
19+
range.selectNodeContents(node)
20+
range.collapse(false)
21+
return range
22+
}
23+
24+
let onListener
25+
// register one listener per test
26+
function on (eventName, func) {
27+
// off() // make sure the last listener is unregistered
28+
const obj = { calls: 0 }
29+
function proxy () {
30+
obj.calls += 1
31+
func.apply(this, arguments)
32+
}
33+
onListener = { event: eventName, listener: proxy }
34+
editable.on(eventName, proxy)
35+
return obj
36+
}
37+
38+
// unregister the event listener registered with 'on'
39+
function off () {
40+
if (onListener) {
41+
editable.unload()
42+
onListener = undefined
43+
}
44+
}
45+
46+
describe('for editable', () => {
47+
beforeEach(() => {
48+
$elem = $('<div contenteditable="true"></div>')
49+
$(document.body).append($elem)
50+
editable = new Editable()
51+
editable.add($elem)
52+
$elem.focus()
53+
})
54+
55+
afterEach(() => {
56+
off()
57+
editable.dispatcher.off()
58+
$elem.remove()
59+
})
60+
61+
describe('on focus', () => {
62+
beforeEach(() => {
63+
focus = $.Event('focus')
64+
blur = $.Event('blur')
65+
})
66+
67+
it('always dispatches with virtual and native ranges in sync.', () => {
68+
// <div>foo\</div>
69+
$elem.html('foo')
70+
createCursor(createRangeAtEnd($elem[0]))
71+
72+
const onFocus = on('focus', (element, selection) => {
73+
if (!selection) return
74+
expect(element).toEqual($elem[0])
75+
const range = selection.range
76+
const nativeRange = range.nativeRange
77+
expect(range.startContainer).toEqual(nativeRange.startContainer)
78+
expect(range.endContainer).toEqual(nativeRange.endContainer)
79+
expect(range.startOffset).toEqual(nativeRange.startOffset)
80+
expect(range.endOffset).toEqual(nativeRange.endOffset)
81+
})
82+
83+
$elem.trigger(focus)
84+
$elem.trigger(blur)
85+
$elem.trigger(focus)
86+
expect(onFocus.calls).toEqual(2)
87+
})
88+
})
89+
})
90+
})

src/create-default-events.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ export default function createDefaultEvents (editable) {
1111
* @event focus
1212
* @param {HTMLElement} element The element triggering the event.
1313
*/
14-
focus (element) {
14+
focus (element, selection) {
15+
console.log(element, selection)
1516
behavior.focus(element)
17+
behavior.selection(element, selection)
1618
},
1719

1820
/**
@@ -35,7 +37,10 @@ export default function createDefaultEvents (editable) {
3537
* @param {String} action The flow action: "start" or "pause".
3638
*/
3739
flow (element, action) {
38-
behavior.flow(element, action)
40+
behavior.flow(
41+
element,
42+
action
43+
)
3944
},
4045

4146
/**

src/dispatcher.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ export default class Dispatcher {
8484
this.$document
8585
.on('focus.editable', selector, function (event) {
8686
if (this.getAttribute(config.pastingAttribute)) return
87-
self.notify('focus', this)
87+
const selection = self.selectionWatcher.getFreshSelection()
88+
self.notify('focus', this, selection)
8889
})
8990

9091
.on('blur.editable', selector, function (event) {

0 commit comments

Comments
 (0)