Skip to content

Commit 1be5ca2

Browse files
committed
Add change event
1 parent b7654b7 commit 1be5ca2

File tree

4 files changed

+123
-5
lines changed

4 files changed

+123
-5
lines changed

spec/keyboard.spec.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,47 @@ describe('Keyboard', function() {
1919
expect(called).toEqual(1);
2020
});
2121

22-
});
22+
describe('notify "character" event', function() {
23+
24+
it('does not fire the event for a "left" key', function() {
25+
keyboard.on('character', function(event) {
26+
called += 1
27+
});
28+
29+
event.keyCode = Keyboard.key['left'];
30+
keyboard.dispatchKeyEvent(event, {}, true);
31+
expect(called).toEqual(0);
32+
});
33+
34+
it('does not fire the event for a "ctrl" key', function() {
35+
keyboard.on('character', function(event) {
36+
called += 1
37+
});
38+
39+
event.keyCode = Keyboard.key['ctrl'];
40+
keyboard.dispatchKeyEvent(event, {}, true);
41+
expect(called).toEqual(0);
42+
});
2343

44+
it('does fire the event for a "e" key', function() {
45+
keyboard.on('character', function(event) {
46+
called += 1
47+
});
48+
49+
event.keyCode = 'e'.charCodeAt(0);
50+
keyboard.dispatchKeyEvent(event, {}, true);
51+
expect(called).toEqual(1);
52+
});
53+
54+
it('does not fire the event for a "e" key without the notifyCharacterEvent param', function() {
55+
keyboard.on('character', function(event) {
56+
called += 1
57+
});
58+
59+
event.keyCode = 'e'.charCodeAt(0);
60+
keyboard.dispatchKeyEvent(event, {}, false);
61+
expect(called).toEqual(0);
62+
});
63+
});
64+
});
2465
});

src/core.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,19 @@ Editable.prototype.empty = function(handler) {
372372
return this.on('empty', handler);
373373
};
374374

375+
/**
376+
* Subscribe to the {{#crossLink "Editable/change:event"}}{{/crossLink}}
377+
* event.
378+
*
379+
* @method change
380+
* @param {Function} handler The callback to execute in response to the
381+
* event.
382+
* @chainable
383+
*/
384+
Editable.prototype.change = function(handler) {
385+
return this.on('change', handler);
386+
};
387+
375388
/**
376389
* Subscribe to the {{#crossLink "Editable/switch:event"}}{{/crossLink}}
377390
* event.

src/dispatcher.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
var Dispatcher = function(editable) {
99
var win = editable.win;
1010
eventable(this, editable);
11+
this.supportsInputEvent = false;
1112
this.$document = $(win.document);
1213
this.config = editable.config;
1314
this.editable = editable;
@@ -17,6 +18,10 @@ var Dispatcher = function(editable) {
1718
this.setup();
1819
};
1920

21+
// This will be set to true once we detect the input event is working.
22+
// Input event description on MDN:
23+
// https://developer.mozilla.org/en-US/docs/Web/Reference/Events/input
24+
var isInputEventSupported = false;
2025

2126
/**
2227
* Sets up all events that Editable.JS is catching.
@@ -61,12 +66,44 @@ Dispatcher.prototype.setupElementEvents = function() {
6166
}).on('cut.editable', _this.editableSelector, function(event) {
6267
log('Cut');
6368
_this.notify('clipboard', this, 'cut', _this.selectionWatcher.getFreshSelection());
69+
_this.triggerChangeEvent(this);
6470
}).on('paste.editable', _this.editableSelector, function(event) {
6571
log('Paste');
6672
_this.notify('clipboard', this, 'paste', _this.selectionWatcher.getFreshSelection());
73+
_this.triggerChangeEvent(this);
74+
}).on('input.editable', _this.editableSelector, function(event) {
75+
log('Input');
76+
if (isInputEventSupported) {
77+
_this.notify('change', this);
78+
} else {
79+
// Most likely the event was already handled manually by
80+
// triggerChangeEvent so the first time we just switch the
81+
// isInputEventSupported flag without notifiying the change event.
82+
isInputEventSupported = true;
83+
}
6784
});
6885
};
6986

87+
/**
88+
* Trigger a change event
89+
*
90+
* This should be done in these cases:
91+
* - typing a letter
92+
* - delete (backspace and delete keys)
93+
* - cut
94+
* - paste
95+
* - copy and paste (not easily possible manually as far as I know)
96+
*
97+
* Preferrably this is done using the input event. But the input event is not
98+
* supported on all browsers for contenteditable elements.
99+
* To make things worse it is not detectable either. So instead of detecting
100+
* we set 'isInputEventSupported' when the input event fires the first time.
101+
*/
102+
Dispatcher.prototype.triggerChangeEvent = function(target){
103+
if (isInputEventSupported) return;
104+
this.notify('change', target);
105+
};
106+
70107
Dispatcher.prototype.dispatchSwitchEvent = function(event, element, direction) {
71108
var cursor;
72109
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
@@ -99,7 +136,8 @@ Dispatcher.prototype.setupKeyboardEvents = function() {
99136
var _this = this;
100137

101138
this.$document.on('keydown.editable', this.editableSelector, function(event) {
102-
_this.keyboard.dispatchKeyEvent(event, this);
139+
var notifyCharacterEvent = !isInputEventSupported;
140+
_this.keyboard.dispatchKeyEvent(event, this, notifyCharacterEvent);
103141
});
104142

105143
this.keyboard.on('left', function(event) {
@@ -130,7 +168,11 @@ Dispatcher.prototype.setupKeyboardEvents = function() {
130168
event.preventDefault();
131169
event.stopPropagation();
132170
_this.notify('merge', this, 'before', cursor);
171+
} else {
172+
_this.triggerChangeEvent(this);
133173
}
174+
} else {
175+
_this.triggerChangeEvent(this);
134176
}
135177
}).on('delete', function(event) {
136178
log('Delete key pressed');
@@ -142,7 +184,11 @@ Dispatcher.prototype.setupKeyboardEvents = function() {
142184
event.preventDefault();
143185
event.stopPropagation();
144186
_this.notify('merge', this, 'after', cursor);
187+
} else {
188+
_this.triggerChangeEvent(this);
145189
}
190+
} else {
191+
_this.triggerChangeEvent(this);
146192
}
147193
}).on('enter', function(event) {
148194
log('Enter key pressed');
@@ -165,6 +211,8 @@ Dispatcher.prototype.setupKeyboardEvents = function() {
165211
event.stopPropagation();
166212
var cursor = _this.selectionWatcher.forceCursor();
167213
_this.notify('newline', this, cursor);
214+
}).on('character', function(event) {
215+
_this.notify('change', this);
168216
});
169217
};
170218

src/keyboard.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var Keyboard = function() {
66
eventable(this);
77
};
88

9-
Keyboard.prototype.dispatchKeyEvent = function(event, target) {
9+
Keyboard.prototype.dispatchKeyEvent = function(event, target, notifyCharacterEvent) {
1010
switch (event.keyCode) {
1111

1212
case this.key.left:
@@ -52,7 +52,20 @@ Keyboard.prototype.dispatchKeyEvent = function(event, target) {
5252
this.notify(target, 'enter', event);
5353
}
5454
break;
55-
55+
case this.key.ctrl:
56+
case this.key.shift:
57+
case this.key.alt:
58+
break;
59+
// Metakey
60+
case 224: // Firefox: 224
61+
case 17: // Opera: 17
62+
case 91: // Chrome/Safari: 91 (Left)
63+
case 93: // Chrome/Safari: 93 (Right)
64+
break;
65+
default:
66+
if (notifyCharacterEvent) {
67+
this.notify(target, 'character', event);
68+
}
5669
}
5770
};
5871

@@ -65,7 +78,10 @@ Keyboard.prototype.key = {
6578
esc: 27,
6679
backspace: 8,
6780
'delete': 46,
68-
enter: 13
81+
enter: 13,
82+
shift: 16,
83+
ctrl: 17,
84+
alt: 18
6985
};
7086

7187
Keyboard.key = Keyboard.prototype.key;

0 commit comments

Comments
 (0)