Django博客如何实现长文超长检测、截断展示与展开交互
Django博客长博文默认截断+交互展开完整实现方案
前期准备
现有页面已经完成的结构不需要大改:正文容器为id="content-blog",内部通过{{ article.content|safe }}渲染富文本内容,整个实现不需要调整Django视图、模型层的任何逻辑。
核心实现逻辑
- 固定判断阈值为1000个用户实际可见的纯文本字符,HTML标签、格式符不纳入长度统计,避免截断偏差
- 绝对不硬切富文本字符串,防止破坏标签结构导致页面样式错乱
- 短于阈值的博文不生成任何额外元素,无额外性能开销
- 纯原生JS实现,无第三方依赖,不影响原有页面加载速度
分步实现代码
1. 模板层基础样式补充
在页面的公共样式块里加入以下代码,适配截断状态的视觉表现:
/* 截断状态的渐变遮罩,避免硬切内容的突兀感 */ .content-fade-mask { position: absolute; bottom: 0; left: 0; right: 0; height: 4rem; background: linear-gradient(to bottom, rgba(255,255,255,0), #ffffff); pointer-events: none; } /* 展开/收起按钮样式,可根据站点风格调整配色 */ .blog-expand-btn { display: block; margin: 1.5rem auto; padding: 0.5rem 1.5rem; border: none; border-radius: 4px; background: #f3f4f6; cursor: pointer; color: #374151; } .blog-expand-btn:hover { background: #e5e7eb; }
你原来的正文容器模板完全保留,不需要修改:
<!-- 原有Django模板代码,无需调整 --> <div id="content-blog"> {{ article.content|safe }} </div>
2. 原生交互逻辑实现
把以下JS代码放在页面最底部(或通过外部脚本引入),不需要修改原有代码结构:
document.addEventListener('DOMContentLoaded', function () { // 获取正文容器,不存在则直接退出,避免报错 const contentEl = document.getElementById('content-blog'); if (!contentEl) return; // 配置项,可根据需要调整阈值 const TRUNCATE_THRESHOLD = 1000; // 暂存完整的富文本内容,避免多次修改DOM const fullHTML = contentEl.innerHTML; // 统计用户实际可见的文本长度,和纯文本阅读体感一致 const visibleTextLength = contentEl.innerText.trim().length; // 长度未达阈值,直接展示完整内容,不加载后续逻辑 if (visibleTextLength <= TRUNCATE_THRESHOLD) return; // -------------- 达到阈值,执行截断逻辑 -------------- // 给正文容器加相对定位,方便放置渐变遮罩 contentEl.style.position = 'relative'; // 克隆内容做截断处理,不直接修改原DOM const tempTruncateWrap = document.createElement('div'); tempTruncateWrap.innerHTML = fullHTML; let currentLen = 0; const childNodes = Array.from(tempTruncateWrap.childNodes); for (let node of childNodes) { // 文本长度已经到阈值,直接移除后续节点 if (currentLen >= TRUNCATE_THRESHOLD) { tempTruncateWrap.removeChild(node); continue; } // 统计当前节点的文本长度 if (node.nodeType === Node.TEXT_NODE) { const nodeText = node.textContent.trim(); currentLen += nodeText.length; // 单个文本节点长度超阈值时,精准截断到目标长度 if (currentLen > TRUNCATE_THRESHOLD) { const cutIndex = nodeText.length - (currentLen - TRUNCATE_THRESHOLD); node.textContent = nodeText.slice(0, cutIndex) + '...'; currentLen = TRUNCATE_THRESHOLD; } } else if (node.nodeType === Node.ELEMENT_NODE) { currentLen += node.innerText.trim().length; } } // 把截断后的内容放回正文容器 contentEl.innerHTML = tempTruncateWrap.innerHTML; // 添加渐变遮罩优化视觉效果 const fadeMask = document.createElement('div'); fadeMask.className = 'content-fade-mask'; contentEl.appendChild(fadeMask); // 插入展开/收起按钮,放在正文容器外面,避免被算进正文长度 const toggleBtn = document.createElement('button'); toggleBtn.className = 'blog-expand-btn'; toggleBtn.textContent = '展开全部内容'; contentEl.after(toggleBtn); // 绑定按钮切换逻辑 let isExpanded = false; toggleBtn.addEventListener('click', () => { if (!isExpanded) { // 展开:替换为完整内容,移除遮罩 contentEl.innerHTML = fullHTML; fadeMask.remove(); toggleBtn.textContent = '收起内容'; isExpanded = true; } else { // 收起:换回截断内容,重新加遮罩 contentEl.innerHTML = tempTruncateWrap.innerHTML; contentEl.appendChild(fadeMask); toggleBtn.textContent = '展开全部内容'; isExpanded = false; } }) })
实现过程中要避开的坑
- 别图省事直接对
innerHTML做字符串截取算长度:富文本里的HTML标签本身会占大量字符数,不仅统计的长度和实际阅读体感差很多,还容易把标签剪坏,比如把<p>拆成<p,直接导致页面样式错乱 - 别用
textContent统计文本长度:它会把隐藏元素、脚本标签内的文本全部算入,和用户实际看到的文本长度偏差很大,innerText才是和用户可见内容完全匹配的统计值 - 别在页面head里直接写JS逻辑:此时DOM还没渲染完成,根本找不到
#content-blog元素,逻辑会直接失效,必须等DOM加载完成后再执行判断
内容的提问来源于stack exchange,提问作者Dheirya Tyagi
相关产品推荐
相关产品推荐

