前几天,pm 提了个需求,希望对用户输入的富文本进行长度的限制,如限制最长为 400 ,包含中英文等,即视觉层面上的 400 。该项目使用的富文本编辑器是 tinymce,或许其他编辑器的实现与 tinymce 不同,仅在此阐述下我的做法,提供一个思路。
忽略 Tag
首先,标签肯定是不需要的,包括其中的 style 和可能存在的 attribute。对于标签,很容易地会想到通过标签的 <> 来判别,当遇到 < 不进行计数,遇到 > 再开始计数,因此可以写出下列的代码
const getPlainTextLen = richText => {
let count = 0
for (let i = 0; i < richText.length; i++) {
if (richText[i] === '<') {
while (richText[i] !== '>') {
i++
}
} else {
count++
}
}
return count
}
在上面的代码中,我们成功忽略了标签所带来的影响,但是实现的方式未免过于丑陋,我们可以用正则来实现,将 < ... > 替换成空字符串,然后获取长度即可,为了不对原字符串造成影响,函数必须是纯函数
const getPlainTextLen = richText => richText.replace(/<[^>]+>/g, '').length
处理空白字符串和换行
很显然,空白字符串也是我们所不需要的,在 html 中,空白字符串可体现为空格或者是 或   等,在 JavaScript 中体现为换行的 \n 和 \r\n 也是空格的形式出现,字符串经过 replace 后还是字符串,因此对上面的函数进行扩展
const getPlainTextLen = richText => richText
.replace(/<[^>]+>/g, '')
.replace(/ |\n|\r\n| | /g, '')
.length
有些编辑器存储空白字符串仅仅是简单的空格,不会出现其他的形式。为了性能考虑(减少判断),你不必完全照搬第二个 replace , 相反的,诸如   也表示为空白字符串,上面代码仅列举了两种,因此最好根据实际情况进行编写。
处理 HTML Entities (html 字符实体)
如果你输入了 1 + 1< 3,编辑器会怎么存储呢,为什么我输入了 <p> 1234 <p> 也能正确显示出来呢 ?
1 + 1< 3 会被存储为 1 + 1 < 3 ,< 被存储为对于的 html entities,可能是为了规避用户可能输入的 html 字符串。关于 html entities,详细资料可参考 w3schools,链接为 HTML Entities (w3schools.com),如果打不开,请使用一点魔法手段哦,上文提到的 也属于此,但由于其表现为空白字符串,因此需要提前进行处理。HTML Entities 还包括了很多人喜爱的 😀 等 emoji 表情。像上文提到的 <,原本 1 + 1< 3 的 5 个长度变成了 8 个长度,😀 对应着 😀 , 但视觉层面应该视为一个长度,那么,需要对其进行处理。
HTML Entities 有两种表示形式,即 &entity_name; OR &#entity_number; 基于此,编写对应的正则。
// {2,5} 和 {1,6} 表示需要匹配的长度,根据实际情况编写
const getPlainTextLen = richText => richText
.replace(/<[^>]+>/g, '')
.replace(/ |\n|\r\n| | /g, '')
.replace(/&([a-z]{2,5}|#[0-9]{1,6});/g, ' ')
.length
最后,还有特殊的 glyph 字形,即我们小学学过的注音音符,如 a -> à ,其对应为 à ,诸如此类,应该对应为 1个字符串,让我们编写最后一个正则,其需要在上面代码中的最后一个正则前提前处理,否则 "a" 会变成 "a " 了,会被计算成 2 个字符串,无法准确的判断了。
const getPlainTextLen = richText => richText
.replace(/<[^>]+>/g, '')
.replace(/ |\n|\r\n| | /g, '')
.replace(/[a-zA-Z]([6-7][0-9]);/g, ' ')
.replace(/&([a-z]{2,5}|#[0-9]{1,6});/g, ' ')
.length
对应字形,并不是必须添加,因为并不是每一个编辑器都能正确地显示,比如我使用的 tinymce 就无法正确地显示,还是那句话,根据实际情况判断是否需要添加。
验证
最后,让我们来验证下该函数是否正确
// 长度为 5 + 8 + 4 + 10 + 11 + 5 + 3 + 4 + 1 = 51
const html =
`<h1>Title</h1>
<h2>subtitle</h2>
<h3>
H́èH́è
</h3>
<ul>
<li> First block </li>
<li> Second block </li>
<li> 1 + 1 < 3 </li>
<li> 5 > 3 </li>
<li> 1 00 € </li>
<li> 😀 </li>
</ul>`
// 视觉层面也为 51 ,结果正确
console.log(getPlainTextLen(html)) // 51
如果这篇文章对你有帮助,请给我点个赞,欢迎下方留言讨论