仿Notion的CMS中嵌套contenteditable div的keydown事件监听异常
解决contenteditable根容器下子元素keydown事件无法触发的问题
原因分析
当根div设置contenteditable="true"后,浏览器会将其视为可编辑容器,焦点默认落在根元素上而非子元素。此时子元素的keydown事件会因焦点不在自身而无法触发,或事件冒泡过程被浏览器的编辑行为干扰,导致通过propogateKeyUp传递的事件无法正常到达父组件。
解决方案
1. 根元素绑定事件+目标元素校验
直接在根可编辑容器上监听keydown事件,通过事件目标判断是否为xdoc-rich-text类元素(或其子元素),再执行对应逻辑:
<template> <div contenteditable="true" @keydown="handleRootKeyDown" > <div class="xdoc-rich-text">编辑内容...</div> </div> </template> <script> export default { methods: { handleRootKeyDown(e) { const targetEl = e.target; // 校验目标是否为目标类元素或其后代 if (targetEl.classList.contains('xdoc-rich-text') || targetEl.closest('.xdoc-rich-text')) { this.handleKeyUp(e); } }, handleKeyUp(e) { // 原有的事件处理逻辑 } } } </script>
2. 强制子元素获取焦点
在子元素挂载或点击时主动让其获取焦点,确保事件能在子元素上触发:
<template> <div contenteditable="true"> <div class="xdoc-rich-text" ref="richText" @click="focusSelf" @keydown="propogateKeyUp" >编辑内容...</div> </div> </template> <script> export default { mounted() { this.focusSelf(); }, methods: { focusSelf() { this.$refs.richText.focus(); }, propogateKeyUp(e) { this.$emit('keydown', e); } } } </script>
如果存在多个xdoc-rich-text元素,需补充点击切换焦点的逻辑。
3. 事件委托统一处理
利用事件委托在根容器上监听所有keydown事件,根据目标元素类名分发处理逻辑:
<template> <div contenteditable="true" @keydown="handleKeyDown" > <div class="xdoc-rich-text">块1</div> <div class="xdoc-rich-text">块2</div> </div> </template> <script> export default { methods: { handleKeyDown(e) { const richTextEl = e.target.closest('.xdoc-rich-text'); if (richTextEl) { // 直接处理或向上传递事件 this.$emit('rich-text-keydown', e, richTextEl); } } } } </script>
额外注意事项
- 浏览器对
contenteditable元素会默认处理回车、删除等编辑行为,若需自定义逻辑,可在事件处理中调用e.preventDefault()或e.stopPropagation()阻止默认行为。 - 合理设置元素的
tabindex属性,避免焦点异常导致事件触发失效。
内容的提问来源于stack exchange,提问作者Het Tarkhala
相关产品推荐
相关产品推荐

