在线代码执行Editable Div优化:实现分步执行与回退重评估兼容
解决分步执行代码编辑器的回退重评估问题
我明白你遇到的痛点:现在的编辑器在新输入时能正常分步执行,但回退修改旧输入后按回车,会错误地生成新的输出和输入框,而不是重新评估当前修改的代码。问题的核心在于没有给每个输入/输出组分配唯一标识,同时依赖了全局变量来跟踪输入输出元素,导致无法正确定位到当前操作的元素。
下面是完整的解决方案,我会一步步解释关键改动:
关键改进点
- 给每个输入/输出元素添加唯一的
data-step属性,用于关联对应的输入和输出 - 移除全局的
input/output变量,改为通过事件源获取当前操作的元素 - 在
parse函数中区分新输入执行和旧输入重评估两种场景 - 保留“输入末尾带冒号不生成输出”的设计需求
修改后的完整代码
JavaScript 部分
// 跟踪当前最大步骤数,初始为0 let currentMaxStep = 0; // 创建输入Div,分配唯一的step标识 function CreateInputDiv(step = null) { // 如果没有指定step,就自动递增 const newStep = step || ++currentMaxStep; const input = document.createElement("div"); input.setAttribute("id", `input-${newStep}`); input.setAttribute("class", "input"); input.setAttribute("contenteditable", "true"); input.setAttribute("data-step", newStep); input.setAttribute("onkeypress", "parse(event, this)"); input.innerHTML = " "; // 修正原代码的转义错误 document.getElementById('calc').appendChild(input); input.focus(); // 更新计数器文本域 updateCounter(); return newStep; } // 创建输出Div,关联对应的step function CreateOutputDiv(step) { const output = document.createElement("div"); output.setAttribute("id", `output-${step}`); output.setAttribute("class", "output"); output.setAttribute("tabindex", "0"); output.setAttribute("contenteditable", "true"); output.setAttribute("data-step", step); document.getElementById('calc').appendChild(output); return output; } // 更新左侧计数器 function updateCounter() { const countEl = document.getElementById("count"); countEl.value = Array.from({length: currentMaxStep}, (_, i) => i + 1).join('\n'); } // 核心解析函数,区分新输入和重评估 function parse(e, el) { const key = e.keyCode || e.which; if (key !== 13) return; // 只处理回车事件 e.preventDefault(); const inputText = el.innerText.trim(); const currentStep = parseInt(el.getAttribute("data-step")); let hasColon = inputText.endsWith(':'); // 移除末尾的冒号用于计算 const evalText = hasColon ? inputText.slice(0, -1).trim() : inputText; try { const result = eval(evalText); console.log(`Step ${currentStep} 执行结果:`, result); // 处理输出逻辑 if (!hasColon) { // 检查是否已存在对应的输出Div let outputEl = document.getElementById(`output-${currentStep}`); if (!outputEl) { // 不存在则创建,插入到当前输入的下一个位置 outputEl = CreateOutputDiv(currentStep); el.parentNode.insertBefore(outputEl, el.nextSibling); } // 更新输出内容 outputEl.innerHTML = result; } // 判断是否需要创建新的输入Div:只有当前是最后一个步骤时才创建 if (currentStep === currentMaxStep) { CreateInputDiv(); } else { // 重评估旧输入,保持焦点在当前输入框 el.focus(); } } catch (err) { // 处理代码执行错误 console.error(`Step ${currentStep} 执行错误:`, err); if (!hasColon) { let outputEl = document.getElementById(`output-${currentStep}`) || CreateOutputDiv(currentStep); outputEl.innerHTML = `错误: ${err.message}`; el.parentNode.insertBefore(outputEl, el.nextSibling); } el.focus(); } }
HTML 部分(仅需修改初始化脚本)
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="JavaS.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdn.plot.ly/plotly-latest.min.js"></script> <meta charset="utf-8" /> <style> div:focus { background-color: lightpink; } .input { background-color: lightgreen; width: 980px; border: none; font-size: 16px; resize: none; overflow: auto; overflow-wrap: break-word; } .output { background-color: lightblue; width: 980px; border: none; font-size: 16px; resize: none; overflow-wrap: break-word; } #count { background-color: lightblue; color: black; width: 25px; height: 650px; font-size: 17px; resize: none; overflow: auto; border: none; } #calc{ background-color:lightblue; overflow: scroll; vertical-align: top; border: none; } </style> </head> <body bgcolor="grey"> <table align="center" width="1000px" height="650px" bgcolor="lightblue"> <tr> <td><textarea id="count" disabled></textarea></td> <td id = "calc"></td> </tr> </table> <script> // 初始化第一个输入框 CreateInputDiv(); </script> </body> </html>
代码解释
唯一标识与步骤跟踪:
- 每个输入/输出元素都带有
data-step属性,并且ID格式为input-{step}/output-{step},确保能精准定位对应元素 currentMaxStep变量跟踪当前最大的步骤数,用于判断是否需要创建新的输入框
- 每个输入/输出元素都带有
全局变量移除:
- 不再使用全局的
input/output变量,而是通过parse函数的第二个参数el获取当前触发事件的输入元素,避免了旧元素被覆盖的问题
- 不再使用全局的
两种场景处理:
- 新输入执行:当当前步骤等于
currentMaxStep时,执行代码后创建新的输入框 - 旧输入重评估:当当前步骤小于
currentMaxStep时,仅更新对应的输出内容,保持焦点在当前输入框,不会生成新的输入/输出
- 新输入执行:当当前步骤等于
冒号逻辑保留:
- 检查输入文本是否以冒号结尾,若是则执行代码但不生成/更新输出Div,符合你的设计需求
错误处理:
- 添加了
try-catch块处理代码执行错误,错误信息会显示在对应的输出Div中(如果没有冒号的话)
- 添加了
这样修改后,你就可以回退到任何之前的输入框修改内容,按回车后会重新评估代码并更新对应的输出,而不会生成新的输入/输出框;同时新输入的分步执行功能也完全保留。
内容的提问来源于stack exchange,提问作者user10993028
相关产品推荐
相关产品推荐

