contenteditable 属性简介

contenteditable 是 HTML5 提供的全局属性,允许用户直接在页面上编辑元素内容。它适用于富文本编辑、实时预览等场景,通过设置 contenteditable="true"contenteditable="false" 控制编辑状态。

在原生 HTML 中使用

在原生 HTML 中,直接为元素添加 contenteditable 属性即可启用编辑功能:

<div contenteditable="true">点击这里可以编辑文本</div>

在 Vue 中的实现

Vue 需要通过 v-model 或自定义指令管理 contenteditable 的内容。由于 contenteditable 不是表单元素,需手动处理数据绑定。

方法 1:使用 v-html@input
<template>
  <div 
    contenteditable="true" 
    v-html="editableContent" 
    @input="handleInput"
  ></div>
</template>

<script>
export default {
  data() {
    return {
      editableContent: '初始内容'
    };
  },
  methods: {
    handleInput(e) {
      this.editableContent = e.target.innerHTML;
    }
  }
};
</script>

方法 2:封装为自定义指令
Vue.directive('editable', {
  bind(el, binding, vnode) {
    el.contentEditable = true;
    el.innerHTML = binding.value;
    el.addEventListener('input', () => {
      vnode.context[binding.expression] = el.innerHTML;
    });
  },
  update(el, binding) {
    if (el.innerHTML !== binding.value) {
      el.innerHTML = binding.value;
    }
  }
});

在模板中使用:

<div v-editable="editableContent"></div>

在 React 中的实现

React 通过 dangerouslySetInnerHTML 和状态管理实现类似功能。

方法 1:受控组件模式
import { useState } from 'react';

function EditableDiv() {
  const [content, setContent] = useState('初始内容');

  const handleInput = (e) => {
    setContent(e.target.innerHTML);
  };

  return (
    <div
      contentEditable
      dangerouslySetInnerHTML={{ __html: content }}
      onInput={handleInput}
    />
  );
}

方法 2:使用 ref 管理内容
import { useRef, useState } from 'react';

function EditableDiv() {
  const [content, setContent] = useState('初始内容');
  const divRef = useRef(null);

  const handleBlur = () => {
    setContent(divRef.current.innerHTML);
  };

  return (
    <div
      ref={divRef}
      contentEditable
      dangerouslySetInnerHTML={{ __html: content }}
      onBlur={handleBlur}
    />
  );
}

常见问题与优化

  1. 性能问题
    频繁的 innerHTML 操作可能影响性能,建议使用 onBlur 替代 onInput 减少更新频率。

  2. XSS 风险
    contenteditable 可能引入不安全内容,需使用 DOMPurify 等库过滤 HTML:

    import DOMPurify from 'dompurify';
    const cleanHTML = DOMPurify.sanitize(userInput);
    

  3. 光标位置丢失
    动态更新内容时可能导致光标重置,可通过 RangeSelection API 保存和恢复光标位置。

富文本扩展

结合 document.execCommand 或第三方库(如 Quill、Tiptap)实现更复杂的富文本功能,例如加粗、插入图片等。

总结

contenteditable 在 Vue/React 中需手动处理数据绑定,通过指令、受控组件或 ref 实现双向同步。注意性能优化和安全性,复杂场景推荐使用现成的富文本编辑器库。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐