如何用Javascript实现不破坏HTML结构的带标签文本前50词截取功能
实现方案及代码修复
原代码的核心问题
- 执行顺序错误:
newDescription = shorten(cutDesc,50)是同步执行的,早于axios异步请求返回的时机,此时cutDesc还是空值,根本拿不到接口返回的内容 - 逻辑位置错误:
shorten函数调用写在map循环外面,没有对每一条返回的e.description做处理,循环里用的newDescription一直是初始空值 - 截断逻辑错误:原有
shorten是按字符长度截断,不是按单词数,而且直接切割带HTML标签的字符串会导致标签断裂,出现排版异常甚至XSS风险
修复方案
第一步:重写截断函数
如果只需要提取纯文本展示,不需要保留原有HTML格式,用下面的函数:
function shorten(html, maxWords) { // 先剥离所有HTML标签,替换空格符,拿到纯文本 const text = html.replace(/<[^>]*>/g, '').replace(/ /g, ' ').trim(); const words = text.split(/\s+/); if (words.length <= maxWords) return html; // 截断后加省略号 return words.slice(0, maxWords).join(' ') + '...'; }
如果需要保留原有HTML格式、不破坏标签结构,用下面的DOM处理版本:
function shortenWithHtml(html, maxWords) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; let wordCount = 0; const traverse = (node) => { if (wordCount >= maxWords) { node.remove(); return; } if (node.nodeType === Node.TEXT_NODE) { const words = node.textContent.trim().split(/\s+/); if (wordCount + words.length <= maxWords) { wordCount += words.length; } else { const remain = maxWords - wordCount; node.textContent = words.slice(0, remain).join(' ') + '...'; wordCount = maxWords; // 移除后面的所有兄弟节点 let next = node.nextSibling; while(next) { const temp = next.nextSibling; next.remove(); next = temp; } } return; } Array.from(node.childNodes).forEach(traverse); }; traverse(tempDiv); return tempDiv.innerHTML; }
第二步:修正异步逻辑和调用位置
把截断逻辑放到axios回调的map循环内部,每次处理单条数据:
let count = 1; axios.get(`${url}info?id=${id}`) .then(function(res) { if (res.data.status == 'success') { $("#info_list").html(res.data.result.map((e) => { // 每条数据单独处理截断 const newDescription = shortenWithHtml(e.description, 50); return `<tr class="tb-item"> <td class=""> ${count++}<br> </td> <td class="tb-data"> <span class="tb-data"> <small>${newDescription}</small> </span> </td> </tr>`; })); } }) // 截断函数定义放在全局位置即可,不需要放到回调内部 function shortenWithHtml(html, maxWords) { // 对应上面选的截断函数内容 }
内容的提问来源于stack exchange,提问作者duatree
相关产品推荐
相关产品推荐

