前言

目前,网上很多在线运行 HTML 的页面,大都是这样的逻辑:

上面一个代码框 <textera> ,下面一个 <iframe>,然后通过 js,将我们的代码框中的 HTML 给输入到 <iframe> 里面,这便是一个简单的在线运行 html 的逻辑。

甚至我们可以在一行里写一个在线运行 html 的页面。比如下面这个,一个高度精简的简短的 HTML JS CSS代码:

<body oninput="i.srcdoc=h.value+'<style>'+c.value+'</style><script>'+j.value+'</script>'"><style>textarea,iframe{width:100%;height:50%}body{margin:0}textarea{width:33.33%;font-size:18}</style><textarea placeholder=HTML id=h></textarea><textarea placeholder=CSS id=c></textarea><textarea placeholder=JS id=j></textarea><iframe id=i>

如果想运行,很简单,将下面这个代码直接粘贴到浏览器框里,并回车即可:

data:text/html,<body oninput="i.srcdoc=h.value+'<style>'+c.value+'</style><script>'+j.value+'</script>'"><style>textarea,iframe{width:100%;height:50%}body{margin:0}textarea{width:33.33%;font-size:18}</style><textarea placeholder=HTML id=h></textarea><textarea placeholder=CSS id=c></textarea><textarea placeholder=JS id=j></textarea><iframe id=i>

效果如下

上面三个框分别是 HTML CSS JS 代码,直接通过 i.srcdoc=h.value+'<style>'+c.value+'</style><script>'+j.value+'</script> 这样一行 js 便赋予给了 <iframe>,然后就能在线运行我们的 前端代码 了。

原理就是这样,无论是菜鸟教程里的代码运行框,还是大名鼎鼎的 codepen.io 这种代码分享库,其原理都差不多。

看,长的一模一样。

问题

但这个时候,就出现了一个问题。

我们有可能,不喜欢在一个页面里夹杂着另一个页面,感觉太闷了,如果 HTML 代码是在一个新的页面运行里就好了。其实实现不难,就是有点费资源。

渐渐我,我就忘了这个问题。

一个偶然

去年,在改进我制作的一个在某 CMS 平台运行的图片压缩插件的时候,我忘了是从哪里复制过来的一个代码,让我这个插件有了这样一个好功能 : 单击某个按钮可以在新的标签页预览一个图片。至于原程序这里放不下,但我们可以这样体验一下,在 F12 浏览器 JS 控制台输入下面的代码:

const imgurl = 'https://assets.cnblogs.com/logo.svg';  // 博客园的 logo 地址
const imgTempBlob = new Blob(['<img style="max-width:100%" src="'+ imgurl +'">'], {type: 'text/html'});
const imgBlobObjurl = window.URL.createObjectURL(imgTempBlob);
window.open(imgBlobObjurl, '_blank');
URL.revokeObjectURL(imgBlobObjurl); // 如果不添加这一行,那么那个地址会一直有效,直到浏览器自己清除

然后浏览器会马上打开一个新页面,然后将 博客园 的 logo 给展示了出来!如图所示:

我当时被震惊了。

在过去,我对 js 的二进制的理解只有 ArrayBuffer base64 Blob 三者,且这三者是可以互相转化的。直到今天,我才知道在 JS 二进制 世界里竟然还有一个 createObjectURL 这样一个方法。

createObjectURL 可以把内存里的一个东西,比如一个字符串、一个图像二进制,等,转换成一个 URL,这样你就可以使用 DOM 渲染出来。比如你加载了一张图片,然后修改了一些内容,接下来要渲染到网页上,就会用到这个函数方法。

当然,我们也要注意,每次调用 createObjectURL() 时,都会创建一个新的对象 URL,即使已经为同一个对象创建了一个 URL。当不再需要这些对象时,必须通过调用 URL.revokeObjectURL() 来释放它们,浏览器会在卸载文档时自动释放对象 URL;然而,为了优化性能和内存使用,如果在安全时间内可以明确卸载,就应该卸载。createObjectURL() 创建的 URL 会占用内存,如果不手动释放,可能会导致内存泄漏。

这个真是好用,而且用在图片的预览上真的太恰当了!为什么很少见到有人使用它呢?或者说我几乎没在别的地方见过 xxx.com/c533df96-d49e-49af-9a8c-bdbab35b7baf 类似的地址呢?可能是会造成性能上的不妥吧。

我后来发觉到里面的 HTML 代码,我感觉它可以再复杂一点。

它可以不预览图片,它可以作为 HTML 在线编辑器的最彻底的预览!

预览 HTML 代码

我们可以写一个简单的文本框,然后写一个按钮,让按钮在单击后,在新的页面预览我们的代码运行效果。代码如下:

<textarea id="htmlcode" placeholder="在此输入 HTML 代码"></textarea><br>
<button id="runcode">在新窗口预览</button> <script>
runcode.onclick = function() {
const codeBlob = new Blob([`<meta charset="utf-8">` + htmlcode.value], {type: 'text/html'});
const codeTempUrl = window.URL.createObjectURL(codeBlob);
window.open(codeTempUrl, '_blank');
URL.revokeObjectURL(codeTempUrl);
}
</script>

运行效果如下:

单击按钮后,

很不错。然后我们就可以根据我们自身的需要,为其添加这样的功能!

  • 在写代码的时候,添加保存快捷键,让其保存到我们的浏览器的 localStorage 里,防止丢失代码
  • 设置一个保存按钮
  • 把 CSS 和 JS 也搞里头,单独设置两个框
  • 一键清除记录
  • 简单的把界面给美化一下
  • 可以将当前的页面转化为 HTML 文件下载

然后我就搞成了这个样子!

大家可以单击这个地址,查看运行效果! https://www.ccgxk.com/528.html 我的站点上的一个页面将其嵌入其中了。

我感觉真的很实用。我在使用 Gemini 这种对前端输出比较厉害的 AI 的时候,它会给我输出一大堆的 HTML ,有时候还会分 CSS 和 JS 输出,我使用这个页面来辅助测试的话还不错,比类似于 codepen 那种页面好用很多。

下面是目前的全部代码,可能还有很多地方要改进的,我之后在使用过程中会进行迭代,并实时更新到我的站点上的那个页面上

<style>
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
} button { cursor: pointer; } .code-textarea {width: 100%;height: 250px;padding: 1em;background-color: azure; border: 0; outline: 0;} #htmlcode::::placeholder { color: grey; } .code-action-btn {float: right;background-color: antiquewhite;border: 0;width: 60px;height: 50px;font-size: 22px;padding: 0;}
</style> <div style="width: 600px;max-width: 100%;">
<textarea placeholder="在此处输入 HTML 代码,单击下面的【运行】,浏览器会新建一个空白标签页运行预览..." name="code" id="htmlcode" class="code-textarea"></textarea><br>
<button class="code-action-btn" id="runhtml">运行</button>
<button class="code-action-btn" id="dwnhtml" style="margin-right: 10px;">下载</button>
<button class="code-action-btn" id="delhtml" style="margin-right: 10px;">清空</button>
<button class="code-action-btn" id="savehtml" style="margin-right: 10px;">保存</button>
<details>
<summary style="cursor: pointer;background-color: cornsilk;width: fit-content;">CSS + JS
<div id="res_info" style=" display: inline; margin-left: 10px; background-color: white; padding-left: 10px; "></div>
</summary>
CSS <br>
<textarea class="code-textarea" name="code" id="csscode"></textarea><br>
JS <br>
<textarea class="code-textarea" name="code" id="jscode"></textarea>
</details>
</div> <script>
// 生成随机字符串
function generateRandomString(length = 8) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
} // 浏览器运行后检测本地 storage
const codeData = (JSON.parse(localStorage.getItem('codeData'))) || false;
if (codeData) {
htmlcode.value = codeData.htmlcode;
csscode.value = codeData.csscode;
jscode.value = codeData.jscode;
} // 点击 运行 事件
runhtml.onclick = function() {
saveHtmlFunc();
let testBlob;
if (csscode.value || jscode.value) {
testBlob = new Blob([`<meta charset="utf-8">` + `<style>` + csscode.value + `</style>` + htmlcode.value + `<script>` + jscode.value + `</sc` + `ript>`], {
type: 'text/html'
});
} else {
testBlob = new Blob([`<meta charset="utf-8">` + htmlcode.value], {
type: 'text/html'
});
}
const codeTempUrl = window.URL.createObjectURL(testBlob);
window.open(codeTempUrl, '_blank');
URL.revokeObjectURL(codeTempUrl);
} // 保存 HTML 函数
function saveHtmlFunc() {
const codeContent = {
htmlcode: htmlcode.value,
csscode: csscode.value,
jscode: jscode.value
};
localStorage.setItem('codeData', JSON.stringify(codeContent));
res_info.innerHTML = "<span style='animation: fadeOut 3s forwards; opacity: 1;'>已暂存</span>";
} // 按下保存快捷键后
document.addEventListener('keydown', function(e) { // 保存快捷键
if (e.keyCode == 83 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
e.preventDefault();
saveHtmlFunc();
}
}); // 点击 下载 事件
dwnhtml.onclick = function() {
if (!htmlcode.value) {
return 0;
}
const fileName = generateRandomString() + '.html';
let testBlob;
if (csscode.value || jscode.value) {
testBlob = new Blob([`<meta charset="utf-8">` + `<style>` + csscode.value + `</style>` + htmlcode.value + `<script>` + jscode.value + `</sc` + `ript>`], {
type: 'text/html'
});
} else {
testBlob = new Blob([`<meta charset="utf-8">` + htmlcode.value], {
type: 'text/html'
});
}
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(testBlob);
downloadLink.download = fileName;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadLink.href);
} // 点击 清空 事件
delhtml.onclick = function() {
htmlcode.value = '';
csscode.value = '';
jscode.value = '';
localStorage.removeItem('codeData');
} // 点击 保存 事件
savehtml.onclick = function() {
saveHtmlFunc();
}
</script>

一个神奇的JS代码,让浏览器在新的空白标签页运行我们 HTML 代码(createObjectURL 的妙用)的更多相关文章

  1. webdriver高级应用- 浏览器中新开标签页(Tab)

    #encoding=utf-8 import unittest from selenium import webdriver import time import win32api, win32con ...

  2. 一个神奇的JS混淆,JSFuck!

    JSFuck,整体由6个字符[, ], (, ), !, +组成,但却是可以正常运行的JS代码,JSFuck程序可以在任何Web浏览器或引擎中运行解释JavaScript! 看一段代码,源代码为:do ...

  3. 奇怪也哉!做一个WebApp居然遇到了FF浏览器进不去某页的问题。

    一个小WebApp,原定login完后就进某页面.前天晚上之前一直在FF浏览器上调试,都正常. 前天晚上稍修改了,提交war上传,之后就休息了. 昨天晚上有些事情处理打开一看,乖乖,点登录后就在登陆页 ...

  4. google浏览器打开新的标签页显示http://www.google.com.hk/url?sa=p&hl=zh-CN&……

    chrome的版本:51.0.2704.106 m使用该版本的chrome后,每次打开新标签页,都会提示“无法访问此网站”.并自动跳转到一个地址“http://www.google.com.hk/ur ...

  5. 用JS实现控制浏览器F12与右键功能

    本文出至:新太潮流网络博客 用JS实现控制浏览器F12与右键功能,防止恶意窃取代码,或其他直接复制进去就好 //禁用右键 document.oncontextmenu = function () { ...

  6. Web前端开发最佳实践(10):JavaScript代码不好读,不好维护?你需要改变写代码的习惯

    前言 这篇文章本应该在上一篇文章:使用更严格的JavaScript编码方式,提高代码质量之前发布,但当时觉得这篇文章太过基础,也就作罢.后来咨询了一些初级的开发者,他们觉得有必要把这篇文章也放上来.尽 ...

  7. Node.js 抓取电影天堂新上电影节目单及ftp链接

    代码地址如下:http://www.demodashi.com/demo/12368.html 1 概述 本实例主要使用Node.js去抓取电影的节目单,方便大家使用下载. 2 node packag ...

  8. 在IDEA中停止和关闭SonarLint自动检查,手动运行SonarLint检查代码

    关闭SonarLint自动检查代码 有时敲一行代码SonarLint插件就会自动检查,让人感觉很不舒服,还会使电脑卡顿: 依次点击:File -> Settings 或直接Ctrl+Alt+S ...

  9. js进阶解决浏览器缓存不能自动更新的问题(在ajax的url上带上一个参数,可以是日期,或者是随机数)(随机数Math.random)(取得日期的毫秒数:new Date().getTime();)

    js进阶解决浏览器缓存不能自动更新的问题(在ajax的url上带上一个参数,可以是日期,或者是随机数)(随机数Math.random)(取得日期的毫秒数:new Date().getTime();) ...

  10. TraceGL监控Node.js应用或者浏览器JavaScript代码

    https://github.com/traceglMPL/tracegl TraceGL能够监控Node.js应用或者浏览器JavaScript代码的运行过程和细节.可视化的用户界面也很友好

随机推荐

  1. C#使用yield关键字提升迭代性能与效率

    前言 yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,减少了内存占用,并允许在迭代时执行复杂逻辑. 传统迭代和yield迭代方式对比 咱们来看看传统迭代方式和yie ...

  2. 自定义Ollama安装路径

    由于Ollama的exe安装软件双击安装的时候默认是在C盘,以及后续的模型数据下载也在C盘,导致会占用C盘空间,所以这里单独写了一个自定义安装Ollama安装目录的教程. Ollama官网地址:htt ...

  3. 服务器安装jdk,tomcat,mysql等全系列

    安装jdk 执行如下命令, 然后等待安装完成 yum install -y java-1.8.0-openjdk-devel.x86_64 安装完毕后执行如下命令查看版本 java -version安 ...

  4. Doris名词解释

    1.Tablet:Doris 表的逻辑分片,一个表有多个分片 2.Replica:分片的副本,默认一个分片有3个副本 3.Healthy Replica:健康副本,副本所在 Backend 存活,且副 ...

  5. 另辟新径实现 Blazor/MAUI 本机交互(二)

    Maui 基础 Preferences 是 .NET MAUI 提供的一个静态类,用于存储和检索应用程序的首选项(即设置或配置).它提供了一种简单的键值对存储机制,可以跨平台使用.每个平台使用其本地的 ...

  6. 告别 DeepSeek 系统繁忙,七个 DeepSeek 曲线救国平替入口,官网崩溃也能用!

    前言 DeepSeek作为一款备受瞩目的国产大模型,以其强大的功能和卓越的性能赢得了众多用户的青睐.然而,随着用户量的激增,DeepSeek官网近期频繁遭遇服务器繁忙甚至崩溃的问题,给广大用户带来了不 ...

  7. Arduino语法--运算符

    本节介绍最常用的一些Arduino运算符,包括赋值运算符.算数运算符.关系运算符.逻辑运算符和递增/减运算符. 一. 赋值运算符 =(等于)为指定某个变量的值,例如:A=x,将x变量的值放入A变量. ...

  8. win - [01] 修改网络连接名称(网络1、网络2...网络10)

    修改网络连接的名称 1.打开运行窗口:Windows 键 + R 2.在运行窗口输入 regedit,打开注册表编辑器 3.在 HKEY_LOCAL_MACHINE\SOFTWARE\Microsof ...

  9. Visual Studio 卸载

    1.找个安装镜像文件 2.必须以管理员身份运行cmd 3.在CMD里输入"G:\vs_professional.exe /uninstall /force" 4.企业版就把prof ...

  10. Ubuntu 22.04 添加 AppImage 到应用程序

    前言 AppImage 逐渐成为 Linux 常用的一种软件包格式,本文将介绍如何将 AppImage 文件添加到 Ubuntu 的应用程序中. 如下图中的 CAJViewer : 操作过程 设置相关 ...