innerHTML unencodes < in attributes

innerHTML unencodes &lt; in attributes

我有一份 HTML 文档,其中某些属性可能包含 &lt;&gt;。我试图通过 XSLT 提取它并 运行 它,但是 XSLT 引擎错误告诉我 < 在属性内部无效。

我做了一些挖掘,发现它在源文件中被正确地转义了,但是当它通过innerHTML加载到DOM时,DOM是取消编码 属性。奇怪的是,它对 &lt;&gt; 执行此操作,但对 &amp;.

等其他一些执行此操作

这是一个简单的例子:

var div = document.createElement('DIV');
div.innerHTML = '<div asdf="&lt;50" fdsa="&amp;50"></div>';
console.log(div.innerHTML)

我假设 DOM 实现决定 HTML 属性可以不如 XML 属性严格,这是 "working as intended"。我的问题是,我可以在不编写一些可怕的正则表达式替换的情况下解决这个问题吗?

我不确定这是否是您正在寻找的,但请看一看。

var div1 = document.createElement('DIV');
var div2  = document.createElement('DIV');
div1.setAttribute('asdf','&lt;50');
div1.setAttribute('fdsa','&amp;50');
div2.appendChild(div1);
console.log(div2.innerHTML.replace(/&amp;/g, '&'));

尝试 XMLSerializer:

var div = document.getElementById('d1');

var pre = document.createElement('pre');
pre.textContent = div.outerHTML;
document.body.appendChild(pre);

pre = document.createElement('pre');
pre.textContent = new XMLSerializer().serializeToString(div);
document.body.appendChild(pre);
<div id="d1" data-foo="a &lt; b &amp;&amp; b &gt; c">This is a test</div>

您可能需要调整 XSLT 以考虑 XHTML 命名空间 XMLSerializer 插入(至少在此处使用 Firefox 进行测试)。

最终对我来说最有效的是在传入文档上使用 XSLT 对这些进行双重转义(并在传出文档上反转)。

所以属性中的 &lt; 变为 &amp;lt;。感谢@Abel 的建议。

这是我添加的 XSLT,以防其他人觉得它有用:

首先是用于在 XSLT 1.0 中进行字符串替换的模板。如果可以使用 XSLT 2.0,则可以使用内置的 replace

<xsl:template name="string-replace-all">
    <xsl:param name="text"/>
    <xsl:param name="replace"/>
    <xsl:param name="by"/>
    <xsl:choose>
        <xsl:when test="contains($text, $replace)">
            <xsl:value-of select="substring-before($text,$replace)"/>
            <xsl:value-of select="$by"/>
            <xsl:call-template name="string-replace-all">
                <xsl:with-param name="text" select="substring-after($text,$replace)"/>
                <xsl:with-param name="replace" select="$replace"/>
                <xsl:with-param name="by" select="$by"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

接下来是执行我需要的特定替换的模板:

<!-- xml -> html -->
<xsl:template name="replace-html-codes">
    <xsl:param name="text"/>
    <xsl:variable name="lt">
        <xsl:call-template name="string-replace-all">
            <xsl:with-param name="text" select="$text"/>
            <xsl:with-param name="replace" select="'&lt;'"/>
            <xsl:with-param name="by" select="'&amp;lt;'"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="gt">
        <xsl:call-template name="string-replace-all">
            <xsl:with-param name="text" select="$lt"/>
            <xsl:with-param name="replace" select="'&gt;'"/>
            <xsl:with-param name="by" select="'&amp;gt;'"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$gt"/>
</xsl:template>

<!-- html -> xml -->
<xsl:template name="restore-html-codes">
    <xsl:param name="text"/>
    <xsl:variable name="lt">
        <xsl:call-template name="string-replace-all">
            <xsl:with-param name="text" select="$text"/>
            <xsl:with-param name="replace" select="'&amp;lt;'"/>
            <xsl:with-param name="by" select="'&lt;'"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="gt">
        <xsl:call-template name="string-replace-all">
            <xsl:with-param name="text" select="$lt"/>
            <xsl:with-param name="replace" select="'&amp;gt;'"/>
            <xsl:with-param name="by" select="'&gt;'"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$gt"/>
</xsl:template>

XSLT 主要是传递。我只是在复制属性时调用适当的模板:

<xsl:template match="@*">
    <xsl:attribute name="data-{local-name()}">
        <xsl:call-template name="replace-html-codes">
            <xsl:with-param name="text" select="."/>
        </xsl:call-template>
    </xsl:attribute>
</xsl:template>

<!-- copy all nodes -->
<xsl:template match="node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

值得一提的几件事可能会对某人有所帮助:

  • 确保您的 HTML 确实有效,例如当我应该使用 / 时,我不小心使用了 \,这导致了这个问题。
  • 正如 OP 在问题中指出的那样,您可以使用 &amp;,因此您可以尝试例如&amp;lt;&amp;gt;.
  • alternatives<> 看起来很相似。
  • 有一种alternate方式来表达<>&#60;&#62;