You need to enable JavaScript to run this app. Django博客如何实现长文超长检测、截断展示与展开交互
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.06 14:06:06