想要在您的网站上显示实时代码示例是很常见的,如果您今天想这样做,有一些服务可以提供这种能力。然而,如果你像我一样,你宁愿自己动手。在本教程中,我将为该站点构建自己的类似代码笔的编辑器。

我的目标比大多数人的用例更复杂,因为该站点使用称为MDXT的降价编译器(这是为该站点制作的自定义编译器);我想将此处的代码包含到 MDXT 语言中。因此,考虑到这一点,我将有两个版本并回顾每个版本的工作原理。第一个版本将介绍如何将输入textarea与 链接iframe并动态更新它。第二个将保留第一个的核心方面,但旨在处理同一页面上的多个编辑器。

为了保持本文档的整洁,我不会在示例中包含样板 HTML,但如果您需要,请输入docVSCode 并将示例粘贴到 body 标记中。

独立编辑器

从编辑器的标记开始,我们将<textarea>在页面正文中包含三个标签以及一个<iframe>. 如果你跳到下面看,你可以看到每个文本区域都有一个data-lang属性。当我们到达那个点时,这将给我们一个非常性感的方法来与文档更新器交互。我没有在本教程中包含样式,因为我还没有为我的编辑器设计样式。...

<textarea data-lang="html" cols="30" rows="10"></textarea>
<textarea data-lang="css" cols="30" rows="10"></textarea>
<textarea data-lang="js" cols="30" rows="10"></textarea>
<iframe frameborder="0"></iframe>

进入 Javascript,我们会将所有内容封装在一个名为makeEditoriframe对于这个片段,我们没有检测到将成为编辑器预览的 iframe,所以我选择从文档中获取元素。可编辑的代码将存储在一个对象中,我们将在编辑时更新文档值。接下来,我们需要获取文本区域,因此我们使用[data-lang]添加到每个文本区域的属性。然后我们遍历文本区域对象,将虚拟文档(doc对象)设置为等于文本区域当前状态的值。这对于允许在页面加载时进行静态呈现很重要。我们正在使用data-lang属性的值作为对象的键;这是(在我看来)最干净的方法。它消除了使用 if 语句检查我们是否有正确输入的需要。

我们将使用该keyup事件用更改后的代码更新 iframe 中的文档。我们再次使用事件目标的值和数据集来选择和设置文档状态。现在进入我们创建虚拟文档的部分,我们正在使用该函数来获取我们与事件侦听器保持同步的对象update()的状态,并将值转换为 HTML 文档字符串。doc一旦我们有了 HTML 文档,我们就可以将文本转换为包含页面的 Blob。使用URL.createObjectURL,我们可以获取 Blob 的输出并虚拟创建一个本地文件,我们可以获取该文件并将其设置为 iframe 的源!我们称update()for 循环末尾的函数最初创建具有文本区域初始状态的 iframe 预览。这就是使我们能够静态呈现编辑器的原因。

function makeEditor() {
  const frame = document.querySelector("iframe");
  let doc = {
    html: "",
    css: "",
    js: "",
  };

  let ta = document.querySelectorAll("[data-lang]");

  for (let i = 0; i < ta.length; i++) {
    doc[ta[i].dataset.lang] = ta[i].value;
    ta[i].addEventListener("keyup", (e) => {
      doc[e.target.dataset.lang] = e.target.value;
      update();
    });
  }
  update();
  function update() {
    const html = `<!DOCTYPE html><html><head><style>${doc.css}</style></head><body>${doc.html}<script>${doc.js}</script></body></html>`;
    const blob = new Blob([html], { type: "text/html" });
    frame.src = URL.createObjectURL(blob);
  }
}

makeEditor();

创建编辑器组件

Code Pen 类编辑器

我将是第一个承认我不是最伟大的设计师的人。这就是为什么我的大部分样式都使用 CSS 库的原因。但是,上面是我们将在下一部分制作的编辑器的屏幕截图。这个版本将与以前的版本不同,因为我们将寻求在一个页面中呈现多个编辑器而不会溢出它们之间的数据。

组件

下面是组件的 HTML 结构;它的工作方式与前一个相同。这个更加结构化,文本区域在底部,iframe 在顶部。该容器div有一个自定义editor属性,可以让我们在我们将创建的新函数中搜索它makeEditor。从这个editordiv 中,我们可以为里面的元素设置样式;在这里,我们给了文本区域标签,告诉用户什么是什么。

<div editor>
        <iframe frameborder="0"></iframe>
        <div>
          <label for="html">
            HTML
            <textarea name="html" data-lang="html" cols="30" rows="10"></textarea>
          </label>
          <label for="css">
            CSS
            <textarea name="css" data-lang="css" cols="30" rows="10"></textarea>
          </label>
          <label for="js">
            JS
            <textarea name="js" data-lang="js" cols="30" rows="10"></textarea>
          </label>
        </div>
</div>

Javascript 的工作方式与上述相同,但我们使用一种在函数内部限定值范围的方法,以保持全局命名空间的清洁。一行一行,我们首先抓取所有具有编辑器属性的元素,这是我们之前定义的容器。我们知道编辑器里面有什么,所以我们可以将每个传递给一个名为spawn. spawn 函数与前一个函数的代码相同,因此我们可以跳过大部分代码,但我们所做的一个重要更改是将iframedoc传递到更新函数中,以便它知道从哪个文档中提取以及将其放在何处.

function makeEditor() {
  const editors = document.querySelectorAll("[editor]");
  for (let a = 0; a < editors.length; a++) {
    spawn(editors[a]);
  }
  function spawn(editor) {
    let doc = {
      html: "",
      css: "",
      js: "",
    };

    let ta = editor.querySelectorAll("[data-lang]");
    let frame = editor.querySelector("iframe");
    for (let i = 0; i < ta.length; i++) {
      doc[ta[i].dataset.lang] = ta[i].value;
      ta[i].addEventListener("keyup", (e) => {
        doc[e.target.dataset.lang] = e.target.value;
        update(doc, frame);
      });
      update(doc, frame);
    }
  }

  function update(doc, frame) {
    const html = `<!DOCTYPE html><html><head><style>${doc.css}</style></head><body>${doc.html}<script>${doc.js}</script></body></html>`;
    const blob = new Blob([html], { type: "text/html" });
    frame.src = URL.createObjectURL(blob);
  }
}

makeEditor();

如果您想了解我是如何设计那个样式的,这里是屏幕截图中使用的 CSS。

[editor] {
  width: 100%;
  background: #18232c;
  padding: 10px;
  margin: 20px;
  border-radius: 4px;
}
[editor] > iframe {
  background: #fff;
  width: 100%;
  height: 300px;
}
[editor] > div {
  display: flex;
  justify-content: space-between;
  height: 300px;
  padding-top: 10px;
}
[editor] > div > label {
  margin-bottom: 20px;
  color: #fff;
  background: #cdcdcd;
  font-family: Arial, Helvetica, sans-serif;
}
[editor] > div > label > textarea {
  background: #1c2a36;
  color: #cdcdcd;
  padding: 10px;
  width: 100%;
  height: 100%;
  border-radius: 4px;
}

为了在您的编辑器中添加突出显示,我在此处构建了第二个教程来展示已完成的产品。

via https://decode.sh/building-a-code-pen-type-editor-from-scratch

从头开始构建 CodePen 类型编辑器
标签: