我发现文章里的语法高亮插件自动把一些转义字符给解码了,导致显示的不正确。可以移步知乎查看:
https://zhuanlan.zhihu.com/p/689946202
API 返回的某个字段里是用于渲染到页面上的 HTML 代码,其中可能混杂有转义字符、正常字符、HTML 标签。如下:
// 解码后应为:1,2'<br />3<4 5>
const str = '1,2'<br />3<4 5>'
// 3、4、5 后面的其实都是转义字符,但是高亮插件给自动解码了,所以你看到的是正常的字符
现在有个需求,将其保存到一个 txt 纯文本里,为了可读性,需要将转义字符变成其“真正”的字符,可以称为“反转义”或“解码”。(这里不需要处理 HTML 标签如 <br /> 等,原样保留即可)。
下面有 3 个方法,第一个是正确的,后面两个是不符合预期的:
// 解码后应为:1,2'<br />3<4 5>
const str = '1,2'<br />3<4 5>'
function htmlDecode (str) {
const textarea = document.createElement('textarea')
textarea.innerHTML = str
return textarea.value
}
console.log(htmlDecode(str))
// 正确
// 1,2'<br />3<4 5>
function divInnerHTML (str){
const div = document.createElement('div')
div.innerHTML = str
return div.innerHTML
}
console.log(divInnerHTML(str))
// 错误
// 1,2'<br>3<4 5>
function divInnerText(str){
const div = document.createElement('div')
div.innerHTML = str
return div.innerText
}
console.log(divInnerText(str))
// 错误
// 1,2'3<4 5>
正确的方法是使用 textarea 标签,先将原文本设置为 innerHTML,因为设置 innerHTML 时浏览器是会解码转义字符的。
然后通过 textarea.value 就可以获取到解码后的字符了,并且 HTML 标签是原样保留的。
那么 div.innerHTML 呢?首先,将字符串设置为 div.innerHTML 时它是会解码转义字符的,但它的问题有 2 个:
问题 1: 获取 div.innerHTML 时,它把部分字符进行了转义,所以 > < 又变回了转义字符(,'倒是保持不变)。为什么会这样呢?规范如此,MDN 上有写:
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/innerHTML
如果一个 <div>, <span>, 或 <noembed> 节点有一个文本子节点,
该节点包含字符 (&), (<), 或 (>), innerHTML 将这些字符分别返回为 &, < 和 >。
使用 Node.textContent 可获取一个这些文本节点内容的正确副本。
我尚不确定原因,硬要说的话,可能是为了防止这些特殊文本挨着 HTML 标签时,并且保持原样的话,会导致某些情况下解析 HTML 标签出错,所以将某些特殊字符又转义了。这导致此方法完全不可用。
问题2 :如果传入的是 html 4 标准的标签(比如 <br /> 是有闭合标记的),设置为 div.innerHTML 时现代浏览器会将其变成 html 5 标准的标签,如 <br>。之后读取时也是 <br>。
这通常没有影响,但在某些只能兼容 html 4 的软件或环境中就会出错。例如 EPUB 格式的电子书文件就只能使用 html 4 的标签,如果使用 <br> 就会导致阅读器解析文件时出错,小说无法打开阅读。
既然获取 div.innerHTML 时它把某些字符又转义回去了,那使用 div.innerText 不就不会转义了吗?
确实如此,所以它可以用来解码。但它也有一些问题:
<br /> 会变成 \n,这是个副作用。如果你希望原样返回 HTML 标签就不能用这个方法。\n 都没有。为什么换行标签会丢失?因为上面的代码没有把 div 添加到页面上,而是只在内存中,实际上文本没有被渲染,也就没有换行效果,所以也就没有 \n。
如果将 div 添加到页面上 document.body.append(div) 然后再获取就会有 \n 了,但这会影响性能。
在上面的代码中,textContent 和 innerText 的表现相同(因为它们都没有返回 \n,所以输出是完全相同的)。
Google Chrome 125
Windows 10/11
学到了