简单实现HTML在线编辑器

我们继续来看一下如何开发一个简单的html在线编辑器,要求很简单 能够同时编辑html,css,js代码,并且运行之后可以同时预览效果


一:前置知识

在H5中设置了一个新的标签,<iframe>, 用于在当前 HTML 文档中嵌入另一个独立的 HTML 文档。这是实现在线编辑器预览功能的关键,因为它提供了一个沙箱环境,内部的 HTML、CSS、JS 不会直接影响到父页面。所以我们来简单学习一下该标签元素的使用。

(1)核心属性和方法 (用于向 iframe 写入内容):

iframe 比较特殊,因为它内部有自己独立的 window 对象和 document 对象。你需要先访问到它们:

  • previewFrame.contentWindow: 返回 iframewindow 对象。
  • previewFrame.contentDocument: 返回 iframedocument 对象。
    • 注意:在某些旧浏览器或特定情况下,contentDocument 可能不可用,更通用的方式是 previewFrame.contentWindow.document

假设我们先获取了一个iframe元素

javascript 复制代码
const previewFriame =document.getElementByID("preview-frame")
方法一 :使用document.write()

这是讲HTML字符串斜土iframe内部文档的传统写法

javascript 复制代码
    const htmlCode = "<h1>Hello from iframe</h1><p>This is a test.</p>";
    const iframeDoc = previewFrame.contentWindow.document;

    iframeDoc.open();     // 1. 打开文档流,准备写入
    iframeDoc.write(htmlCode); // 2. 将 HTML 字符串写入
    iframeDoc.close();    // 3. 关闭文档流,浏览器会解析并显示内容

通过沙箱的contentWindow获取沙箱的内部环境再通过document写入内容

· open(): 清除 `iframe` 中当前的所有内容,并打开一个新的文档流准备接收数据。

· write(): 将字符串写入到打开的文档流中。你可以多次调用 `write()` 来追加内容。

· close(): 关闭文档流。这个步骤很重要,它告诉浏览器所有内容都已写入完毕,可以开始解析和渲染了。如果不调用 `close()`,`iframe` 可能会一直处于加载状态。

方法二:使用`srcdoc` 属性 (更现代,推荐)

`srcdoc` 属性允许你直接将完整的 HTML 内容作为字符串赋值给它。浏览器会将其作为 `iframe` 的源文档内容进行渲染。

javascript 复制代码
  const htmlCode = "<h1>Hello via srcdoc</h1><p>Modern way!</p>";
    previewFrame.srcdoc = htmlCode;

使用这种方法也有一些优点:

1.更安全:比 `document.write` 在某些情况下更安全,可以减少 XSS 风险(虽然在这个项目中你主要写入自己控制的代码,风险较低)。

2.更简洁:一行代码搞定。

3.行为更像加载一个真正的文档

方法三: 操作 `iframe` 内部 DOM (如果只需要修改部分内容,而不是整个文档)

如果你不是要替换整个 `iframe` 的内容,而是想修改它内部已有的某个元素,可以这样做:

javascript 复制代码
// 假设 iframe 内部已经有一个 id 为 "title" 的 h1 元素
 const iframeDocument = previewFrame.contentWindow.document;
 const titleElementInIframe = iframeDocument.getElementById('title');
 if (titleElementInIframe) {
     titleElementInIframe.textContent = 'New Title in Iframe';
    }

对于在线编辑器,通常我们是替换整个预览内容,所以 `document.write()` 或 `srcdoc` 更常用。

其他属性:

1.src属性 指定iframe要加载的外部页面的url。如果你要加载一个已有的网页,会用这个。对于我们的编辑器 我们是动态生成内容 所以scrdoc或者document.write()

2.`width`, `height`: 设置 `iframe` 的宽高。

  1. `sandbox`: (高级) 用于对 `iframe` 内容施加额外的限制,增强安全性。例如,阻止脚本执行、阻止表单提交等。

二 :开发过程

我们总结一下开发任务:

  1. 获取 `textarea` 的引用

  2. 获取 `iframe` 的引用

  3. 给 `textarea` 添加 `input` 事件监听器。

  4. 在事件处理函数中:

获取 `textarea` 的 `.value` (用户输入的 HTML 代码)。

使用 `iframe.contentWindow.document.open()`, `.write()`, `.close()` **或者** `iframe.srcdoc` 将这段 HTML 代码设置到 `iframe` 中,让它显示出来。

先从这两个元素最核心的用法开始,等把 HTML 预览功能实现了,我们再考虑 CSS 和 JavaScript 的集成,那会涉及到更多 `iframe` 内部的操作。

(1)前端页面:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Html在线编译器</title>
    <link rel="stylesheet" href="./css/index.css">
    <link rel="stylesheet" href="./css/base.css">
</head>
<body class="body1">
    <div class="left">
        <div class="html">
            <button class="run-html">运行</button>
           <div class="title">html编辑器</div>
           <textarea name="html" id="1" class="htmlInput"></textarea>
        </div>
        <div class="css">
            <div class="title">css编辑器</div>
            <textarea name="css" id="2" class="cssInput"></textarea>
        </div>
        <div class="js">
            <div class="title">js编辑器</div>
            <textarea name="js" id="3" class="jsInput"></textarea>
        </div>
    </div>
    <div class="right">
        <div class="show">
            <div class="title">在线预览</div>
            <!-- iframe标签用于嵌入一个小的html文档窗口 -->
             <!-- iframe里面的内容和外部的相互独立的 互不影响 -->
            <iframe src="about:blank" frameborder="0">1</iframe>
            <!-- frameborder :这个属性用来指定是否显示 <iframe> 的边框。 0 表示没有边框, 1 表示显示边框 -->
        </div>
    </div>
    <script src="./js/index.js"></script>
</body>
</html>

(2)css

css 复制代码
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: Arial, Helvetica, sans-serif;
}
html, body {
    height: 100%;
}

body{
    background-color: #454545;
    display: flex;
}
/* 左 */
.left{
    width: 50vw;
    height: 100%;
    /* background-color: pink; */
    padding: 5vh;
}
.left .html{
    width: 100%;
    height: 30vh;
    border-radius: 5px;
    background-color: #fff;
    margin-bottom: 1vh;
    display: flex;
    flex-direction: column;
    position: relative;
}
.run-html{
    transform: translate(-110%, -110%);
    top: 100%;
    left: 100%;
    position: absolute;
    width: 10vh;
    border-radius: 2vh;
    background-color: #95e330;
}
.run-html:hover{
    background-color: #88d228;
}
.run-html:active{
    background-color: #78c220;
}
.left .css{
    width: 100%;
    height: 30vh;
    border-radius: 5px;
    background-color: #fff;
    margin-bottom: 1vh;
    display: flex;
    flex-direction: column;

}
.left .js{
    width: 100%;
    height: 30vh;
    border-radius: 5px;
    background-color: #fff;
    margin-bottom: 1vh;
    display: flex;
    flex-direction: column;

}
.left .title{
    width: 100%;
    text-align: center;
    border: 0.5px solid #ccc;
}
.left textarea{
    padding: 2px;
    width: 100%;
    height: 100%;
    border: 0;
    box-sizing: border-box;
    flex-grow: 1;
    background-color: #ccc;
}

.left textarea:focus {
    /* 点击输入框不会出现黑边 */
    outline: none;
}
/* 右 */
.right{
    width: 50vw;
    height: 100%;
    /* background-color: skyblue; */
    padding: 5vh;
}

.right .show{
    width: 100%;
    height: 92vh;
    background-color: #fff;
}

.right .title{
    width: 100%;
    text-align: center;
    border: 0.5px solid #ccc;
}

.right iframe{
    width: 100%;
    height: 100%;
}

(3)js

javascript 复制代码
const html=document.querySelector('.htmlInput');
const css=document.querySelector('.cssInput');
const js=document.querySelector('.jsInput');

const btnhtml=document.querySelector('.run-html');
const output=document.querySelector('.show iframe');
function run(){
    output.contentDocument.body.innerHTML = html.value + '<style>' + css.value + '</style>';
    const script = output.contentDocument.createElement('script');
    script.textContent = js.value;
    output.contentDocument.body.appendChild(script);
}
btnhtml.addEventListener('click',run);

就这样吧 实现这个功能如果利用这个iframe标签的话就非常简单了

相关推荐
慧一居士3 分钟前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead5 分钟前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码6 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子6 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年6 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子6 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina6 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路7 小时前
React--Fiber 架构
前端·react.js·架构
伍哥的传说7 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409198 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app