Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializing an iframe's content from head to toe with HTML special characters within Javascript

I'm using an iframe as an html editor and I load its content by setting iframe's src attribute. Afterwards, I turn on iframe's designMode so I can edit the loaded html content.

Once user is done, he'll press a save button and I'll try to retrieve the edited html content and sending it to the server. It's just that I need the full content of the iframe, including the <html> and <!doctype>. The problem I've faced is that when I retrieve the iframe's content, its embedded javascript code has encoded all < into &lt;, even within Javascript code!

Here's how I wrote my code:

<!DOCTYPE html>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<iframe src="about: blank"></iframe>
<button>Save to textarea</button>
<textarea></textarea>
<script>
$(document).ready(function() {
  var $iframe = $("iframe");
  var $iframeBody = $iframe.contents().find('body');
  $iframeBody.html('<scr'+'ipt>var x = 1 < 2;</scr'+'ipt>&lt;&gt;&amp;');
  $iframe.contents().prop('designMode','on');
  $("button").click(function() {
    var serializer = new XMLSerializer();
    var html = serializer.serializeToString($iframe.contents()[0])
    $("textarea").val(html);
  });
});
</script>
</body>
</html>

Pressing the save button will output:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<script>var x = 1 &lt; 2;</script>&lt;&gt;&amp;
</body>
</html>

As you can see the result is unusable because there's no way I can tell apart which &lt; should be replaced with < (unless the text is parsed)!!

Does anyone have any idea how to retrieve the contents of an iframe completely without ruining it?

like image 301
Mehran Avatar asked Dec 02 '25 09:12

Mehran


1 Answers

First of all - you don't need XMLSerializer. You try to serialize html like xml. I think you need a html. So it will be better to use $iframe.contents().get(0).documentElement.outerHTML. This will return whole html of iframe without doctype. For doctype you can use this function:

function getDoctypeString (doc) {
    var doctypeNode = doc.doctype;
    if (!doctypeNode) {
        return '';
    }
    return "<!DOCTYPE "
         + doctypeNode.name
         + (doctypeNode.publicId ? ' PUBLIC "' + doctypeNode.publicId + '"' : '')
         + (!doctypeNode.publicId && doctypeNode.systemId ? ' SYSTEM' : '') 
         + (doctypeNode.systemId ? ' "' + doctypeNode.systemId + '"' : '')
         + '>';
}

And all together:

<!DOCTYPE html>

<body>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <iframe src="about: blank"></iframe><br>
  <button>Save to textarea</button><br>
  <textarea cols=55 rows=10></textarea>
  <script>
    $(function() {
      function getDoctypeString(doc) {
        var doctypeNode = doc.doctype;
        if (!doctypeNode) {
          return '';
        }
        return "<!DOCTYPE " + doctypeNode.name + (doctypeNode.publicId ? ' PUBLIC "' + doctypeNode.publicId + '"' : '') + (!doctypeNode.publicId && doctypeNode.systemId ? ' SYSTEM' : '') + (doctypeNode.systemId ? ' "' + doctypeNode.systemId + '"' : '') + '>';
      }
      var $iframe = $("iframe");
      var $iframeBody = $iframe.contents().find('body');
      var $textarea = $("textarea");
      $iframeBody.html('<scr' + 'ipt>var x = 1 < 2;</scr' + 'ipt>&lt;&gt;&amp;');
      $iframe.contents().prop('designMode', 'on');
      $("button").click(function() {
        var iframeDocument = $iframe.contents().get(0);
        $textarea.val(getDoctypeString(iframeDocument) + iframeDocument.documentElement.outerHTML);
      });
    });
  </script>
</body>

</html>
like image 93
Pinal Avatar answered Dec 05 '25 11:12

Pinal



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!