JavaScript文本截断功能异常,寻求问题排查与修复方案
问题原因分析及解决方案
问题原因
- 标题文本占用词数额度:原代码会遍历所有子节点并计数所有文本内容,包括
<h1>-<h6>标题内的文字,导致标题的词数直接占用data-limit的限制额度,正文还未显示足够内容就触发截断。 - 空白字符被错误计数:使用
split(/\s+/)分割文本节点时,连续空格、换行等空白会生成空字符串,这些空字符串被当成有效词计数,导致实际有效文本还没达到限制就提前截断。 - 内联标签截断逻辑不完善:当在
<strong>、<a>等内联标签内的文本达到词数限制时,省略号的插入位置可能破坏标签语义,且无法保证标签完整保留。
解决方案代码
JavaScript
document.addEventListener("DOMContentLoaded", function() { // 定义不需要截断/不计入词数的标签 const skipCountTags = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']); // 定义需要完整保留的内联标签(不允许在标签内部截断) const preserveTags = new Set(['strong', 'b', 'a', 'em', 'i']); function shortenText(element, limit) { const originalContent = element.innerHTML; const nodes = Array.from(element.childNodes); let wordCount = 0; let truncatedContent = ''; let isTruncated = false; function traverseNodes(node) { if (isTruncated) return; if (node.nodeType === Node.TEXT_NODE) { if (wordCount >= limit) { isTruncated = true; return; } const text = node.nodeValue.trim(); if (!text) return; // 跳过纯空白文本节点 const words = text.split(/\s+/).filter(word => word.length > 0); const remainingWords = limit - wordCount; const takeWords = words.slice(0, remainingWords); truncatedContent += takeWords.join(' ') + ' '; wordCount += takeWords.length; // 达到限制则添加省略号并终止遍历 if (wordCount >= limit) { truncatedContent += '...'; isTruncated = true; } } else if (node.nodeType === Node.ELEMENT_NODE) { const tagName = node.nodeName.toLowerCase(); // 跳过已存在的"Read more"节点 if (tagName === 'span' && node.classList.contains('more')) return; // 标题标签完整保留,不计入词数 if (skipCountTags.has(tagName)) { truncatedContent += node.outerHTML; return; } // 开始构建标签结构 truncatedContent += `<${tagName}`; Array.from(node.attributes).forEach(attr => { truncatedContent += ` ${attr.name}="${attr.value}"`; }); truncatedContent += '>'; // 处理需要完整保留的内联标签 if (preserveTags.has(tagName)) { const tagText = node.textContent.trim(); const tagWordCount = tagText.split(/\s+/).filter(w => w).length; if (wordCount + tagWordCount <= limit) { // 能完整容纳,直接添加标签内容 truncatedContent += node.innerHTML; wordCount += tagWordCount; } else { // 无法完整容纳,直接截断并标记状态 truncatedContent += '...'; isTruncated = true; } } else { // 普通标签正常遍历子节点 node.childNodes.forEach(childNode => traverseNodes(childNode)); } // 未截断时才闭合标签,避免结构异常 if (!isTruncated) { truncatedContent += `</${tagName}>`; } } } for (let childNode of nodes) { if (!isTruncated) { traverseNodes(childNode); } } // 仅在内容被截断时添加展开链接 if (isTruncated) { element.innerHTML = truncatedContent.trim() + `<span class="more">Read more</span>`; element.dataset.originalContent = originalContent; } } function expandText(element) { element.innerHTML = element.dataset.originalContent; delete element.dataset.originalContent; // 清理缓存的原始内容 } document.querySelectorAll('.shorten-text').forEach(el => { const limit = parseInt(el.dataset.limit) || 100; shortenText(el, limit); el.addEventListener('click', function(event) { if (event.target.classList.contains('more')) { expandText(el); } }); }); });
CSS
* { font-family: Calibri, Arial; margin: 0px; padding: 25px; text-align: center; } .shorten-text { margin: 20px 0; padding: 10px; border: 1px solid #ccc; background-color: #f9f9f9; } .more { color: blue; cursor: pointer; text-decoration: underline; }
HTML
<h1>Text Shortening Test</h1> <div class="shorten-text" data-limit="10"> <h1>A Headline!</h1> <div>A wonderful serenity has taken possession of my entire soul, <strong>like these sweet mornings</strong> of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.</div> <p>I am so happy, my dear friend, so absorbed in the <a href="#">exquisite</a> sense of mere tranquil existence, that I neglect my talents.</p> </div> <div class="shorten-text" data-limit="5"> <h2>Another Header</h2> <div>This is a test paragraph to check the shortening function. It should only show a few words.</div> <p>This paragraph should be mostly hidden.</p> </div> <div class="shorten-text" data-limit="15"> <h3>Yet Another Header</h3> <div>Testing the function with a longer limit to see how it handles more text. The goal is to ensure that the text is truncated properly without breaking the structure of the HTML content.</div> </div>
关键修改说明
- 标题不计入词数:标题标签完整保留且不占用词数额度,确保正文能按设定限制显示足够内容。
- 过滤无效空白:只计数有效单词,避免空白字符错误消耗词数配额。
- 完整保留内联标签:
<strong>、<a>等内联标签要么完整显示,要么直接截断,不会出现标签被拆分的情况。 - 精准截断控制:用
isTruncated标记管理遍历状态,避免不必要的节点处理,同时保证HTML结构完整。 - 条件显示展开链接:只有内容确实被截断时才显示“Read more”,避免冗余元素。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

