Open toFrankie opened 5 months ago
此前有个项目里面一个功能是:运营后台进行富文本配置,然后在浏览器、微信小程序各端做展示。
富文本编辑器本身是可以设置字体的,当预设的字体名称包含空格(比如 Microsoft Yahei)时,出现问题了,在小程序端渲染时字体不生效。
Microsoft Yahei
当时临时方案是将字体名称用连字符替代空格,因为第二天要上线。
这不是一个好的解决方案,比如针对 Microsoft Yahei、PingFang SC、Helvetica Neue 这类系统内置的字体,不应写成 Microsoft-Yahei,这是不合理的。
PingFang SC
Helvetica Neue
Microsoft-Yahei
下面开始找找根本原因。
通常来说,我们在 CSS 中设置 font-family 时,字体名称是可以包含空格的,但有空格时,应该要用引号括起来。
font-family
MDN
The name of a font family. For example, "Times" and "Helvetica" are font families. Font family names containing whitespace should be quoted. For example: "Comic Sans MS".
比如:
.app { font-family: "Helvetica Neue", "Segoe UI", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; }
鉴于浏览器的包容性很强,其实不写引号,也能正常识别。
经排查发现,前面遇到的问题,其实是就是因为对 HTML 实体转义后,导致 HTML 结构不正确导致的。
后台的富文本编辑器用的是 Braft Editor,它设置字体的方式如下:
const fontFamilies = [ { name: 'ST Song', family: '"ST Song"', // "'ST Song'" 在 BraftEditor 内不生效 }, ]
<BraftEditor fontFamilies={fontFamilies} />
是的,在 Braft Editor 里字体名称只能用「双引号」包裹,单引号不生效。
假设富文本编辑器输出的 HTML 如下(Hello World):
<p><span style="font-family:"ST Song"">"Hello World"</span></p>
在浏览器中,这段富文本内容直接通过 Element.innerHTML 去修改 DOM,是可以得到预期效果的。但在小程序里,需要对类似 "(双引号)等 HTML Entity 进行转换,才能正常显示,否则它会将 " 当作五个普通字符,而不是一个双引号。
Element.innerHTML
"
之前是通过 entities 来做转义的。
import { decodeHTML } from 'entities' const html = `<p><span style="font-family:"ST Song"">"Hello World"</span></p>` const transformedHtml = decodeHTML(html) console.log(transformedHtml)
得到的转义结果是:
<p><span style="font-family:"ST Song"">"Hello World"</span></p>
其实这就看出问题了,style 属性包裹了两个双引号,自然解析不到预期结果。
style
我们知道,CSS 字符串类型的属性值,可以用单引号或双引号。我们先把 style 里可能出现的引号,全部转为单引号,这样的话就能正确解析了。
CSS 属性值使用到引号的(只想起了这几个):
content
url()
Related Link: https://www.w3.org/TR/2011/REC-CSS2-20110607/syndata.html#values
这样的话,用表达式做匹配出 style 的属性值,然后将里面的引号替换掉,方法如下:
function transformHtmlInlineStyle(html) { return html.replace(/(\s+style="[^"]*")/gi, match => { return match.replace(/"|'|"|'|"|'/gi, "'") }) }
因此,上面的流程只要加多一步就行:
import { decodeHTML } from 'entities' const html = `<p><span style="font-family:"ST Song"">"Hello World"</span></p>` + let transformedHtml = transformHtmlInlineStyle(html) transformedHtml = decodeHTML(html) console.log(transformedHtml)
得到的结果为:
<p><span style="font-family:'ST Song'">"Hello World"</span></p>
示例:CodeSandbox
The end.
背景
此前有个项目里面一个功能是:运营后台进行富文本配置,然后在浏览器、微信小程序各端做展示。
富文本编辑器本身是可以设置字体的,当预设的字体名称包含空格(比如
Microsoft Yahei
)时,出现问题了,在小程序端渲染时字体不生效。当时临时方案是将字体名称用连字符替代空格,因为第二天要上线。
这不是一个好的解决方案,比如针对
Microsoft Yahei
、PingFang SC
、Helvetica Neue
这类系统内置的字体,不应写成Microsoft-Yahei
,这是不合理的。下面开始找找根本原因。
开始之前
通常来说,我们在 CSS 中设置
font-family
时,字体名称是可以包含空格的,但有空格时,应该要用引号括起来。MDN
比如:
寻找原因
经排查发现,前面遇到的问题,其实是就是因为对 HTML 实体转义后,导致 HTML 结构不正确导致的。
后台的富文本编辑器用的是 Braft Editor,它设置字体的方式如下:
是的,在 Braft Editor 里字体名称只能用「双引号」包裹,单引号不生效。
假设富文本编辑器输出的 HTML 如下(Hello World):
在浏览器中,这段富文本内容直接通过
Element.innerHTML
去修改 DOM,是可以得到预期效果的。但在小程序里,需要对类似"
(双引号)等 HTML Entity 进行转换,才能正常显示,否则它会将"
当作五个普通字符,而不是一个双引号。之前是通过 entities 来做转义的。
得到的转义结果是:
其实这就看出问题了,
style
属性包裹了两个双引号,自然解析不到预期结果。解决问题
我们知道,CSS 字符串类型的属性值,可以用单引号或双引号。我们先把
style
里可能出现的引号,全部转为单引号,这样的话就能正确解析了。CSS 属性值使用到引号的(只想起了这几个):
font-family
content
url()
这样的话,用表达式做匹配出
style
的属性值,然后将里面的引号替换掉,方法如下:因此,上面的流程只要加多一步就行:
得到的结果为:
示例:CodeSandbox
The end.