可编辑div中如何在光标位置添加内容

本文记录了在可编辑div中实现光标位置插入内容的方法,适用于HTML与jQuery结合的场景,供日后参考。

之前做了一个可编辑div需要在里面插入内容,搜了好多代码,就这个能实现我的功能,记录一下,以备以后用

<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title>UMEDITOR 简单功能</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <script type="text/javascript" src="../third-party/jquery.min.js"></script>
</head>
<body>

    <h1>UMEDITOR 简单功能</h1>
	<script type="text/javascript">
		$(function(){
			$("#myEditor").focus(function(){
		    	$("#myEditor").removeClass("flag");
		    });
		    /* $("#myEditor").blur(function(){
		    	$("#myEditor").addClass("flag");
		     }); */
		});
	
   		function add(){ 
   		 insertHTML("<input type='text' disabled />"); 
   		}
   		
   		 //再加入一个全屏事件  
   		     $(window).click(function(e)  
   		          {  
   		            if (window.getSelection)  
   		            {  
   		                 var getevent=e.srcElement?e.srcElement:e.target;//不要告诉我不知道这句的意思  
   		              //console.log(getevent.id,getevent.tagName);
   		                if(getevent.id!=null && getevent.id=="cmdInsert"||getevent.id=="myEditor")  
   		                {  
   		                	//alert(0);
   		                	//代表 点了插入html的按钮  
   		                    //则不执行getFocus方法  
   		                 }  
   		                else  
   		                	$("#myEditor").addClass("flag");//除非点了那个插入html的按钮 其他时候必须要执行getFocus来更新最后失去焦点的div  
   		            }  

   		          }) 
   		
   		       
   		function insertHTML(html)  
        {  
            var dthis=$("#myEditor")[0];//要插入内容的某个div,在标准浏览器中 无需这句话  
            //dthis.focus();
             var sel, range; 
             console.log($(dthis).hasClass("flag"));
             if($(dthis).hasClass("flag")){
            	 $(dthis).html(dthis.innerHTML+html);
            	 return;
             }
             if (window.getSelection)  
              {  
                     // IE9 and non-IE  
                     sel = window.getSelection();  
                     if (sel.getRangeAt && sel.rangeCount) {  
                     range = sel.getRangeAt(0);  
                     range.deleteContents();  
                     var el = document.createElement('div');  
                     el.innerHTML = html;  
                     var frag = document.createDocumentFragment(), node, lastNode;  
                     while ( (node = el.firstChild) )  
                      {  
                         lastNode = frag.appendChild(node);  
                      }  

                 range.insertNode(frag);  
                     if (lastNode) {  
                     range = range.cloneRange();  
                     range.setStartAfter(lastNode);  
                     range.collapse(true);  
                     sel.removeAllRanges();  
                     sel.addRange(range);  
                     }  
                    }  
             }   
             else if (document.selection && document.selection.type !='Control')   
             {  		
                 $(dthis).focus(); //在非标准浏览器中 要先让你需要插入html的div 获得焦点  
          	ierange= document.selection.createRange();//获取光标位置  
                 ierange.pasteHTML(html);    //在光标位置插入html 如果只是插入text 则就是fus.text="..."  
                 $(dthis).focus();      

             }  
        }  	
	</script>
	<button onclick="add()" id="cmdInsert" style="border: none;background-color: #fff;">增加节点</button>
    <div id="myEditor" class="flag" style="position:absolute;width:800px;height:240px;border:1px;border-color: red;background-color: #f5f5f5" contenteditable="true">
    	
    </div>
	
</body>
</html>


<think>我们需要记录可编辑div中的光标位置,并在下次获取焦点时恢复到该位置。关键点在于使用Range和Selection API来保存和恢复光标位置。 步骤: 1. 当div失去焦点时,保存当前的光标位置。 2. 当div再次获取焦点时,恢复之前保存的光标位置。 具体实现: 保存光标位置: - 使用window.getSelection()获取当前选择对象。 - 如果选择范围不为空,则获取第一个范围(Range)。 - 记录这个范围的信息。但是,由于DOM结构可能会变化,直接保存Range对象不可靠,我们需要保存一个与DOM结构无关的表示,比如使用节点和偏移量。 恢复光标位置: - 根据保存的节点和偏移量重新创建一个Range,并将其添加到选择对象中。 但是,需要注意的是,在保存时,我们保存的节点必须是DOM树中稳定存在的节点(比如可编辑div本身或其子节点),并且要确保在恢复时这些节点仍然存在(没有在后续操作中被移除)。 另一种方法是使用路径(通过节点在DOM树中的位置索引)来记录,这样即使节点被移除,我们也可以尝试重新定位,但实现起来较复杂。 这里我们采用一种简单的方法:保存选中范围的起始节点和偏移量,以及结束节点和偏移量(对于选区,我们通常只需要起始位置即可,因为恢复时我们通常只需要插入点)。 但是,如果保存的节点在恢复时被删除,那么恢复就会失败。因此,我们假设在记录和恢复之间,可编辑div内容没有发生结构性变化(即节点没有被删除或替换,但文本内容可能变化)。如果内容变化,节点可能就不存在了,所以这种方法有局限性。 更健壮的方法是使用序列化的路径(例如,记录从根节点到目标节点的索引序列),但这里我们先实现基本方法。 实现代码: 1. 保存光标位置: - 当div失去焦点(blur)时,记录当前光标位置。 2. 恢复光标位置: - 当div获得焦点(focus)时,尝试恢复光标位置。 示例: 假设可编辑div的id为"editor"。 我们定义两个函数: - saveSelection(): 保存当前选择 - restoreSelection(): 恢复之前保存的选择 我们用一个全局变量(或闭包内变量)来保存选择信息。 注意:如果保存时没有选择(比如用户没有选择任何内容,只是点击了其他地方),那么我们应该记录一个默认的位置(比如最后)。 另外,在恢复时,如果之前没有保存过,则不做任何操作。 代码示例: 由于我们只关心光标位置插入点),所以只保存一个范围(Range)的起始位置。 具体步骤: 保存: - 获取当前选择(selection)。 - 如果选择范围数大于0,则获取第一个范围(range)。 - 记录range的startContainer(起始节点)和startOffset(起始偏移量),以及endContainer和endOffset(但我们恢复时通常只需要一个插入点,所以可以只保存start)。 - 注意:节点是对象,直接保存对象在后续可能无效(因为DOM可能重新渲染),所以我们需要保存节点在DOM中的相对位置?但是这里我们先直接保存节点引用,并假设在恢复之前DOM结构没有改变。 恢复: - 创建一个新的range。 - 设置range的起始位置为之前保存的节点和偏移量。 - 折叠范围(设置为插入点)。 - 清除当前选择,并添加这个range。 但是,如果保存的节点在恢复时已经不存在(比如被删除了),那么我们需要理错误。我们可以尝试将光标放在可编辑div的末尾。 另一种方法:在保存时,我们记录相对于可编辑div光标位置(例如,整个div的文本内容中的字符偏移量)。这样,即使节点变化,我们也可以根据文本内容来恢复。但是,如果div中有复杂的HTML结构,这种方法就会变得复杂。 这里我们采用节点引用的方法,并假设在保存和恢复之间,可编辑div的DOM结构没有变化(即节点没有被删除或替换,但文本内容可以改变,因为文本节点的内容改变不会改变节点本身)。 注意:如果文本节点的内容改变,但节点还在,那么偏移量可能仍然有效(比如用户只是添加或删除了文本,但节点还在,那么偏移量可能还在合理范围内)。 实现: 我们用一个对象来保存位置信息:{container: node, offset: offset}。 但是,在事件理中,我们需要注意,当div失去焦点时,我们保存的位置信息是当前的光标位置。当div再次获得焦点时,我们恢复这个位置。 然而,如果用户在这期间点击了其他地方,我们只记录这个div光标位置,其他元素的操作我们不关心。 代码示例: 由于我们直接保存节点引用,而节点引用是活的(live),所以只要节点没有被移除,就可以使用。但是,如果节点被移除,那么我们就需要有一个备选方案(比如将光标放在div的末尾)。 步骤: 1. 为可编辑div添加blur事件监听器,调用saveCursorPosition函数。 2. 为可编辑div添加focus事件监听器,调用restoreCursorPosition函数。 注意:在恢复光标时,我们需要确保div已经获得焦点,然后才能设置选择。 代码实现: 我们使用一个变量lastCursorPos来保存位置信息。 具体代码: ```html <div id="editor" contenteditable="true" style="border:1px solid #ccc; min-height:100px;"></div> <script> let lastCursorPos = null; const editor = document.getElementById('editor'); editor.addEventListener('blur', () => { saveCursorPosition(); }); editor.addEventListener('focus', () => { if (lastCursorPos) { restoreCursorPosition(); } }); function saveCursorPosition() { const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); // 我们只保存当前的光标位置插入点),所以保存起始位置即可 lastCursorPos = { startContainer: range.startContainer, startOffset: range.startOffset }; } else { lastCursorPos = null; } } function restoreCursorPosition() { if (!lastCursorPos) return; const selection = window.getSelection(); const range = document.createRange(); // 设置范围 range.setStart(lastCursorPos.startContainer, lastCursorPos.startOffset); range.collapse(true); // 折叠到开始点(即插入点) selection.removeAllRanges(); selection.addRange(range); } </script> ``` 但是,如果保存的节点在恢复时已经被删除(比如从DOM中移除了),那么setStart会抛出错误。因此我们需要进行错误理。 改进的restoreCursorPosition: ```javascript function restoreCursorPosition() { if (!lastCursorPos) return; try { const selection = window.getSelection(); const range = document.createRange(); range.setStart(lastCursorPos.startContainer, lastCursorPos.startOffset); range.collapse(true); selection.removeAllRanges(); selection.addRange(range); } catch (e) { // 如果恢复失败,将光标放在div的末尾 const selection = window.getSelection(); selection.selectAllChildren(editor); selection.collapseToEnd(); // 同时更新保存的位置为末尾位置(可选) saveCursorPosition(); // 重新保存一次,现在位置在末尾 } } ``` 另外,在保存时,我们可能还需要考虑跨浏览器兼容性,但现代浏览器基本都支持Range和Selection API。 注意:在div失去焦点时,我们保存的位置是当时的位置。如果用户在这之后又编辑了内容(比如在另一个可编辑区域编辑),然后回到这个div,那么恢复的位置就是之前保存的位置。这符合需求。 但是,如果用户在这期间编辑了同一个div(比如通过键盘快捷键),那么div内容和DOM结构可能已经改变,之前保存的节点可能已经不存在。因此,我们可能需要监听div内容变化,并在变化时清除lastCursorPos(或者重新保存)。但这样就会改变需求:我们只记录失去焦点时的位置,并在下次获得焦点时恢复,即使中间有编辑操作,我们也恢复。如果用户编辑了,那么光标位置应该由编辑操作决定,所以通常我们不希望自动恢复。因此,我们只在失去焦点时保存,在获得焦点时恢复,中间不做理。 如果需求是记录最后一次编辑的光标位置,那么我们需要在每次编辑后保存位置。但这里的需求是“下次获取焦点时,自动恢复到该位置”,这个“该位置”是指上次失去焦点时的位置。因此,我们不需要在编辑时保存。 综上所述,我们实现了基本功能。 但是,如果可编辑div内容被程序修改(比如通过innerHTML),那么之前保存的节点可能被删除。这时恢复就会失败,并进入错误理,将光标放在末尾。 为了更健壮,我们可以记录节点在可编辑div中的路径(例如,从div开始,到目标节点的子节点索引序列)。这样,即使节点被删除,我们也可以尝试重新创建路径(但前提是DOM结构没有变化)。如果DOM结构被改变,那么路径可能无效。 由于时间关系,这里我们只实现基本方法。 最后,注意:在移动端,某些浏览器可能存在兼容性问题,需要测试。 现在,根据要求,我们需要在最后提出3-5个相关问题。 §§相关问题§§ 1. 如果可编辑div内容被动态替换,如何确保保存的光标位置信息仍然有效? 2. 如何实现跨浏览器兼容的光标位置保存与恢复,特别是对旧版IE浏览器的支持? 3. 当可编辑div中包含大量文本节点时,如何优化光标位置记录的性能? 4. 在协同编辑场景中,如何同步多个用户的光标位置? 5. 如何扩展当前方案以支持多选(选区)而不仅仅是插入点?</think>### 回答 在可编辑 div 中记录和恢复光标位置,需要通过以下步骤实现: #### 核心实现方案 1. **记录光标位置**(使用 `Range` 和 `Selection` API) ```javascript // 保存光标位置的全局变量 let savedRange = null; function saveCursorPosition() { const selection = window.getSelection(); if (selection.rangeCount > 0) { savedRange = selection.getRangeAt(0).cloneRange(); } } ``` 2. **恢复光标位置** ```javascript function restoreCursorPosition() { if (!savedRange) return; const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(savedRange.cloneRange()); // 确保元素获得焦点 document.getElementById('editor').focus(); } ``` 3. **事件绑定** ```javascript const editor = document.getElementById('editor'); // 失去焦点时保存位置 editor.addEventListener('blur', saveCursorPosition); // 获得焦点时恢复位置 editor.addEventListener('focus', restoreCursorPosition); ``` #### 完整实现示例 ```html <div id="editor" contenteditable="true" style="border:1px solid #ccc; padding:10px; min-height:100px;"> 在此编辑内容... </div> <script> let savedRange = null; const editor = document.getElementById('editor'); // 保存光标位置 function saveCursorPosition() { const sel = window.getSelection(); if (sel.rangeCount > 0) { savedRange = sel.getRangeAt(0).cloneRange(); } } // 恢复光标位置 function restoreCursorPosition() { if (!savedRange) return; const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(savedRange.cloneRange()); editor.focus(); } // 绑定事件 editor.addEventListener('blur', saveCursorPosition); editor.addEventListener('focus', restoreCursorPosition); </script> ``` #### 特殊场景理 1. **内容动态更新后恢复** ```javascript function updateContent() { // 更新内容前保存光标 saveCursorPosition(); // 执行DOM操作 editor.innerHTML += "<p>新添加内容</p>"; // 恢复光标 setTimeout(restoreCursorPosition, 0); } ``` 2. **移动端兼容理** ```javascript // 添加触摸事件支持 editor.addEventListener('touchstart', saveCursorPosition); editor.addEventListener('touchend', restoreCursorPosition); ``` 3. **多光标支持(高级)** ```javascript // 保存多个光标位置 const savedSelections = []; function saveMultiSelection() { const sel = window.getSelection(); savedSelections = []; for (let i = 0; i < sel.rangeCount; i++) { savedSelections.push(sel.getRangeAt(i).cloneRange()); } } ``` #### 注意事项 1. 使用 `cloneRange()` 防止原始 Range 对象失效 2. 添加 `setTimeout` 延迟恢复解决浏览器渲染时序问题 3. 在内容更新前后需手动管理光标状态 4. 对富文本编辑器需额外理嵌套元素的光标定位
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值