当内部 HTML 更改时保留 HTML contenteditable 中的插入符位置
P粉668804228
P粉668804228 2023-11-08 22:38:23
0
2
684

我有一个充当所见即所得编辑器的 div。它充当文本框,但在其中呈现 Markdown 语法,以显示实时更改。

问题:键入字母时,插入符号位置会重置为 div 的开头。


const editor = document.querySelector('div'); editor.innerHTML = parse('**dlob** *cilati*'); editor.addEventLis tener('input', () => { editor.innerHTML = parse(editor.innerText); }); function parse(text) { return text .replace(/**(.*)**/gm, '****') // bold .replace(/*(.*)*/gm, '**'); // italic }
div { height: 100vh; width: 100vw; }


Codepen:https://codepen.io/ADAMJR/pen/MWvPebK

像 QuillJS 这样的 Markdown 编辑器似乎可以编辑子元素而不编辑父元素。这避免了问题,但我现在确定如何使用此设置重新创建该逻辑。

问题:如何让插入符号位置在打字时不重置?

更新: 我已经设法在每个输入上将插入符号位置发送到 div 的末尾。然而,这本质上仍然重置了位置。 https://codepen.io/ADAMJR/pen/KKvGNbY


P粉668804228
P粉668804228

全部回复 (2)
P粉393030917

大多数富文本编辑器的做法是保持自己的内部状态,在按键事件上更新它并渲染自定义可视层。例如这样:

const $editor = document.querySelector('.editor'); const state = { cursorPosition: 0, contents: 'hello world'.split(''), isFocused: false, }; const $cursor = document.createElement('span'); $cursor.classList.add('cursor'); $cursor.innerText = '᠎'; // Mongolian vowel separator const renderEditor = () => { const $contents = state.contents .map(char => { const $span = document.createElement('span'); $span.innerText = char; return $span; }); $contents.splice(state.cursorPosition, 0, $cursor); $editor.innerHTML = ''; $contents.forEach(el => $editor.append(el)); } document.addEventListener('click', (ev) => { if (ev.target === $editor) { $editor.classList.add('focus'); state.isFocused = true; } else { $editor.classList.remove('focus'); state.isFocused = false; } }); document.addEventListener('keydown', (ev) => { if (!state.isFocused) return; switch(ev.key) { case 'ArrowRight': state.cursorPosition = Math.min( state.contents.length, state.cursorPosition + 1 ); renderEditor(); return; case 'ArrowLeft': state.cursorPosition = Math.max( 0, state.cursorPosition - 1 ); renderEditor(); return; case 'Backspace': if (state.cursorPosition === 0) return; delete state.contents[state.cursorPosition-1]; state.contents = state.contents.filter(Boolean); state.cursorPosition = Math.max( 0, state.cursorPosition - 1 ); renderEditor(); return; default: // This is very naive if (ev.key.length > 1) return; state.contents.splice(state.cursorPosition, 0, ev.key); state.cursorPosition += 1; renderEditor(); return; } }); renderEditor();
.editor { position: relative; min-height: 100px; max-height: max-content; width: 100%; border: black 1px solid; } .editor.focus { border-color: blue; } .editor.focus .cursor { position: absolute; border: black solid 1px; border-top: 0; border-bottom: 0; animation-name: blink; animation-duration: 1s; animation-iteration-count: infinite; } @keyframes blink { from {opacity: 0;} 50% {opacity: 1;} to {opacity: 0;} }
             
    P粉060112396

    需要先获取光标的位置,然后对内容进行处理和设置。然后恢复光标位置。

    当存在嵌套元素时,恢复光标位置是一个棘手的部分。此外,您每次都会创建新的元素,旧的元素将被丢弃。

    const editor = document.querySelector(".editor"); editor.innerHTML = parse( "For **bold** two stars.\nFor *italic* one star. Some more **bold**." ); editor.addEventListener("input", () => { //get current cursor position const sel = window.getSelection(); const node = sel.focusNode; const offset = sel.focusOffset; const pos = getCursorPosition(editor, node, offset, { pos: 0, done: false }); if (offset === 0) pos.pos += 0.5; editor.innerHTML = parse(editor.innerText); // restore the position sel.removeAllRanges(); const range = setCursorPosition(editor, document.createRange(), { pos: pos.pos, done: false, }); range.collapse(true); sel.addRange(range); }); function parse(text) { //use (.*?) lazy quantifiers to match content inside return ( text .replace(/\*{2}(.*?)\*{2}/gm, "****") // bold .replace(/(?*") // italic // handle special characters .replace(/\n/gm, "
    ") .replace(/\t/gm, " ") ); } // get the cursor position from .editor start function getCursorPosition(parent, node, offset, stat) { if (stat.done) return stat; let currentNode = null; if (parent.childNodes.length == 0) { stat.pos += parent.textContent.length; } else { for (let i = 0; i = stat.pos) { range.setStart(parent, stat.pos); stat.done = true; } else { stat.pos = stat.pos - parent.textContent.length; } } else { for (let i = 0; i
    .editor { height: 100px; width: 400px; border: 1px solid #888; padding: 0.5rem; white-space: pre; } em, strong{ font-size: 1.3rem; }
                  
      最新下载
      更多>
      网站特效
      网站源码
      网站素材
      前端模板
      关于我们 免责声明 Sitemap
      PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!