我在 2021 年底,就下载了海量的 No Starch 电子书,但直到最近,还开始读第一本 Understanding ECMAScript 6,我想做一个在线的 JavaScript Runner,比 Chrome 控制台更直观、方便的运行、测试 JavaScript,虽然 W3Schools Tryit Editor 也很好,但它的定位主要在 DOM 开发,必须搭配 HTML 输出,稍显复杂。
问:
请用 HTML JavaScript 开发一个示例 Web 程序,在页面上模拟 JavaScript 代码运行,并输出结果。
界面及功能:
- 在第一个
textarea
中输入 JavaScript,要求使用 CodeMirror 实现语法高亮,接下来一个运行按钮,点击运行,在运行按钮下方的第二个textarea
中,展示上面 JavaScript 代码console.log()
函数输出的结果; - 用户可以编辑修改第一个
textarea
中的代码,再次点击运行按钮,观察结果的变化; - 该程序的 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("<", "<").replace(">", ">");
似乎可以,但是否还有其它的隐患呢?
问:
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 标签被转义了, >
变成了 >
<
变成了 <
请写一个 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:唉,我给了答案,你又看不懂,只能换一个方法,将原答案重新推一次了。
其实两个函数的思路是一致的,用 textContent
或 innerText
还原 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
元素。然后调用 textarea
的 select
方法,将 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 更好的老师了!