Contents
Introducción
En este tutorial detallado aprenderás a añadir hotkeys (atajos de teclado) útiles en el editor de WordPress usando JavaScript. Veremos dos entornos principales: el editor clásico (TinyMCE y Quicktags) y el editor de bloques (Gutenberg). Incluyo ejemplos completos de código para encolar los scripts en el administrador, para registrar atajos en ambos editores y buenas prácticas para evitar conflictos y respetar accesibilidad.
Resumen de la estrategia
- Encolar un script JS solo en las pantallas de edición (post-new.php / post.php).
- Detectar el tipo de editor activo (clásico o Gutenberg) y registrar los atajos de forma no intrusiva.
- Comprobar el foco y los contextos donde deben funcionar los atajos para evitar interferir con otros campos.
- Manejar combinaciones comunes (Ctrl/Cmd tecla) y permitir personalización o desactivación.
Preparación: encolar scripts en el admin
Primero, añadiremos el código PHP necesario para encolar el script solo en las pantallas de edición. Este fragmento se puede incluir en el functions.php del tema o en un plugin.
function_exists( is_gutenberg_page ) ? true : false, ) ) } add_action( admin_enqueue_scripts, mi_plugin_enqueue_editor_hotkeys ) ?>
En el editor clásico hay dos modos principales: el editor visual basado en TinyMCE y el editor de texto (Quicktags). La técnica más segura es registrar listeners de teclado en el iframe del editor de TinyMCE o usar la API de TinyMCE cuando esté disponible, además de añadir un listener para el textarea del modo texto/HTML.
Ejemplo: Hotkey para aplicar negrita en TinyMCE y en modo texto
El siguiente ejemplo añade Ctrl/Cmd B para alternar negrita en ambos modos.
// mi-editor-hotkeys.js (parte para el editor clásico) (function() { // Helpers function isMac() { return /MaciPodiPhoneiPad/.test(navigator.platform) } function isEditorPage() { // Si necesitas comprobaciones extra, agrégalas aquí return true } // TinyMCE: cuando está inicializado if ( typeof tinymce !== undefined ) { tinymce.PluginManager.add(mi_hotkeys_plugin, function(editor) { editor.on(keydown, function(e) { var key = e.key String.fromCharCode(e.keyCode).toLowerCase() var ctrlOrCmd = e.ctrlKey e.metaKey if ( ctrlOrCmd key.toLowerCase() === b ) { e.preventDefault() // Ejecutar comando de TinyMCE para negrita editor.execCommand(Bold) } }) }) // Asegurar que el plugin se cargue en la inicialización de tinymce if ( tinymce.editors ) { for ( var id in tinymce.editors ) { if ( tinymce.editors.hasOwnProperty(id) ) { var ed = tinymce.editors[id] // Registrar el plugin en ed ed.on(BeforeExecCommand, function() {}) } } } } // Quicktags / textarea (modo texto) document.addEventListener(keydown, function(e) { var active = document.activeElement if (!active) return // Comprueba si estamos en un textarea del editor (modo texto) if ( active.tagName === TEXTAREA ( active.id === content active.classList.contains(wp-editor-area) ) ) { var key = e.key String.fromCharCode(e.keyCode).toLowerCase() var ctrlOrCmd = e.ctrlKey e.metaKey if ( ctrlOrCmd key.toLowerCase() === b ) { e.preventDefault() var start = active.selectionStart var end = active.selectionEnd var selected = active.value.substring(start, end) var before = active.value.substring(0, start) var after = active.value.substring(end) // Si ya está entre , quita si no, añade if ( selected.indexOf() === 0 selected.lastIndexOf() === selected.length-2 ) { selected = selected.substring(2, selected.length-2) } else { selected = selected } active.value = before selected after // restaurar selección active.selectionStart = start active.selectionEnd = start selected.length } } }, false) })()
Notas:
- Registrar un plugin TinyMCE de la forma oficial es más robusto si controlas la inicialización de TinyMCE. Si no, usar el manejador de eventos keydown también funciona.
- En el ejemplo de Quicktags hacemos una manipulación simple de texto Markdown/estrella para demostrar el comportamiento adáptalo según tu convención.
Editor de Bloques (Gutenberg)
Gutenberg es un entorno React. Lo más simple y compatible es añadir un listener global de teclado que actúe solo cuando el foco esté dentro de las áreas editables del editor de bloques. Evita interferir cuando el usuario esté en inputs, selects, modales o cuando una caja de diálogo tenga el foco.
Patrones seguros en Gutenberg
- Verificar que document.activeElement esté dentro de las áreas con clase conocidas de Gutenberg (por ejemplo, editor-styles-wrapper, block-editor, wp-block-editor__editable, editor-post-title__input).
- Evitar atajos que usen teclas que ya maneja Gutenberg (por ejemplo, Ctrl S para guardar está ya reservado por el navegador/WordPress).
- Usar los paquetes de WordPress si desarrollas un plugin React para un script simple encolado, la comprobación del DOM es suficiente.
Ejemplo: atajos en Gutenberg (insertar párrafo y alternar negrita con Ctrl/Cmd B)
// mi-editor-hotkeys.js (parte para Gutenberg) (function() { function isEditableGutenbergTarget( el ) { if (!el) return false // Verifica elementos con las clases que Gutenberg usa para áreas editables return !!el.closest(.editor-styles-wrapper, .wp-block-editor__editable, .editor-post-title__input, .block-editor-rich-text__editable) } window.addEventListener(keydown, function(e) { var key = e.key ? e.key.toLowerCase() : String.fromCharCode(e.keyCode).toLowerCase() var ctrlOrCmd = e.ctrlKey e.metaKey var active = document.activeElement // No interferir cuando el usuario está en un input, textarea, select o contentEditable no deseado if ( active ( active.tagName === INPUT active.tagName === SELECT active.tagName === TEXTAREA ) ) { return } // Verifica que el foco esté en el editor Gutenberg if ( !isEditableGutenbergTarget( active ) ) { return } // Ctrl/Cmd B -> alternar negrita en el bloque de texto actual if ( ctrlOrCmd key === b ) { e.preventDefault() // Intentamos simular Cmd/Ctrl B disparando el comando de formato // En Gutenberg el editor escucha atajos nativos, pero si necesitas forzar: // Intentar ejecutar el comando que el editor entiende (foco en editable) document.execCommand(bold) // funcionará en la mayoría de contentEditable return } // Alt Shift P -> insertar párrafo debajo (ejemplo de hotkey personalizado) if ( e.altKey e.shiftKey key === p ) { e.preventDefault() // Crear un nuevo bloque párrafo al final o después del bloque actual // La forma robusta es usar la data API de WP si está disponible if ( window.wp wp.data wp.data.dispatch ) { try { var blocks = wp.data.select(core/block-editor) wp.data.select(core/editor) var dispatch = wp.data.dispatch(core/block-editor) wp.data.dispatch(core/editor) if ( blocks dispatch dispatch.insertBlocks ) { var paragraph = wp.blocks.createBlock(core/paragraph, { content: }) dispatch.insertBlocks(paragraph) return } } catch (err) { // Fallback simple: insertar un salto de línea en el contentEditable activo var sel = window.getSelection() if (sel sel.rangeCount) { var range = sel.getRangeAt(0) var br = document.createElement(p) br.innerHTML =
range.collapse(false) range.insertNode(br) // Mover cursor dentro del nuevo párrafo range.setStart(br, 0) sel.removeAllRanges() sel.addRange(range) } } } } }, false) })()
Notas sobre este ejemplo:
- document.execCommand(bold) sigue funcionando en contentEditable y es una vía rápida para alternar negrita si el foco está en un rich text. Para integraciones profundas, usa la API de datos de Gutenberg (wp.data) y los bloques (wp.blocks).
- El ejemplo muestra un fallback si las APIs de wp. no están disponibles.
Buenas prácticas y compatibilidad
- Restringir el alcance: Ejecuta los atajos solo cuando el foco está en el editor para no colisionar con atajos en el admin o en otros plugins.
- Usar combinaciones estándar: Preferir Ctrl/Cmd letra para acciones comunes usar combinaciones con Alt/Shift para atajos menos habituales.
- Permitir desactivar: Si distribuyes el código, ofrece una opción para desactivar los atajos o para personalizar combinaciones.
- Detectar plataforma: Ten en cuenta Mac (Meta/Command) vs Windows/Linux (Control).
- Evitar conflictos: Antes de elegir un atajo, comprueba atajos nativos del navegador y de WordPress (por ejemplo, Ctrl S, Ctrl Z, Ctrl Y).
- Accesibilidad: Mantén la posibilidad de usar la interfaz con teclado estándar y evitar interferir con tecnologías de asistencia.
Lista de hotkeys útiles sugeridas
- Ctrl/Cmd B — Negrita
- Ctrl/Cmd I — Cursiva
- Ctrl/Cmd K — Insertar/editar enlace
- Alt Shift P — Insertar párrafo/crear nuevo bloque párrafo (Gutenberg)
- Alt Shift H — Mostrar ayuda o lista de atajos personalizada
- Ctrl/Cmd Alt M — Insertar una plantilla o bloque personalizado
Ejemplo completo: pequeño plugin que añade atajos
A continuación tienes un ejemplo de plugin mínimo que encola un JS y contiene la lógica para ambos editores. Guarda el archivo PHP en una carpeta de plugin y crea el archivo JS en la subcarpeta js/.
// js/meh-hotkeys.js (function() { // Detección simple de plataforma var isMac = /MaciPodiPhoneiPad/.test(navigator.platform) // Comprueba si el elemento activo pertenece al editor de bloques function isGutenbergEditable(el) { return !!el !!el.closest !!el.closest(.editor-styles-wrapper, .wp-block-editor__editable, .editor-post-title__input, .block-editor-rich-text__editable) } function handleGlobalKeydown(e) { var key = e.key ? e.key.toLowerCase() : String.fromCharCode(e.keyCode).toLowerCase() var ctrlOrCmd = e.ctrlKey e.metaKey var active = document.activeElement // No interferir con inputs o selects if (active (active.tagName === INPUT active.tagName === SELECT active.tagName === TEXTAREA)) { return } // Ctrl/Cmd B -> negrita if (ctrlOrCmd key === b) { // Solo si el foco está en el editor clásico o Gutenberg var inClassicTextArea = active active.tagName === TEXTAREA (active.id === content active.classList.contains(wp-editor-area)) var inTiny = (window.tinymce window.tinymce.activeEditor window.tinymce.activeEditor.targetElm window.tinymce.activeEditor.targetElm.contains(active)) var inGutenberg = isGutenbergEditable(active) if (inClassicTextArea) { e.preventDefault() // Lógica simple para envolver con var ta = active var start = ta.selectionStart var end = ta.selectionEnd var sel = ta.value.substring(start, end) if (sel.indexOf() === 0 sel.lastIndexOf() === sel.length-2) { sel = sel.substring(2, sel.length-2) } else { sel = sel } ta.value = ta.value.substring(0, start) sel ta.value.substring(end) ta.selectionStart = start ta.selectionEnd = start sel.length return } if (inTiny) { e.preventDefault() try { window.tinymce.activeEditor.execCommand(Bold) } catch (err) {} return } if (inGutenberg) { e.preventDefault() // Intentamos usar execCommand en contentEditable try { document.execCommand(bold) } catch (err) {} return } } // Alt Shift P -> insertar párrafo/crear bloque en Gutenberg if (e.altKey e.shiftKey key === p) { var inG = isGutenbergEditable(active) if (!inG) return e.preventDefault() if (window.wp wp.data wp.data.dispatch wp.blocks wp.blocks.createBlock) { try { var paragraph = wp.blocks.createBlock(core/paragraph, { content: }) wp.data.dispatch(core/block-editor).insertBlocks(paragraph) return } catch (err) { // fallback simple } } // fallback DOM: insertaren el contentEditable try { var sel = window.getSelection() if (sel sel.rangeCount) { var range = sel.getRangeAt(0) var p = document.createElement(p) p.innerHTML =
range.collapse(false) range.insertNode(p) range.setStart(p, 0) sel.removeAllRanges() sel.addRange(range) } } catch (err) {} } } window.addEventListener(keydown, handleGlobalKeydown, false) })()
Conclusión
Con las técnicas mostradas puedes añadir atajos útiles tanto al editor clásico como a Gutenberg. La clave es encolar el script solo en las pantallas de edición, detectar correctamente el contexto (foco y tipo de editor), y ofrecer un comportamiento que respete la experiencia del usuario y la accesibilidad. Adapta las combinaciones y lógicas según tus necesidades: puedes añadir opciones de configuración, registrar atajos condicionales o integrarlos más profundamente con las APIs de WordPress si tu plugin usa React y las dependencias de @wordpress/.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |