diff --git a/src/index.ts b/src/index.ts
index fede7e6..9068ba9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -25,7 +25,13 @@ function keyDownHandler(event: KeyboardEvent) {
if (isFormField(event.target)) {
const target = event.target as HTMLElement
if (!target.id) return
- if (!target.ownerDocument.querySelector(`[data-hotkey-scope="${target.id}"]`)) return
+ const hasScope =
+ typeof CSS !== 'undefined' && CSS.escape
+ ? target.ownerDocument.querySelector(`[data-hotkey-scope="${CSS.escape(target.id)}"]`)
+ : Array.from(target.ownerDocument.querySelectorAll('[data-hotkey-scope]')).some(
+ element => element.getAttribute('data-hotkey-scope') === target.id
+ )
+ if (!hasScope) return
}
// If the user presses a hotkey that doesn't exist in the Trie,
diff --git a/test/test.js b/test/test.js
index d26d497..7478321 100644
--- a/test/test.js
+++ b/test/test.js
@@ -173,6 +173,34 @@ describe('hotkey', function () {
})
describe('data-hotkey-scope', function () {
+ it('matches scoped form fields without CSS.escape', function () {
+ setHTML('')
+ const field = document.getElementById('textfield')
+ field.id = 'field"one'
+ document.getElementById('button1').setAttribute('data-hotkey-scope', field.id)
+ const original = CSS.escape
+ try {
+ CSS.escape = undefined
+ field.dispatchEvent(new KeyboardEvent('keydown', {bubbles: true, key: 'b', metaKey: true}))
+ expect(elementsActivated).toEqual(['button1'])
+ } finally {
+ CSS.escape = original
+ }
+ })
+
+ it.each(['field"one', 'field\\one'])('matches a scope whose ID contains CSS escapes: %s', function (id) {
+ setHTML(`
+
+ `)
+ const field = document.getElementById('textfield')
+ field.id = id
+ document.getElementById('button1').setAttribute('data-hotkey-scope', id)
+
+ field.dispatchEvent(new KeyboardEvent('keydown', {bubbles: true, key: 'b', metaKey: true}))
+
+ expect(elementsActivated).toEqual(['button1'])
+ })
+
it('allows hotkey action from form field', function () {
setHTML(`