tbranyen/diffhtml

innerHTML <code> contents is escaped

nbianca opened this issue · 2 comments

Hello,

I am using diffhtml's innerHTML with <code> elements and the contents is escaped even if it is not necessary.

<html>
  <head></head>
  <body>
    <div id="example1"></div>
    <div id="example2"></div>
  </body>
</html>
import { innerHTML } from '//diffhtml.org/es';

const html = `<code><b>test</b></code>`;

document.getElementById('example1').innerHTML = html;
innerHTML(document.getElementById('example2'), html);

will output

<html>
  <head></head>
  <body>
    <div id="example1"><code><b>test</b></code></div>
    <div id="example2"><code>&lt;b&gt;test&lt;/b&gt;</code></div>
  </body>
</html>

but I expected

<html>
  <head></head>
  <body>
    <div id="example1"><code><b>test</b></code></div>
    <div id="example2"><code><b>test</b></code></div>
  </body>
</html>

I think the problem is related to the HTML parser:

window.diff.Internals.parse('<code><b>test</b></code>').childNodes[0].childNodes[0]
// =>
{
"rawNodeName":"#text",
"nodeName":"#text",
"nodeValue":"<b>test</b>",
"nodeType":3,
"key":"",
"childNodes":[],
"attributes":{}
}

This is intentional since typically <code> is designed to display code and not necessarily render markup. I can see how this is problematic as innerHTML and outerHTML are designed to work like their DOM counterparts, and they are differing here. I will look into supporting unescaped markup.

In the meanwhile you can work around this using a transition handler:

  import { addTransitionState } from '//diffhtml.org/es';

  addTransitionState('attached', el => {
    if (el.nodeName.toLowerCase() === 'code') {
      // Decode HTML entities for code tags
      el.innerHTML = el.innerText;
    }
  });

@nbianca ah good call with the parser debugging. Since <code/> is treated as a block level element, it will not parse the internal HTML. You can work around that even easier with:

  innerHTML(document.getElementById('example2'), html, {
    parser: {
      // Disable <code /> being a block level element
      rawElements: [ 'script',  'noscript', 'style', 'template' ] // or just use [] if you don't need these elements
    }
  });