iframe 文档写入不会更新 JavaScript

Mor*_*ori 7 html javascript iframe

这是一个基本的 HTML 编辑器:

textarea,
iframe {
  width: 400px;
  height: 300px;
}
Run Code Online (Sandbox Code Playgroud)
<textarea></textarea>
<iframe></iframe>
Run Code Online (Sandbox Code Playgroud)
var textarea = document.querySelector('textarea');

function preview() {
  var iframeDoc = document.querySelector('iframe').contentDocument;
  iframeDoc.open();
  iframeDoc.write(textarea.value);
  iframeDoc.close();
}

textarea.addEventListener('input', preview);
Run Code Online (Sandbox Code Playgroud)

演示版

它会更新您放入的 HTML 和 CSS textarea,但您不能使用 JavaScriptconstlet变量,因为一旦您编辑插入的代码,它就会抛出以下语法错误:

Identifier * has already been declared

要明白我的意思,请将以下示例代码插入到textarea

<!doctype html>
<html lang="en">
<head>
  <title>Sample Code</title>
</head>
<body>
  <p>Hello!</p>
  <script>
    const p = document.querySelector('p');
    p.style.color = 'blue';
  </script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

现在改变Hello!Hello, world!bluered

有什么解决方案可以让用户继续编辑代码而不会出现该错误?

更新

受到svarlitskiy 的回答的启发,我最终决定iframe用新创建的替换:

function preview() {
  var iframe = document.createElement('iframe');
  document.querySelector('iframe').replaceWith(iframe);
  var iframeDoc = iframe.contentDocument;
  iframeDoc.write(textarea.value);
  iframeDoc.close();
}
Run Code Online (Sandbox Code Playgroud)

Plu*_*uto 5

在我看来,有两种主要方法可以做到这一点:使用srcdoc属性(@Kaiido\'s answer)或使用 blob URL。在这个答案中,我将解释这些选项是什么以及它们有何不同。我还将为您提供一个如何使用 blob URL 选项的代码示例。

\n

使用 srcdoc

\n

srcdoc属性允许您将 iframe 的 HTML 内容写入为字符串。这很简单,但也有一些问题:

\n

\xe2\x80\xa2 某些旧浏览器不支持该srcdoc属性,因此您可能需要使用 src 属性提供后备。

\n

\xe2\x80\xa2 HTML 内容可能包含可能危及主文档安全的恶意脚本或链接。在将 HTML 内容分配给srcdoc属性之前,您可能需要对其进行清理或转义。

\n

因此,如果您不介意此类问题,您可能不需要这个答案,因为使用blobURL 比使用srcdoc.

\n

使用 blob URL

\n

blob URL 方法涉及从 HTML 内容创建一个新的 blob 对象,然后将其 URL 分配给srciframe 的属性。这种方法可以避免一些兼容性和安全问题:

\n

\xe2\x80\xa2 大多数支持该src属性的浏览器都支持 blob URL,因此您无需担心后备问题。

\n

\xe2\x80\xa2 Blob URL 与主文档分离,并具有自己的来源和权限,因此它无法访问或修改主文档中的任何内容。

\n

例子:

\n

以下是如何使用 blob URL 方法的代码示例。我使用了一个debounce函数来避免过于频繁地更新 iframe,这可能会导致闪烁。您可以在此处找到有关该debounce功能的更多信息。

\n
function preview() {\n  var blob = new Blob([textarea.value], { type: "text/html" });\n  var url = URL.createObjectURL(blob);\n  iframe.onload = function () {\n    URL.revokeObjectURL(url);\n  };\n  iframe.src = url;\n}\n\n\nvar debounceTimer;\n\nfunction debounce(func, delay) {\n  return function () {\n    clearTimeout(debounceTimer);\n    debounceTimer = setTimeout(func, delay);\n  };\n}\n\nvar debouncedPreview = debounce(preview, 300);\n\ntextarea.addEventListener("input", debouncedPreview);\n
Run Code Online (Sandbox Code Playgroud)\n

\r\n
\r\n
function preview() {\n  var blob = new Blob([textarea.value], { type: "text/html" });\n  var url = URL.createObjectURL(blob);\n  iframe.onload = function () {\n    URL.revokeObjectURL(url);\n  };\n  iframe.src = url;\n}\n\n\nvar debounceTimer;\n\nfunction debounce(func, delay) {\n  return function () {\n    clearTimeout(debounceTimer);\n    debounceTimer = setTimeout(func, delay);\n  };\n}\n\nvar debouncedPreview = debounce(preview, 300);\n\ntextarea.addEventListener("input", debouncedPreview);\n
Run Code Online (Sandbox Code Playgroud)\r\n
var textarea = document.querySelector("textarea");\nvar iframe = document.querySelector("iframe");\nvar debounceTimer;\n\nfunction debounce(func, delay) {\n  return function () {\n    clearTimeout(debounceTimer);\n    debounceTimer = setTimeout(func, delay);\n  };\n}\n\nfunction preview() {\n  var blob = new Blob([textarea.value], { type: "text/html" });\n  var url = URL.createObjectURL(blob);\n  iframe.onload = function () {\n    URL.revokeObjectURL(url);\n  };\n  iframe.src = url;\n}\n\nvar debouncedPreview = debounce(preview, 300);\n\ntextarea.addEventListener("input", debouncedPreview);
Run Code Online (Sandbox Code Playgroud)\r\n
textarea,\niframe {\n  width: 400px;\n  height: 300px;\n}
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

总之,srcdoc和 blob URL 方法都可用于在 iframe 中创建 HTML 内容的实时预览。该srcdoc方法更简单,但兼容性和安全性较差,而 blob URL 方法更复杂,但兼容性和安全性更高。我更喜欢 blob URL 方法,因为它可以避免旧浏览器和恶意内容的一些潜在问题,但您可以选择适合您的需求和偏好的方法。

\n


Tes*_*ell 0

据我所知,没有办法在 JS 中取消声明变量或将它们从文档的范围中删除。我建议iframe在每个input事件上动态地重新创建一个元素。像这样的东西:

// index.js
const textarea = document.querySelector("textarea");

function preview() {
  const container = document.getElementById("iframe-container");
  const existing = document.querySelector("iframe");

  if (existing) existing.remove();

  const iframe = document.createElement("iframe");
  container?.appendChild(iframe);
  const iframeDoc = iframe?.contentDocument;

  iframeDoc?.open();
  iframeDoc?.write(textarea?.value ?? "");
  iframeDoc?.close();
}

textarea?.addEventListener("input", preview);
Run Code Online (Sandbox Code Playgroud)
<!-- index.html -->
<textarea></textarea>
<div id="iframe-container">
  <iframe></iframe>
</div>
Run Code Online (Sandbox Code Playgroud)