You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止contenteditable div内的span被删除(Vue3场景)

解决Vue3中可编辑div内不可删除span被误删的问题

当你给span设置contenteditable="false"后,浏览器仍允许通过退格键(光标在span右侧时)或全选删除操作移除这个span,这是因为浏览器默认会把不可编辑元素和周围的可编辑内容视为整体处理。以下是Vue3中的具体解决方案:

实现代码

模板部分

<template>
  <div 
    class="editable-div" 
    contenteditable
    @keydown="handleKeydown"
    @input="handleInput"
    ref="editableDiv"
  >
    <span ref="protectedSpan" contenteditable="false" class="protected-span">some text</span>
  </div>
</template>

脚本部分

<script setup>
import { ref } from 'vue';

const editableDiv = ref(null);
const protectedSpan = ref(null);

// 拦截误删的键盘事件
const handleKeydown = (e) => {
  const { key, ctrlKey, metaKey } = e;
  const selection = window.getSelection();
  const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;

  // 阻止全选操作,手动选中可编辑内容
  if ((ctrlKey || metaKey) && key === 'a') {
    e.preventDefault();
    selectEditableOnly();
    return;
  }

  // 处理退格/删除键,阻止删除保护span
  if (key === 'Backspace' || key === 'Delete') {
    if (!range || !protectedSpan.value) return;

    const span = protectedSpan.value;
    // 判断光标是否在span相邻位置,或span被选中
    const isAfterSpan = range.startOffset === 0 && range.startContainer === span.nextSibling;
    const isBeforeSpan = range.endOffset === 0 && range.endContainer === span.previousSibling;
    const isSpanSelected = range.intersectsNode(span);

    if (isAfterSpan || isBeforeSpan || isSpanSelected) {
      e.preventDefault();
      // 退格时自动将光标定位到span右侧
      if (key === 'Backspace' && isAfterSpan) {
        setCursorAfterSpan();
      }
    }
  }
};

// 仅选中可编辑区域内的非保护内容
const selectEditableOnly = () => {
  const selection = window.getSelection();
  selection.removeAllRanges();
  const range = document.createRange();
  const div = editableDiv.value;
  const childNodes = Array.from(div.childNodes);

  // 找到第一个和最后一个非保护节点
  const startNode = childNodes.find(node => node !== protectedSpan.value) || div;
  const endNode = childNodes.reverse().find(node => node !== protectedSpan.value) || div;

  range.setStart(startNode, 0);
  range.setEnd(endNode, endNode.textContent?.length || 0);
  selection.addRange(range);
};

// 将光标定位到保护span右侧
const setCursorAfterSpan = () => {
  const selection = window.getSelection();
  selection.removeAllRanges();
  const range = document.createRange();
  const span = protectedSpan.value;

  if (span.nextSibling) {
    range.setStart(span.nextSibling, 0);
  } else {
    // 若span是最后一个节点,创建空文本节点用于定位光标
    const emptyText = document.createTextNode('');
    span.parentNode.appendChild(emptyText);
    range.setStart(emptyText, 0);
  }
  range.collapse(true);
  selection.addRange(range);
};

// 检查保护span是否被意外移除,若移除则重新插入
const handleInput = () => {
  if (!editableDiv.value.contains(protectedSpan.value)) {
    editableDiv.value.appendChild(protectedSpan.value);
    setCursorAfterSpan();
  }
};
</script>

样式部分(可选)

<style scoped>
.editable-div {
  padding: 8px;
  border: 1px solid #e0e0e0;
  min-height: 48px;
  border-radius: 4px;
}
.protected-span {
  background-color: #f5f5f5;
  padding: 2px 6px;
  border-radius: 3px;
  user-select: none;
}
</style>

核心逻辑说明

  1. 键盘事件拦截:监听keydown事件,阻止全选操作并手动选中可编辑内容,避免选中保护span;同时判断光标位置,阻止针对保护span的退格/删除操作。
  2. 光标定位处理:当用户在span右侧按退格时,自动将光标定位回span右侧,避免用户困惑。
  3. 输入事件校验:通过input事件检查保护span是否存在于可编辑div中,防止通过拖拽等其他方式意外移除span。

内容的提问来源于stack exchange,提问作者Inigo Selwood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:42:55