前端HTML contenteditable 属性使用指南 - 教程

news/2025/10/7 16:53:57/文章来源:https://www.cnblogs.com/lxjshuju/p/19128623

​​什么是 contenteditable?

<p contenteditable="true">可编辑的段落</p>

属性值说明
contenteditable 的三种值:
true:元素可编辑
false:元素不可编辑
inherit:继承父元素的可编辑状态

<p contenteditable="false">不可编辑的段落</p>
<div contenteditable="true">点击编辑此内容</div>
<p contenteditable="inherit">继承父元素的可编辑状态</p>

核心功能实现​

保存编辑内容​
<divstyle="margin-left: 36px;"v-html="newData"contenteditable="true"ref="ediPending2Div"class="editable"@blur
="updateContent"@input
="handleInput"@focus
="saveCursorPosition"@keydown.enter.prevent
="handleEnterKey"></div>
// 更新内容
updateContent(
) {
this.isEditing = false
if (
this.rawData !==
this.editContent) {
this.submitChanges(
)
this.editContent =
this.rawData
}
}
,
编辑时光标位置的设置
<divstyle="margin-left: 36px;"v-html="newData"contenteditable="true"ref="ediPending2Div"class="editable"@blur
="updateContent"@input
="handleInput"@focus
="saveCursorPosition"@keydown.enter.prevent
="handleEnterKey"></div>
// 保存光标位置
saveCursorPosition(
) {
const selection = window.getSelection(
)
if (selection.rangeCount >
0
) {
const range = selection.getRangeAt(0
)
this.lastCursorPos = {
startContainer: range.startContainer,
startOffset: range.startOffset,
endOffset: range.endOffset
}
}
}
,
// 恢复光标位置
restoreCursorPosition(
) {
if (!
this.lastCursorPos || !
this.isEditing)
return
const selection = window.getSelection(
)
const range = document.createRange(
)
try {
range.setStart(
this.lastCursorPos.startContainer,
Math.min(
this.lastCursorPos.startOffset,
this.lastCursorPos.startContainer.length)
)
range.setEnd(
this.lastCursorPos.startContainer,
Math.min(
this.lastCursorPos.endOffset,
this.lastCursorPos.startContainer.length)
)
selection.removeAllRanges(
)
selection.addRange(range)
}
catch (e) {
// 出错时定位到末尾
range.selectNodeContents(
this.$refs.ediPending2Div)
range.collapse(false
)
selection.removeAllRanges(
)
selection.addRange(range)
}
}
,
// 处理输入
handleInput(
) {
this.saveCursorPosition(
)
this.rawData =
this.$refs.ediPending2Div.innerHTML
}
,
处理换行失败的问题(需要回车两次触发)
// 给数组添加回车事件
handleEnterKey(e
) {
// 阻止默认回车行为(创建新div)
e.preventDefault(
)
;
// 获取当前选区
const selection = window.getSelection(
)
;
if (!selection.rangeCount)
return
;
const range = selection.getRangeAt(0
)
;
const br = document.createElement('br'
)
;
// 插入换行
range.deleteContents(
)
;
range.insertNode(br)
;
// 移动光标到新行
range.setStartAfter(br)
;
range.collapse(true
)
;
selection.removeAllRanges(
)
;
selection.addRange(range)
;
// 触发输入更新
this.handleInput(
)
;
}
,

踩坑案例

完整代码展示

带数组代码

<template><div style="margin-left: 36px;" v-loading="loading_"contenteditable="true"ref="editPendingDiv"class='editable'@blur="updateContent"@input="handleInput"@focus="saveCursorPosition"@keydown.enter.prevent="handleEnterKey"><p class="pending_title">会议待办</p><p>提炼待办事项如下:</p><div v-for="(item, index) in newData" :key="index"class="todo-item"><div class="text_container"><!-- <img src="@/assets/404.png" alt="icon"class="icon-img">--><p><span class="icon-span">AI</span>{{item}}</p></div></div></div></template><script>// 会议待办事项组件import {todoList}from '@/api/audio';import router from '@/router';exportdefault {name: 'pendingResult',props: {// items: {// type: Array,// required: true// }},data() {return {rawData:null,editContent: '', // 编辑内容缓存lastCursorPos:null, // 光标位置记录isEditing: false,loading_:false,dataList: [],routerId:this.$route.params.id};},computed: {newData () {// 在合格换行后下面添加margin-botton: 10pxreturnthis.dataList}},watch: {newData() {this.$nextTick(this.restoreCursorPosition)this.$nextTick(this.sendHemlToParent)}},mounted() {this.$refs.editPendingDiv.addEventListener('focus', () =>{this.isEditing = true})},created() {this.getDataList();},methods: {// 给数组添加回车事件handleEnterKey(e) {// 阻止默认回车行为(创建新div)e.preventDefault();// 获取当前选区const selection = window.getSelection();if (!selection.rangeCount)return;const range = selection.getRangeAt(0);const br = document.createElement('br');// 插入换行range.deleteContents();range.insertNode(br);// 移动光标到新行range.setStartAfter(br);range.collapse(true);selection.removeAllRanges();selection.addRange(range);// 触发输入更新this.handleInput();},// 发送生成数据sendHemlToParent(){this.$nextTick(()=>{const htmlString =this.$refs.editPendingDiv.innerHTMLconsole.log('获取修改',htmlString)this.$emit('editList',htmlString)})},// 保存光标位置saveCursorPosition() {const selection = window.getSelection()if (selection.rangeCount >0) {const range = selection.getRangeAt(0)this.lastCursorPos = {startContainer: range.startContainer,startOffset: range.startOffset,endOffset: range.endOffset}}},// 恢复光标位置restoreCursorPosition() {if (!this.lastCursorPos || !this.isEditing)returnconst selection = window.getSelection()const range = document.createRange()try {range.setStart(this.lastCursorPos.startContainer,Math.min(this.lastCursorPos.startOffset,this.lastCursorPos.startContainer.length))range.setEnd(this.lastCursorPos.startContainer,Math.min(this.lastCursorPos.endOffset,this.lastCursorPos.startContainer.length))selection.removeAllRanges()selection.addRange(range)}catch (e) {// 出错时定位到末尾range.selectNodeContents(this.$refs.editPendingDiv)range.collapse(false)selection.removeAllRanges()selection.addRange(range)}},// 处理输入handleInput() {this.saveCursorPosition()this.rawData =this.$refs.editPendingDiv.innerHTML},// 更新内容// updateContent() {// this.isEditing = false// if (this.rawData !== this.editContent) {// this.submitChanges()// this.editContent = this.rawData// }// },updateContent() {this.isEditing = false;// 清理HTML格式const cleanedHTML =this.rawData.replace(/<div><br><\/div>/g, '<br>').replace(/<p><br><\/p>/g, '<br>');if (cleanedHTML !==this.editContent) {this.submitChanges(cleanedHTML);}},// 提交修改submitChanges() {// 这里添加API调用逻辑console.log('提交内容:',this.rawData)this.$emit('editList',this.rawData)},async getDataList() {const id = {translate_task_id:this.routerId};this.loading_=truetry {const res=await todoList(id)if (res.code === 0) {if (res.data.todo_text == [] || res.data.todo_text ===null) {this.$message.warning("暂无待办事项");return;}// console.log("会议纪要数据:", res.data);this.dataList=res.data.todo_text}}finally {this.loading_=false}// const normalizedText = res.data.todo_text.replace(/\/n/g, '\n');// // 分割文本并过滤空行// this.dataList = normalizedText.split('\n')// .filter(line => line.trim().length > 0)// .map(line => line.trim());}}}</script><style scoped>.pending_title {/* font-size: 20px; *//* font-family: "宋体"; *//* font-weight: bold; */margin-bottom: 20px;}.text_container {display: flex;align-items: center;}.icon-img {width: 20px;height: 20px;margin-right: 10px;}.editable {/* 确保可编辑区域行为正常 */user-select: text;white-space: pre-wrap;outline: none;}.todo-item {display: flex;align-items: center;margin: 4px 0;}/* 防止图片被选中 */.icon-span {pointer-events: none;user-select: none;margin-right: 6px;font-weight: 700;color: #409EFF;}</style>

不带数组代码

<template><div><divstyle="margin-left: 36px;"v-html="newData"contenteditable="true"ref="ediPending2Div"class="editable"@blur="updateContent"@input="handleInput"@focus="saveCursorPosition"@keydown.enter.prevent="handleEnterKey"></div></div></template><script>// 会议待办事项组件222exportdefault {name: 'pendingResult2',props: {dataList: {type: Object,required: true}},data() {return {rawData:null,editContent: '', // 编辑内容缓存lastCursorPos:null, // 光标位置记录isEditing: false,};},computed: {newData () {returnthis.dataList.todo_text}},watch: {newData() {this.$nextTick(this.restoreCursorPosition)}},mounted() {this.$refs.ediPending2Div.addEventListener('focus', () =>{this.isEditing = true})},created() {// console.log(":", this.dataList);},methods: {// 给数组添加回车事件handleEnterKey(e) {// 阻止默认回车行为(创建新div)e.preventDefault();// 获取当前选区const selection = window.getSelection();if (!selection.rangeCount)return;const range = selection.getRangeAt(0);const br = document.createElement('br');// 插入换行range.deleteContents();range.insertNode(br);// 移动光标到新行range.setStartAfter(br);range.collapse(true);selection.removeAllRanges();selection.addRange(range);// 触发输入更新this.handleInput();},// 保存光标位置saveCursorPosition() {const selection = window.getSelection()if (selection.rangeCount >0) {const range = selection.getRangeAt(0)this.lastCursorPos = {startContainer: range.startContainer,startOffset: range.startOffset,endOffset: range.endOffset}}},// 恢复光标位置restoreCursorPosition() {if (!this.lastCursorPos || !this.isEditing)returnconst selection = window.getSelection()const range = document.createRange()try {range.setStart(this.lastCursorPos.startContainer,Math.min(this.lastCursorPos.startOffset,this.lastCursorPos.startContainer.length))range.setEnd(this.lastCursorPos.startContainer,Math.min(this.lastCursorPos.endOffset,this.lastCursorPos.startContainer.length))selection.removeAllRanges()selection.addRange(range)}catch (e) {// 出错时定位到末尾range.selectNodeContents(this.$refs.ediPending2Div)range.collapse(false)selection.removeAllRanges()selection.addRange(range)}},// 处理输入handleInput() {this.saveCursorPosition()this.rawData =this.$refs.ediPending2Div.innerHTML},// 更新内容updateContent() {this.isEditing = falseif (this.rawData !==this.editContent) {this.submitChanges()this.editContent =this.rawData}},// 提交修改submitChanges() {// 这里添加API调用逻辑console.log('提交内容:',this.rawData)this.$emit('editList',this.rawData)},getDataList() {},},}</script><style scoped>::v-deep .el-loading-mask{display: none !important;}p {/* margin: 0.5em 0; *//* font-family: "思源黑体 CN Regular"; *//* font-size: 18px; */}img {width: 20px;height: 20px;margin-right: 10px;}.indent_paragraph {text-indent: 2em;/* 默认缩进 */}.pending_title {/* font-size: 20px; *//* font-family: "宋体"; *//* font-weight: bold; */margin-bottom: 20px;}.text_container {display: flex;align-items: center;}.icon-img {width: 20px;height: 20px;margin-right: 10px;}.editable {/* 确保可编辑区域行为正常 */user-select: text;white-space: pre-wrap;outline: none;}.todo-item {display: flex;align-items: center;margin: 4px 0;}/* 防止图片被选中 */.icon-span {pointer-events: none;user-select: none;margin-right: 6px;font-weight: 700;color: #409EFF;}</style>
效果展示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/930618.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

太原建站模板企业网站建设的几种形式

前言 好久都没有写博客了&#xff0c;由于博主之前一直都在上班处于加班的阶段&#xff0c;所以根本就没有时间去学习。现在请假回到学校写论文&#xff0c;有时间来学习了。 所以会不断的进行博客的更新&#xff0c;以及分享我在公司学到的一些新的技术&#xff0c;希望大家多…

Seismic Unix 基础使用

目录基本语法结构数据格式线性编辑命令与非线性编辑命令 基本语法结构su_command [input] [parameter] [output]su_command是处理地震数据的核心命令。 [input]是输入参数,可以是SU格式的数据文件,也可以是其他命令的…

2025实验室净化厂家/实验室装修厂家/实验室建设厂家权威推荐榜:专业设计与洁净技术实力之选

在现代科研与产业升级的双重驱动下,实验室净化环境的质量直接关系到研发成果的准确性与生产安全。随着生物医药、精密制造等领域的快速发展,企业对洁净空间的技术要求日益严格,不仅需要符合国际标准的设计方案,更追…

做网站费用怎么核算关键词与网站标题

人工智能(AI)每天在以惊人的速度发展。这项技术在2018年已经取得了巨大的成功&#xff0c;简化医疗保健业的工作流程&#xff0c;降低制造业的间接费用&#xff0c;并减少教育业的行政工作量。现在是2019年&#xff0c;每天似乎都有一家新的AI初创公司冒出来&#xff0c;致力于…

网站内容建设总结网站反向绑定域名

目录 认识 Spring MVC什么是 Spring MVCMVC 的定义 Spring MVC 注解的运用1. Spring MVC 的连接RequestMapping 注解 2. 获取参数获取单个参数获取多个参数传递对象表单传参后端参数重命名RequestBody 接收 JSON 对象PathVariable 获取 URL 中的参数上传文件 RequestPart获取 C…

贵阳网站定制电话做装修网站

1 第2.1节&#xff1a;AWK脚本结构 1.1 第1个awk脚本 假设有如下的数据待处理&#xff0c;需要将第2列提取出来&#xff1a; #, 名称, 大小, 类型, 修改, 属性 1, COMMIT_EDITMSG, 331 bytes, 文件, 24/09/16 08:42:19, -a----- 2, config, …

有公司可以做网站升级ipv6wordpress 商城主题 汉

本文将为您描述layui表格字段表格显示不全(自适应),具体操作方法:好记性不如烂笔头。本来就是没什么好记得东西&#xff0c;下次再用时已经想不起来了。问题如下&#x1f41f;使用layui表格&#xff0c;表格字段显示不全&#xff0c;如果超出分配的位置&#xff0c;就会显示省略…

网站主机建设方案三合一网站cms

目录 NLP自然语言处理 的RNN、Seq2Seq与attention注意力机制 RNN循环神经网络 前馈网络入门 前馈网络 循环网络 多层感知器架构示例 循环神经网络的运作原理 展开 RNN seq2seq模型 Attention&#xff08;注意力机制&#xff09; 总结 引用 NLP自然语言处理 的RNN、…

简单搭建Ajax基础应用

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

修改注册表,实现电脑小键盘开机自启(NumLock灯常亮)

每次开机,小键盘(NumLock键)都需要手动开启才能使用。现分享一套只需通过修改注册表,就能让小键盘开机自启的方案。无需调整BIOS,快来看看吧。 1、打开注册表 首先 Win + R 同时按,打开运行,接着输入 regedit …

网站建设开票税收分类奇缦科技珠海网站建设优化

作者:CSDN @ _养乐多_ 本文详细记录了从Excel读取用于训练机器学习模型的数据,包括独立变量和因变量数据,以供用于机器学习模型的训练。这些机器学习模型包括但不限于随机森林回归模型(RF)和支持向量机回归模型(SVM)。随后,我们将测试数据集应用于这些模型,进行预测和…

06627网页制作和网站建设淘宝 网站开发 退货

工业以太网由于其固有的可靠性、高性能和互操作性&#xff0c;已经渗透到工厂车间&#xff0c;成为自动化和控制系统的首选通信协议。近年&#xff0c;工业以太网的市场份额已经超过了传统的现场总线协议&#xff0c;总线协议通常需要多个独立和专有的布线设施。为了满足工业环…

旅游包车网站最新模板东莞常平医院网站建设

目录 观察者模式&#xff08;Observer Pattern&#xff09; 实际应用 股票价格监控系统 发布-订阅系统 总结 观察者模式&#xff08;Observer Pattern&#xff09; 观察者模式是一种行为型设计模式&#xff0c;它定义了对象间的一对多依赖关系。当一个对象的状态发生改变…

多Agent协作入门:基于A2A协议的Agent通信

本文介绍了A2A的基本概念以及和MCP的关系,然后通过一个Hello World案例介绍了如何快速开始一个A2A Demo,相信我们可以有一个快速的感性认识。大家好,我是Edison。 之前我们学习了Semantic Kernel中的一些常见的编排…

完整教程:nav2笔记-250603

完整教程:nav2笔记-250603pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", …

MCP gateway

Top 5 MCP Gateways of 2025 https://www.truefoundry.com/blog/best-mcp-gateways Real-world deployment data reveals significant differences between marketing claims and production performance. Based on te…

点云的遮挡剔除

点云的遮挡剔除点云的遮挡剔除

在线视频下载网站如何做泸州大浪科技做网站

https://www.lucidchart.com/pages/signup?utm_expid39895073-174.qKyHpBEbQS26y86OArD-rQ.1 https://www.processon.com/

English of root for May 30th - 详解

English of root for May 30th - 详解2025-10-07 16:32 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !i…

门户网站指的是什么个人旅游网站建设方案

核心提示&#xff1a;在普拉纳夫看来&#xff0c;数字信息以像素的形式被限制在显示屏幕之中。他发明的"第六感装置"震惊全场&#xff0c;让世界为之惊叹。 MIT(麻省理工)印度裔学生Prarnav Mistry的天才发明:“第六感装置” 视频地址&#xff1a;http://v.youku.com…