前端??HTML contenteditable 屬性使用指南

??什么是 contenteditable?

  • HTML5 提供的全局屬性,使元素內容可編輯
  • 類似于簡易富文本編輯器
  • 兼容性??
    支持所有現代瀏覽器(Chrome、Firefox、Safari、Edge)
    移動端(iOS/Android)部分鍵盤行為需測試
<p contenteditable="true">可編輯的段落</p>

屬性值說明
contenteditable 的三種值:
true:元素可編輯
false:元素不可編輯
inherit:繼承父元素的可編輯狀態

<p contenteditable="false">不可編輯的段落</p>
<div contenteditable="true">點擊編輯此內容</div>
<p contenteditable="inherit">繼承父元素的可編輯狀態</p>

核心功能實現?

保存編輯內容?
  <div style="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 = falseif (this.rawData !== this.editContent) {this.submitChanges()this.editContent = this.rawData}},
編輯時光標位置的設置
  <div style="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) 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},
處理換行失敗的問題(需要回車兩次觸發)
    // 給數組添加回車事件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();},

踩坑案例

  • 數組遍歷標簽上不能夠使用此事件contenteditable

完整代碼展示

  • 帶數組的處理
  • 不帶數組的處理

帶數組代碼

<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';
export default {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: 10pxreturn this.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><div style="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>
// 會議待辦事項組件222
export default {name: 'pendingResult2',props: {dataList: {type: Object,required: true}},data() {return {rawData:null,editContent: '',      // 編輯內容緩存lastCursorPos: null,  // 光標位置記錄isEditing: false,};},computed: {newData () {return this.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.pswp.cn/news/908142.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908142.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908142.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

持續領跑中國異地組網路由器市場,貝銳蒲公英再次登頂銷量榜首

作為國產遠程連接SaaS服務的創領者&#xff0c;貝銳持續引領行業發展&#xff0c;旗下貝銳蒲公英異地組網路由器&#xff0c;憑借出色的技術實力和市場表現&#xff0c;斬獲2024年線上電商平臺市場銷量份額中國第一的佳績&#xff0c;充分彰顯了其在網絡解決方案與異地組網領域…

五大主流大模型推理引擎深度解析:llama.cpp、vLLM、SGLang、DeepSpeed和Unsloth的終極選擇指南

在人工智能的競技場上,大模型推理框架就像是為超級跑車精心調校的引擎系統——選對了能讓你的AI應用一騎絕塵,選錯了可能連"停車場"都開不出去。這些框架的核心價值在于將訓練好的"大腦"轉化為實際可用的"肌肉記憶",而選擇標準則需要像職業賽…

前端面試二之運算符與表達式

目錄 1.JavaScript 中的 和 運算符 2.|| (邏輯或) 運算符 與 ES6 默認參數的區別 與 ?? (空值合并運算符) 的區別 3.?.&#xff08;可選鏈&#xff09;運算符 (1). 安全訪問深層嵌套屬性 (2). 安全調用可能不存在的函數 (3). 安全訪問數組元素 4.展開運算符 (..…

GB/T 24507-2020 浸漬紙層壓實木復合地板檢測

浸漬紙層壓實木地板是指以一層或多層專用紙浸漬熱固性氨基樹脂&#xff0c;經干燥后鋪裝在膠合板基材正面&#xff0c;專用紙表面加耐磨層&#xff0c;基材背面可加平衡層&#xff0c;經熱壓、成型的地板。 GB/T 24507-2020 浸漬紙層壓實木復合地板測試項目&#xff1a; 測試項…

AWS DocumentDB vs MongoDB:數據庫的技術抉擇

隨著非關系型數據庫在現代應用中的廣泛應用&#xff0c;文檔型數據庫因其靈活的結構與出色的擴展性&#xff0c;逐漸成為企業開發與架構設計中的核心選擇。在眾多文檔數據庫中&#xff0c;MongoDB 憑借其成熟生態與社區支持占據主導地位&#xff1b;與此同時&#xff0c;AWS 提…

微信小程序實現運動能耗計算

微信小程序實現運動能耗計算 近我做了一個挺有意思的微信小程序&#xff0c;能夠實現運動能耗的計算。只需要輸入性別、年齡、體重、運動時長和運動類型這些信息&#xff0c;就能算出對應的消耗熱量。 具體來說&#xff0c;在小程序里&#xff0c;性別不同&#xff0c;身體基…

三軸地磁傳感器的主要應用場景

隨著材料科學、微電子技術以及傳感器技術的不斷進步&#xff0c;三軸地磁傳感器的性能將不斷提升&#xff0c;包括提高精度、降低功耗、增強抗干擾能力等。 RAMSUN提供的是一款三軸地磁傳感器采用第三代AMR技術&#xff0c;帶有自動溫度補償的三軸磁傳感器&#xff0c;該產品因…

使用 SseEmitter 實現 Spring Boot 后端的流式傳輸和前端的數據接收

1.普通文本消息的發送和接收 GetMapping("/stream")public SseEmitter streamResponse() {SseEmitter emitter new SseEmitter(0L); // 0L 表示永不超時Executors.newSingleThreadExecutor().execute(() -> {try {for (int i 1; i < 5; i) {emitter.send(&q…

nssm配置springboot項目環境,注冊為windows服務

NSSM 的官方下載地址是&#xff1a;NSSM - the Non-Sucking Service Manager1 使用powershell輸入命令,java項目需要手動配置和依賴nacos .\nssm.exe install cyMinio "D:\minio\啟動命令.bat" .\nssm.exe install cyNacos "D:\IdeaProject\capacity\nacos-s…

WinCC學習系列-基礎概念

從本節起&#xff0c;學習和了解西門子最新SCADA軟件WinCC 8.0&#xff0c;將從基礎概念開始&#xff0c;到入門操作&#xff08;創建項目、組態通信、組態過程畫面、組態面板類型和變量結構、歸檔和顯示值、組態消息&#xff09;&#xff0c;到高級應用&#xff08;WinCC選件、…

數據分析圖表類型及其應用場景

說明&#xff1a;頂部HTML文件下載后可以直接查看&#xff0c;帶有示圖。 摘要 數據可視化作為現代數據分析的核心環節&#xff0c;旨在將復雜、抽象的數據轉化為直觀、易懂的圖形形式。這種轉化顯著提升了業務決策能力&#xff0c;優化了銷售與營銷活動&#xff0c;開辟了新…

《江西棒壘球》敗方mvp叫什么·棒球1號位

敗方mvp也是MVP&#xff0c;以棒球運動為例&#xff0c;MLB&#xff08;美國職棒大聯盟&#xff09;的個人獎項旨在表彰球員在不同領域的卓越表現&#xff0c;涵蓋常規賽和季后賽的杰出成就。 常規賽核心獎項 最有價值球員獎&#xff08;MVP&#xff09; 定義&#xff1a;表彰…

CD43.vector模擬實現(2)

目錄 1.拷貝構造函數 寫法1 寫法2 測試代碼 調試找bug 解決方法:修改拷貝構造函數 測試代碼 2.operator[ ] 測試代碼 1.沒有const修飾 2.有const修飾 3.insert 迭代器失效問題 承接CD42.vector模擬實現(1)文章 1.拷貝構造函數 設置start、finish和end_of_storag…

【C/C++】入門grpc的idl

文章目錄 grpc idl 簡單介紹1. 文件結構組織規范文件命名包結構&#xff1a;推薦&#xff1a;一個文件只定義一個 service&#xff0c;如果 service 很復雜&#xff0c;可拆分多個 proto 文件。 2. 消息定義規范命名風格字段編號&#xff1a;示例&#xff1a; 3. 服務與 RPC 設…

安全-JAVA開發-第二天

Web資源訪問的流程 由此可見 客戶訪問JAVA開發的應用時 會先通過 監聽器&#xff08;Listener&#xff09;和 過濾器&#xff08;Filter&#xff09; 今天簡單的了解下這兩個模塊的開發過程 監聽器&#xff08;Listener&#xff09; 主要是監聽 我們觸發了什么行為 并進行反應…

使用 Ansys Q3D 進行電容提取

精確的電容提取在高速和 RF 設計中至關重要。雖然簡單的公式可以提供一個很好的起點&#xff0c;但它們往往無法捕捉 fringing fields 和 layout-dependent parasitics 的影響。在本博客中&#xff0c;我們演示了如何使用Ansys Q3D Extractor來計算電容值&#xff0c;從基本的平…

卡西歐模擬器:Windows端功能強大的計算器

引言 大家還記得初中高中時期用的計算器嗎&#xff1f;今天給大家分享的就是一款windows端的卡西歐計算器。 軟件介紹 大家好&#xff0c;我是逍遙小歡。 CASIO fx-9860G是一款功能強大的圖形計算器&#xff0c;適用于數學、科學和工程計算。以下是其主要功能和特點的詳細介…

【Bluedroid】藍牙啟動之gatt_init 流程源碼解析

本文圍繞Android藍牙協議棧中 GATT(通用屬性配置文件)模塊的初始化函數gatt_init展開,深入解析其核心實現邏輯與關鍵步驟。通過分析gatt_init及其關聯子函數(如L2CA_RegisterFixedChannel、gatt_profile_db_init、EattExtension::Start等),以及相關數據結構(如tGATT_CB控…

Vue 3 中ref 結合ts 獲取 DOM 元素的實踐指南。

文章目錄 前言一、為什么需要為 ref 添加類型&#xff1f;二、基本用法&#xff1a;引用 DOM 元素1. 引用通用 DOM 元素&#xff08;HTMLElement&#xff09;2. 引用特定類型的 DOM 元素&#xff08;如 HTMLDivElement&#xff09; 三、<script setup> 語法中的類型定義四…

Axure形狀類組件圖標庫(共8套)

點擊下載《月下倚樓圖標庫(形狀組件)》 原型效果&#xff1a;https://axhub.im/ax9/02043f78e1b4386f/#g1 摘要 本圖標庫集錦精心匯集了8套專為Axure設計的形狀類圖標資源&#xff0c;旨在為產品經理、UI/UX設計師以及開發人員提供豐富多樣的設計素材&#xff0c;提升原型設計…