How to add useful hotkeys in the editor with JS in WordPress

Contents

Introduction

This article explains, in exhaustive detail, how to add useful keyboard hotkeys to the WordPress editor using JavaScript. It covers both editors currently in use—the Classic Editor (TinyMCE) and the Block Editor (Gutenberg)—and gives implementation options, best practices, PHP enqueue examples, multiple concrete JS snippets, accessibility considerations, conflicts to watch out for, and steps for debugging and testing.

Overview: Which editor are you targeting?

  • Classic Editor (TinyMCE): If your site uses the classic editor or the TinyMCE instance (plugin, custom meta boxes, or the Classic Editor plugin), you can use TinyMCE APIs like editor.addShortcut, the Command API, or listen inside the editors iframe.
  • Block Editor (Gutenberg): If youre working in the block editor you can either register shortcuts using WordPress packages and components (when available) or use robust, safe vanilla JavaScript key event handling that cooperates with Gutenbergs stores via wp.data.dispatch (for saving, inserting blocks, etc.).

Design considerations before you add hotkeys

  • Context awareness — Only run hotkey handlers on relevant screens (post-editor) and only when the user is not typing into a text field or rich-editable region unless that is the target.
  • Modifier key normalization — Mac users use Command (meta), Windows/Linux users use Ctrl. Use checks for metaKey and ctrlKey appropriately or use a library that normalizes with a mod alias.
  • Avoid overriding existing shortcuts — Common browser and WordPress shortcuts (Ctrl/Cmd S, Ctrl/Cmd Z, Ctrl/Cmd B) are busy — either use the same behavior or choose unique combos with additional modifiers (alt/shift) after checking existing defaults in your environment.
  • Accessibility — Ensure that keyboard handlers dont prevent navigation or break screen readers. Prefer using ARIA or visual affordances if your hotkey triggers an important UI change.
  • International keyboards consistency — Key positions and labels may vary by layout relying on e.key (string) is usually better than keyCode, but test in target locales.
  • Cleanup — Remove listeners on unload or when the editor unmounts to avoid memory leaks or duplicated behavior.

Implementation options at a glance

  1. TinyMCE API — Use editor.addShortcut and editor.addCommand. Works inside the TinyMCE instance and binds directly to the editor iframe.
  2. Window/document-level keydown — Add a keydown listener in the admin editor page and route actions via wp.data dispatches or DOM manipulation. Use this for Gutenberg or universal hotkeys.
  3. Third-party keyboard libraries — Use Mousetrap or hotkeys.js for easier combos and normalization enqueue them as dependencies then bind handlers.
  4. Gutenberg integrations — Use wp.data dispatch/select to operate on the editor (insert blocks, save) and optionally register an editor plugin UI with registerPlugin if you need a visual cue.

Safe keyboard detection utilities (recommended)

Below is a compact, robust helper to determine if the user is typing and a normalized key combo generator. This is the foundation to avoid interfering while the user types in inputs or rich-editable areas.

/
  isTypingInInput(e)
  Returns true if the active element is an input, textarea, select, or contentEditable
 /
function isTypingInInput(e) {
    var el = document.activeElement
    if (!el) return false
    var tag = (el.tagName  ).toUpperCase()
    if (tag === INPUT  tag === TEXTAREA  tag === SELECT) return true
    if (el.isContentEditable) return true
    return false
}

/
  normalizeKeyEvent(e)
  Returns normalized combo like ctrl shift s or meta alt p
 /
function normalizeKeyEvent(e) {
    var keys = []
    if (e.ctrlKey) keys.push(ctrl)
    if (e.metaKey) keys.push(meta)
    if (e.altKey) keys.push(alt)
    if (e.shiftKey) keys.push(shift)

    // Use e.key when available, fallback to e.code or keyCode mapping if needed.
    var key = (e.key  ).toLowerCase()
    // handle single-character keys and some named keys consistently
    if (key ===  ) key = space
    keys.push(key)
    return keys.join( )
}

Practical example: Gutenberg (Block Editor) — Add hotkeys that save and insert a paragraph

This example demonstrates how to add two hotkeys inside the Block Editor:

  • Ctrl/Cmd S -> Save post (intercepts default to route through WP editor saving).
  • Ctrl/Cmd Alt P -> Insert a new paragraph block at the current selection.

Enqueue this script using the proper PHP hook (example after the JS snippet).

(function (wp) {
    var dispatch = wp.data.dispatch
    var createBlock = wp.blocks.createBlock

    function isTypingInInputLocal() {
        var el = document.activeElement
        if (!el) return false
        var tag = (el.tagName  ).toUpperCase()
        if (tag === INPUT  tag === TEXTAREA  tag === SELECT) return true
        if (el.isContentEditable) return true
        return false
    }

    function normalizeKeyEvent(e) {
        var keys = []
        if (e.ctrlKey) keys.push(ctrl)
        if (e.metaKey) keys.push(meta)
        if (e.altKey) keys.push(alt)
        if (e.shiftKey) keys.push(shift)
        var key = (e.key  ).toLowerCase()
        if (key ===  ) key = space
        keys.push(key)
        return keys.join( )
    }

    function onKeyDown(e) {
        // Dont interfere when the user is typing into an input or contentEditable
        if (isTypingInInputLocal(e)) return

        var combo = normalizeKeyEvent(e)

        // Ctrl/Cmd S -> Save post
        if (combo === ctrl s  combo === meta s) {
            e.preventDefault()
            // Save via core editor store
            if (dispatch  dispatch(core/editor)  typeof dispatch(core/editor).savePost === function) {
                dispatch(core/editor).savePost()
            }
            return
        }

        // Ctrl/Cmd Alt P -> insert paragraph block (non-destructive demo)
        if (combo === ctrl alt p  combo === meta alt p) {
            e.preventDefault()
            var paragraph = createBlock(core/paragraph, { content: Inserted via hotkey })
            // Insert using block-editor store
            if (dispatch  dispatch(core/block-editor)  typeof dispatch(core/block-editor).insertBlocks === function) {
                dispatch(core/block-editor).insertBlocks(paragraph)
            }
            return
        }
    }

    // Register on ready. Using capture ensures editor key events are handled first.
    if (wp  wp.domReady) {
        wp.domReady(function () {
            window.addEventListener(keydown, onKeyDown, true)
            // cleanup if necessary: window.removeEventListener(keydown, onKeyDown, true)
        })
    }
})(window.wp)

PHP: Enqueue the Gutenberg hotkeys script


Practical example: Classic Editor (TinyMCE) — Use TinyMCEs shortcut API

TinyMCE exposes APIs to add commands and shortcuts directly inside the editor. Below is a minimal plugin-style snippet that adds two hotkeys:

  • Ctrl/Cmd Alt L -> Insert Lorem Ipsum paragraph
  • Ctrl/Cmd Shift N -> Insert a Note block via a custom command
(function() {
    function initEditorShortcuts(editor) {
        if (!editor) return

        // Simple addShortcut call: addShortcut(shortcut, desc, callback or command)
        editor.addShortcut(ctrl alt l, Insert Lorem, function() {
            editor.insertContent(

Lorem ipsum dolor sit amet, inserted via hotkey.

) }) // Define a command and bind a shortcut to it (string name of the command) editor.addCommand(mceInsertNote, function() { editor.insertContent(

Note: this note was inserted via keyboard.

) }) editor.addShortcut(ctrl shift n, Insert Note, mceInsertNote) // Alternatively, listen to low-level keydown inside editor editor.on(keydown, function(e) { // Example: if you need deeper control, inspect the event and call e.preventDefault() // This probably isnt necessary if addShortcut covers your case. }) } // Wait for TinyMCE to be ready if (typeof tinymce !== undefined) { tinymce.on(AddEditor, function(e) { if (e e.editor) { initEditorShortcuts(e.editor) } }) // If the activeEditor exists on load, initialize immediately if (tinymce.activeEditor) { initEditorShortcuts(tinymce.activeEditor) } } })()

PHP: Enqueue TinyMCE script only on the Classic Editor screen

base !== post ) {
        return
    }

    wp_enqueue_script(
        my-hotkeys-tinymce,
        plugin_dir_url(__FILE__) . js/hotkeys-tinymce.js,
        array( jquery ),
        filemtime( plugin_dir_path(__FILE__) . js/hotkeys-tinymce.js ),
        true
    )
}
add_action( admin_enqueue_scripts, my_hotkeys_enqueue_classic_editor )
?>

Alternative: Use a small library (Mousetrap) for easier combos

Libraries like Mousetrap normalize modifier keys and provide simple string combos such as mod s (where mod = Ctrl on Windows/Linux and Cmd on Mac). You can enqueue Mousetrap and then bind to combinations. This is particularly handy when you need flexible multi-OS handling with less code.

// Example using Mousetrap (assumes Mousetrap is enqueued)
Mousetrap.bind(mod s, function(e) {
    e.preventDefault()
    // For Gutenberg, save via wp.data
    if (window.wp  wp.data  wp.data.dispatch) {
        if (typeof wp.data.dispatch(core/editor).savePost === function) {
            wp.data.dispatch(core/editor).savePost()
        }
    } else {
        // Fallback: try triggering native form submit or other save action
    }
})

Testing and debugging hotkeys

  • Test on Mac and Windows to ensure meta/ctrl normalization works.
  • Check conflicts: open console and verify no JavaScript errors when handlers run.
  • Test in different browser contexts (iframes, plugin sidebars). For TinyMCE the keydown can occur inside the iframe using the editor API avoids scoping issues.
  • Use console logging with combo output to validate your normalization function during development.

Best practices and recommendations

  • Prefer editor APIs where available (TinyMCE addShortcut, Gutenberg wp.data) to minimize conflicts and ensure actions operate within the editors state.
  • Disable or adjust hotkeys when a modal, dialog, or typeable UI control is focused.
  • Document custom hotkeys for users — if you add persistent hotkeys consider adding a small help panel or plugin settings page explaining them.
  • Keep hotkey handlers minimal and fast avoid expensive DOM queries inside the keydown handler.
  • Use event capture or passive options carefully if you need to preventDefault, attach the listener with capture to intercept before other handlers, but be mindful of side effects.

Security and performance notes

  • Do not read or store arbitrary user-entered content unnecessarily in your hotkey handler if you need content, use safe APIs (wp.data.select or TinyMCE API) rather than raw DOM hacks where possible.
  • Prefer non-blocking code in handlers (dispatch actions, but avoid long synchronous loops).
  • If enqueuing third-party libraries, ensure they are trusted and updated to avoid supply-chain problems.

Common pitfalls and how to avoid them

  • Hotkeys firing when typing: Use checks for contentEditable/inputs and avoid global handlers or scope them carefully.
  • Duplicate handlers: Ensure listeners are registered once. If your script may be loaded multiple times, guard using a global flag or remove existing listeners before adding new ones.
  • Inconsistent modifier behavior on Mac: Use metaKey checks and normalize combos (or use library alias mod).
  • Missing dependencies in enqueues: Always declare WordPress script dependencies like wp-data, wp-dom-ready, wp-blocks if your script uses them.

Recommended key combos table (guidance)

Action Suggested Combo Notes
Save post Ctrl/Cmd S (mod s) Already expected by many users. Route to WP save behavior.
Insert predefined paragraph Ctrl/Cmd Alt P Relatively safe uncommon conflict.
Insert note Ctrl/Cmd Shift N Shift helps avoid common browser combos.
Open custom panel Ctrl/Cmd Alt K Choose combos unlikely to conflict with browser or WP core.

Complete example: small plugin skeleton (files flow)

  1. Create a plugin folder and add a PHP bootstrap file that enqueues a block-editor JS file using enqueue_block_editor_assets.
  2. Create the JS file that attaches a keydown listener (or uses a library) and dispatches to wp.data or TinyMCE as appropriate.
  3. Test on post edit and new post screens in admin. Verify save and insert behaviors.
  4. Optionally provide an admin settings page to allow site admins to toggle shortcuts or change combos.

Minimal quick checklist before launch

  • Make sure enqueues run only on editor screens.
  • Ensure no JavaScript console errors.
  • Validate hotkey behavior on multiple OS/browsers.
  • Confirm handlers dont interrupt typing or core editor shortcuts.
  • Document the hotkeys in UI or a basic help popup.

Final notes

Adding hotkeys to the WordPress editor is a powerful way to boost editing speed, but it must be done responsibly: scope the behavior, avoid conflicts, consider accessibility, and rely on editor APIs when possible. The code examples above include safe normalization, scoping, and direct usage of WordPress editor stores/APIs so you can plug them into your plugin or theme. Use the TinyMCE approach for classic editors and the window/wp.data approach for Gutenberg, and consider user settings for enabling/disabling custom shortcuts.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

Your email address will not be published. Required fields are marked *