可编辑div输入回车后无法选中对应代码块问题求助
解决可编辑Div中选中```包裹文本块的问题
原代码失效的核心原因:回车会把可编辑Div的内容拆分成多个文本节点,但原逻辑仅操作第一个文本节点;同时通过innerText索引定位的方式,无法和DOM节点的偏移量准确对应,导致换行后功能完全失效。
以下是简化后的实现代码,能正确处理包含回车的文本块:
$('button').on('click', function() { const editor = $('#cstory')[0]; const selection = window.getSelection(); if (selection.rangeCount === 0) return; let currentNode = selection.getRangeAt(0).startContainer; // 确保当前操作的是文本节点 if (currentNode.nodeType !== Node.TEXT_NODE) { currentNode = currentNode.firstChild; if (!currentNode) return; } let startRange = null; let endRange = null; // 向前遍历节点,找到起始的```标记 function findStartMarker(node) { if (!node) return false; const text = node.textContent; const markerIndex = text.lastIndexOf('```'); if (markerIndex !== -1) { // 校验是否是独立的块起始标记(后接换行或位于文本开头) const afterMarker = text.slice(markerIndex + 3); if (afterMarker.startsWith('\n') || markerIndex === 0) { startRange = document.createRange(); startRange.setStart(node, markerIndex); startRange.setEnd(node, markerIndex + 3); return true; } } // 遍历前一个兄弟节点,若没有则遍历父节点的前一个兄弟 if (node.previousSibling) return findStartMarker(node.previousSibling); if (node.parentNode !== editor) return findStartMarker(node.parentNode.previousSibling); return false; } // 向后遍历节点,找到结束的```标记 function findEndMarker(node) { if (!node) return false; const text = node.textContent; const markerIndex = text.indexOf('```'); if (markerIndex !== -1) { // 校验是否是独立的块结束标记(前接换行或位于文本末尾) const beforeMarker = text.slice(0, markerIndex); if (beforeMarker.endsWith('\n') || markerIndex === text.length - 3) { endRange = document.createRange(); endRange.setStart(node, markerIndex); endRange.setEnd(node, markerIndex + 3); return true; } } // 遍历后一个兄弟节点,若没有则遍历父节点的后一个兄弟 if (node.nextSibling) return findEndMarker(node.nextSibling); if (node.parentNode !== editor) return findEndMarker(node.parentNode.nextSibling); return false; } // 找到成对标记后,选中整个块 if (findStartMarker(currentNode) && findEndMarker(currentNode)) { const fullRange = document.createRange(); fullRange.setStart(startRange.startContainer, startRange.startOffset); fullRange.setEnd(endRange.endContainer, endRange.endOffset); selection.removeAllRanges(); selection.addRange(fullRange); } });
.cstory{white-space:pre-wrap;min-height:100px;border:1px solid #ccc;padding:8px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class='cstory' id='cstory' contenteditable> lorem sea
lorem ipsum
dolor sit
sky
lorem sky
lorem ipsum
dolor sit
sky
dolor sit </div> <br> <button>CLICK</button>
关键逻辑说明
- 节点遍历:从光标所在的文本节点出发,向前/向后遍历所有关联的兄弟节点、父节点的兄弟节点,确保不会漏掉换行拆分后的任意节点。
- 标记校验:只认独立的块标记(起始标记后接换行、结束标记前接换行),避免误选中文本中零散出现的```字符。
- Range精准选中:通过匹配到的起始、结束标记位置,创建完整的Range对象,实现整个代码块的选中。
内容的提问来源于stack exchange,提问作者provance
相关产品推荐
相关产品推荐

