2018 年马上就要过去了,这一年真的经历了太多,本应该按惯例写写年终总结的,不过最近真的太忙了,总有些事情是更优先的,又赶上今天有人需要帮忙,索性就把解决这个小问题的代码讲一讲,全当作是对 2018 年的一个告别吧。
其实很久以前,在某个项目中也遇到过这个问题,我们在一个前端 Table (Grid) 中,希望显示出一个包含了 HTML 样式的富文本内容列,但是其内容很长,我们希望可以按照一个指定的字数进行截取(需要继续保留富文本的样式),并将字符串的剩余部分显示为 …
我们知道,通过 CSS ellipsis 也能设置 … 样式,但是这只能按照外围容器的 width 进行设定,无法按照字数进行处理。所以,为了庆祝新年,我们自己动手写一个吧!
主要目标
- 根据指定的字符数,对 HTML 字符串进行计数,跳过所有 HTML 标签
- 统计截止到结束处,所有未闭合的标签,并协助进行追加
基本思路
- 从目标字符串的第一个字符开始遍历,如果没有遇到 < 就继续遍历并计数,如果遇到 < 则暂停计数,直到遇见下一个 > 时,再继续计数
- 如果字数计数器达到调用者所指定的 lengthLimit 阈值则停止查询,并记录得到的结束位置
- 我们可以在上述循环过程中同时记录每一个标签的配对情况,从而帮助我们在最后补齐被截取一半的标签
- 创建一个数组作为栈结构,遇到一个 HTML 的标签则压栈,遇到与之对应的闭合标签则直接弹栈(如果是自闭合标签则直接跳过),在到达指定字符串位置时,只需将栈内剩余标签弹出并追加到字符串的结尾即可
- 加上结尾的 … 字符
OK,具体实现如下。
var testHtmlRichText = "Hello <br />, <div><p><b>World</b>! <em>what the hell is <b>that</b></em></p>?! <h1>你好,再见!</h1><div>Tomorrow is another day.</div></div> 明天又是一天"; // console.log(foldHtmlRichTextToEllipsis(testHtmlRichText, 200, " ...")); // console.log(foldHtmlRichTextToEllipsis(testHtmlRichText, 142, " ...")); console.log(foldHtmlRichTextToEllipsis(testHtmlRichText, 11, " ...")); // OUTPUT: Hello <br />, <div><p><b>Wor</b></p></div> ... function foldHtmlRichTextToEllipsis(htmlRichText, lengthLimit, ellipsisSuffix) { if (!ellipsisSuffix) { ellipsisSuffix = " ..."; } if (lengthLimit >= htmlRichText.length) { return htmlRichText + ellipsisSuffix; } let htmlSymbolStack = []; let wordCounter = 0; let index = 0; while (wordCounter < lengthLimit && index < htmlRichText.length) { let currentChar = htmlRichText[index]; index++; if (currentChar === "<") { let htmlTagStart = index; // Jump to the end of the tag while (htmlRichText[index] !== ">" && index < htmlRichText.length) { index++; } let htmlTagEnd = index++; let htmlTagString = htmlRichText.substring(htmlTagStart, htmlTagEnd); // For DEBUG // console.log("Tag:" + htmlTagString); // If it's not a self-closing tag, add it to stack if (!htmlTagString.endsWith("/")) { // Check if it's the closed tag if (htmlTagString.startsWith("/")) { // Check if the top element of the stack is paired. if (htmlSymbolStack[htmlSymbolStack.length - 1] === htmlTagString.replace("/", "")) { // If it is paired, it will directly pop the top element of the htmlSymbolStack htmlSymbolStack.pop(); } else { // Else means, there is a HTML tag pairing syntax problem with the source string console.error( "Please check the source HTML string, there is a HTML tag pairing syntax problem." ); } } else { htmlSymbolStack.push(htmlTagString); } } } else { wordCounter++; } } let result = htmlRichText.substring(0, index); // For DEBUG // console.log("HTML Symbol Stack: " + result); // htmlSymbolStack.forEach(element => { // console.log(element); // }); let closeElement; while (closeElement = htmlSymbolStack.pop()) { result += ("</" + closeElement + ">"); } // For DEBUG // console.log("Result:" + result); return result + ellipsisSuffix; }
最后祝大家新年快乐,2019 年安康和顺,财源广进!