Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iframe document.write Doesn't Update JavaScript Variables

I have created a basic HTML editor using a textarea and an iframe to preview HTML, CSS, and JavaScript changes. Here's the setup:

textarea,
iframe {
  width: 300px;
  height: 200px;
}
<textarea></textarea>
<iframe></iframe>
const textarea = document.querySelector('textarea');
const iframe = document.querySelector('iframe');

function preview() {
  const iframeDoc = iframe.contentDocument;
  iframeDoc.open();
  iframeDoc.write(textarea.value);
  iframeDoc.close();
}

textarea.addEventListener('input', preview);

DEMO

The code successfully updates the HTML and CSS content from the textarea into the iframe. However, you can't use JavaScript const or let variables with document.write because it rewrites the entire iframe content, causing a redeclaration error as soon as you edit the inserted code:

Identifier * has already been declared

To reproduce the issue, insert the following sample code into the 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>

Then try changing Hello! to Hello, world!, or blue to red.

What is the best solution to allow users to keep editing the code without encountering this error? Can the try...catch statement be used to handle this specific error, or is there a better approach?

like image 775
Mori Avatar asked Feb 24 '26 03:02

Mori


2 Answers

In my opinion, there are two main ways to do this: using the srcdoc attribute (@Kaiido's answer) or using a blob URL. In this answer, I will explain what these options are and how they differ. I will also give you a code example of how to use the blob URL option.

Using srcdoc

The srcdoc attribute lets you write the HTML content of the iframe as a string. This is easy and simple, but it has some problems:

• Some old browsers do not support the srcdoc attribute, so you may need to provide a fallback using the src attribute.

• The HTML content may contain malicious scripts or links that could compromise the security of your main document. You may need to sanitize or escape the HTML content before assigning it to the srcdoc attribute.

So if you don't mind such problems, you might not need this answer because using a blob URL is more complex and indirect than using srcdoc.

Using blob URL

The blob URL method involves creating a new blob object from the HTML content, and then assigning its URL to the src attribute of the iframe. This method can avoid some compatibility and security issues:

• The blob URL is supported by most browsers that support the src attribute, so you do not need to worry about fallbacks.

• The blob URL is separate from the main document and has its own origin and permissions, so it cannot access or modify anything in your main document.

Example:

Here is a code example of how to use the blob URL method. I have used a debounce function to avoid updating the iframe too often, which could cause flickering. You can find more information about the debounce function here.

function preview() {
  var blob = new Blob([textarea.value], { type: "text/html" });
  var url = URL.createObjectURL(blob);
  iframe.onload = function () {
    URL.revokeObjectURL(url);
  };
  iframe.src = url;
}


var debounceTimer;

function debounce(func, delay) {
  return function () {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(func, delay);
  };
}

var debouncedPreview = debounce(preview, 300);

textarea.addEventListener("input", debouncedPreview);

var textarea = document.querySelector("textarea");
var iframe = document.querySelector("iframe");
var debounceTimer;

function debounce(func, delay) {
  return function () {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(func, delay);
  };
}

function preview() {
  var blob = new Blob([textarea.value], { type: "text/html" });
  var url = URL.createObjectURL(blob);
  iframe.onload = function () {
    URL.revokeObjectURL(url);
  };
  iframe.src = url;
}

var debouncedPreview = debounce(preview, 300);

textarea.addEventListener("input", debouncedPreview);
textarea,
iframe {
  width: 400px;
  height: 300px;
}
<textarea></textarea>
<iframe src=""></iframe>

In summary, both the srcdoc and the blob URL methods can be used to create a live preview of HTML content in an iframe. The srcdoc method is simpler but less compatible and secure, while the blob URL method is more complex but more compatible and secure. I prefer the blob URL method because it avoids some potential problems with old browsers and malicious content, but you may choose the method that suits your needs and preferences.

like image 176
Pluto Avatar answered Feb 25 '26 16:02

Pluto


Set your iframe's srcdoc instead of reopening its Document:

var textarea = document.querySelector('textarea'),
  iframe = document.querySelector('iframe');

function preview() {
  iframe.srcdoc = textarea.value;
}

textarea.addEventListener('input', preview);
textarea,
iframe {
  width: 400px;
  height: 300px;
}
<textarea></textarea>
<iframe></iframe>
like image 36
Kaiido Avatar answered Feb 25 '26 16:02

Kaiido