You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在线代码执行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>

代码解释

  1. 唯一标识与步骤跟踪:

    • 每个输入/输出元素都带有data-step属性,并且ID格式为input-{step}/output-{step},确保能精准定位对应元素
    • currentMaxStep变量跟踪当前最大的步骤数,用于判断是否需要创建新的输入框
  2. 全局变量移除:

    • 不再使用全局的input/output变量,而是通过parse函数的第二个参数el获取当前触发事件的输入元素,避免了旧元素被覆盖的问题
  3. 两种场景处理:

    • 新输入执行:当当前步骤等于currentMaxStep时,执行代码后创建新的输入框
    • 旧输入重评估:当当前步骤小于currentMaxStep时,仅更新对应的输出内容,保持焦点在当前输入框,不会生成新的输入/输出
  4. 冒号逻辑保留:

    • 检查输入文本是否以冒号结尾,若是则执行代码但不生成/更新输出Div,符合你的设计需求
  5. 错误处理:

    • 添加了try-catch块处理代码执行错误,错误信息会显示在对应的输出Div中(如果没有冒号的话)

这样修改后,你就可以回退到任何之前的输入框修改内容,按回车后会重新评估代码并更新对应的输出,而不会生成新的输入/输出框;同时新输入的分步执行功能也完全保留。

内容的提问来源于stack exchange,提问作者user10993028

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:15:38