Skip to content

(feat) Add inContentEditable property #220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ angular.module('myApp', ['cfp.hotkeys'])
- `callback`: The function to execute when the key(s) are pressed. Passes along two arguments, `event` and `hotkey`
- `action`: [OPTIONAL] The type of event to listen for, such as `keypress`, `keydown` or `keyup`. Usage of this parameter is discouraged as the underlying library will pick the most suitable option automatically. This should only be necessary in advanced situations.
- `allowIn`: [OPTIONAL] an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
- `inContentEditable`: [OPTIONAL] Wether to allow this combo in a contenteditable element. Defaults to false.

```js
hotkeys.add({
Expand Down Expand Up @@ -243,6 +244,19 @@ hotkeys.add({
});
```

### Allowing hotkeys in contenteditable elements
By default, Mousetrap prevents hotkey callbacks from firing when their event originates from an element with contenteditable="true". To enable hotkeys in these elements, you must set `inContentEditable` to `true`:
```js
hotkeys.add({
combo: 'ctrl+w',
description: 'Description goes here',
inContentEditable: true
callback: function(event, hotkey) {
event.preventDefault();
}
});
```

## Credits:

Muchas gracias to Craig Campbell for his [Mousetrap](https://github.com/ccampbell/mousetrap) library, which provides the underlying library for handling keyboard shortcuts.
60 changes: 32 additions & 28 deletions build/hotkeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,13 @@

// monkeypatch Mousetrap's stopCallback() function
// this version doesn't return true when the element is an INPUT, SELECT, or TEXTAREA
// (instead we will perform this check per-key in the _add() method)
// (instead we will perform this check per-key in the _add() method). Idem for contenteditable elements.
Mousetrap.prototype.stopCallback = function(event, element) {
if (!mouseTrapEnabled) {
return true;
}

// if the element has the class "mousetrap" then no need to stop
if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
return false;
}

return (element.contentEditable && element.contentEditable == 'true');
return false;
};

/**
Expand Down Expand Up @@ -142,14 +137,15 @@
/**
* Hotkey object used internally for consistency
*
* @param {array} combo The keycombo. it's an array to support multiple combos
* @param {String} description Description for the keycombo
* @param {Function} callback function to execute when keycombo pressed
* @param {string} action the type of event to listen for (for mousetrap)
* @param {array} allowIn an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
* @param {Boolean} persistent Whether the hotkey persists navigation events
* @param {array} combo The keycombo. it's an array to support multiple combos
* @param {String} description Description for the keycombo
* @param {Function} callback function to execute when keycombo pressed
* @param {string} action the type of event to listen for (for mousetrap)
* @param {array} allowIn an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
* @param {Boolean} persistent Whether the hotkey persists navigation events
* @param {boolean} inContentEditable Wether to allow combo in contenteditable elements
*/
function Hotkey (combo, description, callback, action, allowIn, persistent) {
function Hotkey (combo, description, callback, action, allowIn, persistent, inContentEditable) {
// TODO: Check that the values are sane because we could
// be trying to instantiate a new Hotkey with outside dev's
// supplied values
Expand All @@ -160,6 +156,7 @@
this.action = action;
this.allowIn = allowIn;
this.persistent = persistent;
this.inContentEditable = inContentEditable;
this._formated = null;
}

Expand Down Expand Up @@ -328,14 +325,15 @@
/**
* Creates a new Hotkey and creates the Mousetrap binding
*
* @param {string} combo mousetrap key binding
* @param {string} description description for the help menu
* @param {Function} callback method to call when key is pressed
* @param {string} action the type of event to listen for (for mousetrap)
* @param {array} allowIn an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
* @param {boolean} persistent if true, the binding is preserved upon route changes
* @param {string} combo mousetrap key binding
* @param {string} description description for the help menu
* @param {Function} callback method to call when key is pressed
* @param {string} action the type of event to listen for (for mousetrap)
* @param {array} allowIn an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
* @param {boolean} persistent if true, the binding is preserved upon route changes
* @param {boolean} inContentEditable Wether to allow combo in contenteditable elements
*/
function _add (combo, description, callback, action, allowIn, persistent) {
function _add (combo, description, callback, action, allowIn, persistent, inContentEditable) {

// used to save original callback for "allowIn" wrapping:
var _callback;
Expand All @@ -347,12 +345,13 @@
var objType = Object.prototype.toString.call(combo);

if (objType === '[object Object]') {
description = combo.description;
callback = combo.callback;
action = combo.action;
persistent = combo.persistent;
allowIn = combo.allowIn;
combo = combo.combo;
description = combo.description;
callback = combo.callback;
action = combo.action;
persistent = combo.persistent;
allowIn = combo.allowIn;
inContentEditable = combo.inContentEditable;
combo = combo.combo;
}

// no duplicates please
Expand Down Expand Up @@ -411,6 +410,11 @@
if ((' ' + target.className + ' ').indexOf(' mousetrap ') > -1) {
shouldExecute = true;
} else {
// By default prevent execution of callback if in contenteditable
if (target.contentEditable && target.contentEditable == 'true') {
shouldExecute = inContentEditable || false;
}

// don't execute callback if the event was fired from inside an element listed in preventIn
for (var i=0; i<preventIn.length; i++) {
if (preventIn[i] === nodeName) {
Expand Down Expand Up @@ -1050,7 +1054,7 @@
}

function _belongsTo(element, ancestor) {
if (element === document) {
if (element === null || element === document) {
return false;
}

Expand Down
Loading