Vue3中contenteditable div子元素聚焦与新元素焦点设置问题
Vue3 contenteditable 动态子元素聚焦与添加解决方案
一、更优的当前聚焦元素追踪方案
比起每次调用Selection API查询,更推荐通过Vue事件绑定实时追踪当前聚焦的子元素,逻辑更清晰且性能更优:
- 给
v-for循环的子元素绑定@focus事件,传递循环索引 - 用响应式变量存储当前活跃元素的索引,后续添加新元素时直接基于这个索引确定插入位置
代码示例:
<template> <div contenteditable="true" class="editable-container"> <div v-for="(item, index) in items" :key="index" class="editable-item" tabindex="0" @focus="activeItemIndex = index" > {{ item.content }} </div> </div> <button @click="addNewItem">添加新元素</button> </template> <script setup> import { ref, nextTick } from 'vue' const items = ref([{ content: '初始元素1' }, { content: '初始元素2' }]) const activeItemIndex = ref(0) </script>
二、解决新元素无法聚焦的问题
contenteditable容器内直接调用element.focus()经常失效,需要结合nextTick等待DOM更新,再用Range API精准设置光标位置:
- 基于
activeItemIndex在当前活跃元素后方插入新元素 - 用
nextTick等待Vue完成DOM渲染 - 创建Range对象并将光标定位到新元素内部,再关联到选区完成聚焦
代码示例(补充到上述script部分):
const addNewItem = async () => { // 在当前活跃元素后插入新元素 const newItem = { content: '新元素' } items.value.splice(activeItemIndex.value + 1, 0, newItem) activeItemIndex.value += 1 await nextTick() // 等待DOM更新完成 const newElement = document.querySelector(`.editable-item:nth-child(${activeItemIndex.value + 1})`) if (newElement) { const range = document.createRange() const selection = window.getSelection() range.selectNodeContents(newElement) range.collapse(true) // 将光标定位到元素开头 selection.removeAllRanges() selection.addRange(range) newElement.focus() } }
补充说明:
- 子元素需要添加
tabindex="0",确保div能被聚焦(div默认不可获得焦点) - 直接调用
focus()仅能让容器获得焦点,无法精准定位到子元素内部,必须配合Range API设置光标位置
内容的提问来源于stack exchange,提问作者Sahar Elbehery
相关产品推荐
相关产品推荐

