我在 2021 年底,就下载了海量的 No Starch 电子书,但直到最近,还开始读第一本 Understanding ECMAScript 6,我想做一个在线的 JavaScript Runner,比 Chrome 控制台更直观、方便的运行、测试 JavaScript,虽然 W3Schools Tryit Editor 也很好,但它的定位主要在 DOM 开发,必须搭配 HTML 输出,稍显复杂。

问:

请用 HTML JavaScript 开发一个示例 Web 程序,在页面上模拟 JavaScript 代码运行,并输出结果。

界面及功能:

  1. 在第一个 textarea 中输入 JavaScript,要求使用 CodeMirror 实现语法高亮,接下来一个运行按钮,点击运行,在运行按钮下方的第二个 textarea 中,展示上面 JavaScript 代码 console.log() 函数输出的结果;
  2. 用户可以编辑修改第一个 textarea 中的代码,再次点击运行按钮,观察结果的变化;
  3. 该程序的 HTML,CSS,JavaScript 请分模块编写,外部引用,特别是 JavaScript 代码,如果网络中断了,我会要求你从上次断开的地方,接着编写。

答:

以下是一个使用 CodeMirror 实现语法高亮和在页面上模拟 JavaScript 代码运行的示例 Web 程序,HTML、CSS、JavaScript 分别存储在不同的文件中并使用外部引用。如果网络中断,可以根据需要在断开的地方继续编写。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JS Code Runner</title>
  <link rel="stylesheet" href="codemirror.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <div class="editor-container">
      <textarea id="editor"></textarea>
    </div>
    <div class="button-container">
      <button id="run-btn">运行</button>
    </div>
    <div class="output-container">
      <textarea id="output" readonly></textarea>
    </div>
  </div>
  <script src="codemirror.js"></script>
  <script src="mode/javascript/javascript.js"></script>
  <script src="main.js"></script>
</body>
</html>
.container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  justify-content: center;
  align-items: center;
}

.editor-container {
  width: 100%;
  max-width: 800px;
}

#editor {
  height: 500px;
  width: 100%;
}

.button-container {
  margin-top: 20px;
}

#run-btn {
  font-size: 20px;
  padding: 10px 20px;
  background-color: #0066ff;
  color: #fff;
  border: none;
  border-radius: 5px;
}

.output-container {
  margin-top: 20px;
  width: 100%;
  max-width: 800px;
}

#output {
  height: 200px;
  width: 100%;
}
const editor = CodeMirror.fromTextArea(document.getElementById('editor'), {
  lineNumbers: true,
  mode: 'javascript',
  theme: 'dracula'
});

const runBtn = document.getElementById('run-btn');
const output = document.getElementById('output');

runBtn.addEventListener('click', () => {
  output.value = '';
  try {
    const result = eval(editor.getValue());
    console.log(result);
    output.value = result;
  } catch (error) {
    console.error(error);
    output.value = error.message;
  }
});

codemirror.css 和 codemirror.js 可以从 CodeMirror 的官方网站下载。

没有想到这么简单,我引入 CodeMirror 5.25.2,程序正常运行,但语法高亮没有效果,注释掉 theme: 'dracula' 之后,一切正常,看来是版本冲突,或者说旧版 CodeMirror 不支持 dracula 主题。

因为简单,所以简陋,只能跑最基础的代码:https://dev.iresin.cn/ivy/olpc/mode/jsrunner/

const add = (a,b) => a + b;
add(1,3);

注意,不支持 console.log 输出,也不支持复杂的结构:

// JavaScript Code
var funcs = [];
for (var i = 0; i < 10; i++) {
    funcs.push(function() {
        console.log(i);
    });
}
funcs.forEach(function(func) {
    func(); // outputs the number "10" ten times
});

而我起意做 JavaScript Runner 的目标,就是能在页面上直观的跑 JavaScript 代码,太基础,意义也不大了。

程序的核心是 eval 方法,查 MDN 的文档,惊喜的发现,Mozilla 就有完美的实现,可且可以本地运行,虽然代码已经混淆了。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval

我这是我伺服到本地的 JavaScript Runner:https://dev.iresin.cn/ivy/olpc/mode/javascript/

相对原版,增加了一个 Copy 功能,但发现复制代码时,< > 被转义,显然是 CodeMirror 为了显示 HTML 所做的功课,怎么解决呢?

innerHTML.replace("&lt;", "<").replace("&gt;", ">");

似乎可以,但是否还有其它的隐患呢?

问:

HTML 代码被转义了,请写一个 JavaScript 函数,将转义后的 HTML 代码还原。

答:

可以使用浏览器提供的 innerHTML 属性,将转义后的 HTML 字符串插入到一个 DOM 元素中,然后获取该元素的 textContent 属性,即可得到原始的 HTML 代码。

以下是一个示例函数的实现:

function unescapeHtml(htmlString) {
  const tempElement = document.createElement('div');
  tempElement.innerHTML = htmlString;
  return tempElement.textContent;
}

该函数将接收一个转义后的 HTML 字符串作为输入,将其插入到一个新创建的 div 元素中,并返回该元素的 textContent 属性,即可得到原始的 HTML 代码。

由于我自己的知识不足,我没有理解它的答案,还以为 ChatGPT 没有听明白我的问题,于是再次提问:

问:

HTML 标签被转义了, > 变成了 &gt; < 变成了 &lt; 请写一个 JavaScript 函数,将转义后的代码,还原为 HTML 标签。

答:

可以使用浏览器自带的解码函数 innerHTML 来实现将转义后的代码还原为 HTML 标签。具体实现如下:

function decodeHTML(html) {
  let el = document.createElement('div');
  el.innerHTML = html;
  return el.innerText;
}

这里先创建一个 div 元素,将转义后的 HTML 代码赋值给 div 元素的 innerHTML 属性。然后返回 div 元素的 innerText 属性,即可得到还原后的 HTML 标签。

奇怪,几乎是同样的答案,我试着调用这个方法,还真的成功了!

我估计 ChatGPT 内心的 OS:唉,我给了答案,你又看不懂,只能换一个方法,将原答案重新推一次了

其实两个函数的思路是一致的,用 textContentinnerText 还原 innerHTML,但既然如此,我何不直接用 textContent 取元素的内容呢?完美!

document.querySelector('#copy').addEventListener('click', function(){
    //const code = decodeHTML(document.querySelector('#static-js').innerHTML);
    //https://developer.mozilla.org/zh-CN/docs/Web/API/Node/textContent
    const code = document.querySelector('#static-js').textContent;
    const item = new ClipboardItem({ 'text/plain': new Blob([code], { type: 'text/plain' }) });
    const handler = navigator.clipboard.write([item]);
    //https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/promise
    handler.then(function(){
        console.log('Successfully copy to clipboard.');
    });
});

上述复制到剪贴板的功能,也是 ChatGPT 提供的。

问:

请实现一个函数,copyToClipboard,将页面上指定的 textarea 中的值,拷贝至剪贴板,要求保留原格式,换行,缩进,空格,都要完整保留。

答:

要实现复制功能,可以使用 Clipboard API。下面是一个实现 copyToClipboard 函数的例子:

function copyToClipboard() {
  const textarea = document.querySelector('#source-textarea');

  // 选中文本
  textarea.select();

  // 将选中的文本复制到剪贴板
  document.execCommand('copy');
}

该函数中,我们首先通过 document.querySelector 方法选中要复制的 textarea 元素。然后调用 textareaselect 方法,将 textarea 中的文本选中。最后,调用 document.execCommand('copy') 方法将选中的文本复制到剪贴板中。

需要注意的是,document.execCommand('copy') 方法在现代浏览器中已经被废弃,推荐使用 Clipboard API。下面是一个使用 Clipboard API 实现 copyToClipboard 函数的例子:

function copyToClipboard() {
  const textarea = document.querySelector('#source-textarea');

  // 创建 ClipboardItem
  const item = new ClipboardItem({ 'text/plain': new Blob([textarea.value], { type: 'text/plain' }) });

  // 将 ClipboardItem 内容复制到剪贴板
  navigator.clipboard.write([item]);
}

该函数中,我们首先通过 document.querySelector 方法选中要复制的 textarea 元素。然后,使用 ClipboardItem 构造函数创建一个 ClipboardItem 对象,该对象中包含要复制的文本内容。最后,使用 Clipboard API 的 navigator.clipboard.write 方法将 ClipboardItem 对象中的内容复制到剪贴板中。

需要注意的是,Clipboard API 并不是所有浏览器都支持,如果需要兼容性更好的方案,可以考虑使用 document.execCommand('copy') 方法。

一片苦心,天地可鉴。

很明显,ChatGPT 对开发的帮助,在功能模块的实现上,相对传统搜索引擎,有质的飞跃。

文章结束之前,再举一个例子,比如我想实现一个灵活的大小写转换功能,感受一下提问的技巧。

问:

请写一个 JavaScript 函数,接受 2 个参数,第一个参数为 string,第二个参数为 int。当 int 为 0,将 string 转为小写,int 为 1,string 转为大写,int 为 2,string 中的所有单词,首字母改为大写,int 为 3,string 中的所有字母,大小写反转。

答:

以下是一个接受两个参数的 JavaScript 函数,第一个参数为 string,第二个参数为 int,根据 int 的不同值,对字符串进行不同的处理:

function stringTransform(str, num) {
  if (num === 0) {
    return str.toLowerCase();
  } else if (num === 1) {
    return str.toUpperCase();
  } else if (num === 2) {
    return str.replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
  } else if (num === 3) {
    return str.replace(/[a-zA-Z]/g, (L) => L === L.toUpperCase() ? L.toLowerCase() : L.toUpperCase());
  } else {
    return "Invalid parameter!";
  }
}

使用示例:

console.log(stringTransform("hello world", 0)); // "hello world"
console.log(stringTransform("hello world", 1)); // "HELLO WORLD"
console.log(stringTransform("hello world", 2)); // "Hello World"
console.log(stringTransform("Hello World", 3)); // "hELLO wORLD"

真是再没有比 ChatGPT 和 Mozilla 更好的老师了!

最后修改:2023 年 04 月 19 日
如果觉得我的文章对你有用,请随意赞赏