使用contenteditable的div实现代码高亮与预览功能问题
解决contenteditable编辑器的源码显示与预览问题
问题核心
- 使用textarea时,预览功能正常,但无法实现语法高亮
- 改用contenteditable属性的div后,语法高亮正常,但出现两个问题:
- 预览面板输出
undefined - 输入HTML标签(如
<h1>标题</h1>)会被直接渲染为元素,而非显示源码
- 预览面板输出
修复方案
1. 修复预览undefined问题
contenteditable元素没有value属性,需通过innerText获取编辑器内容,替换原代码中document.getElementById("editor").value为document.getElementById("editor").innerText。
2. 实现源码显示(避免HTML渲染)
输入HTML特殊字符时,需将其转义为实体字符(<→<,>→>),确保浏览器将其视为文本而非HTML元素。同时在高亮处理时,基于转义后的内容生成带高亮的HTML。
3. 调整光标定位逻辑
修改innerHTML后光标会丢失,需重新计算光标位置,确保编辑体验流畅。
修改后的完整代码
index.html
<div id="editor" contenteditable="true" oninput="showPreview()"> <!-- HTML CODE TO PRINT --> <h1>This is a Heading</h1> <p>This is a paragraph.</p> <!-- --> </div> <h3>PREVIEW</h3> <div class="preview-area"> <iframe id="preview-window"></iframe> </div>
style.css
#editor { width: 400px; height: 100px; padding: 10px; background-color: #444; color: white; font-size: 14px; font-family: monospace; white-space: pre-wrap; /* 保留换行和空格格式 */ } .statement { color: orange; }
javascript.js
// PREVIEW function showPreview() { // 使用innerText获取编辑器文本内容 var editorContent = document.getElementById("editor").innerText; var frame = document.getElementById("preview-window").contentWindow.document; frame.open(); frame.write(editorContent); frame.close(); } // 语法高亮与转义处理 var keywords = ["DIV", "H1", "P", "<", ">", "/"]; document.querySelector('#editor').addEventListener('keyup', e => { // 获取原始文本并转义特殊字符 let rawText = e.target.innerText; let escapedText = rawText.replace(/</g, '<').replace(/>/g, '>'); // 分割文本并匹配关键词 let chunks = escapedText.split(new RegExp( keywords.map(w => `(${w.replace(/[<>/]/g, '\\$&')})`).join('|'), 'i' )).filter(Boolean); // 生成高亮标记 let markup = chunks.reduce((acc, chunk) => { let isKeyword = keywords.some(key => chunk.toUpperCase() === key.toUpperCase()); acc += isKeyword ? `<span class="statement">${chunk}</span>` : `<span class='other'>${chunk}</span>`; return acc; }, ''); e.target.innerHTML = markup; // 重新定位光标到文本末尾 let range = document.createRange(); let sel = window.getSelection(); let lastChild = e.target.lastElementChild || e.target; range.setStart(lastChild, lastChild.textContent.length); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); e.target.focus(); });
内容的提问来源于stack exchange,提问作者Nodigap
相关产品推荐
相关产品推荐

