本文介绍如何在 Django 模板中结合 truncatewords_html 过滤器与轻量级 JavaScript,实现语义完整的“Read more / Read less”交互效果,彻底规避按字符数截断导致的单词断裂问题。
本文介绍如何在 django 模板中结合 `truncatewords_html` 过滤器与轻量级 javascript,实现语义完整的“read more / read less”交互效果,彻底规避按字符数截断导致的单词断裂问题。
在 Django 前端开发中,为长文本(如游戏简介、文章摘要)添加“展开/收起”功能是常见需求。但若直接使用 slice 过滤器按固定字符数截取(如 |slice:"300"),极易在单词中间切断(例如将 "each" 截成 "eac"),导致展开后文本出现突兀的拼接瑕疵,严重影响可读性与用户体验。
推荐方案:基于词级截断 + DOM 切换
核心思路是放弃字符级硬切,改用语义安全的词级截断,并利用 CSS 显示/隐藏控制完整内容切换:
✅ 使用 Django 内置过滤器 truncatewords_html(支持 HTML 标签保留)替代 slice
✅ 预先渲染两份内容:截断版(如前 50 个词)和完整版,均置于 DOM 中
✅ 通过 JavaScript 控制 .overview(截断)与 .full-text(完整)两个 <span> 的显隐状态
✅ 正确的模板写法(Django HTML)
<p><strong>Overview:</strong>
<span class="overview">{{ game.overview|default:"N/A"|truncatewords_html:"50" }}</span>
<span class="full-text" style="display: none;">{{ game.overview|default:"N/A" }}</span>
<button class="read-button" onclick="readMore(this);">[Read more...]</button>
</p>? 关键说明:
- truncatewords_html:"50" 会精确截取前 50 个完整单词(自动保留闭合 HTML 标签,避免破坏结构);
- default:"N/A" 确保空值安全;
- .full-text 初始隐藏,避免布局抖动。
✅ 稳健的 JavaScript 逻辑
function readMore(button) {
const parent = button.parentNode;
const truncatedText = parent.querySelector(".overview");
const fullText = parent.querySelector(".full-text");
if (fullText.style.display === "inline") {
// 当前显示全文 → 切回摘要
truncatedText.style.display = "inline";
fullText.style.display = "none";
button.innerHTML = "[Read more...]";
} else {
// 当前显示摘要 → 展开全文
truncatedText.style.display = "none";
fullText.style.display = "inline";
button.innerHTML = "[Read less...]";
}
}⚠️ 注意事项:
- 避免依赖 display: none 的初始状态判断(易受 CSS 干扰),更健壮的方式是检查 getComputedStyle(fullText).display === 'none',但本例中因明确设定了内联样式,当前逻辑已足够可靠;
- 若页面含多个同类组件,建议改用事件委托(监听 .read-button 类)并用 this.closest('p') 定位父容器,提升可维护性;
- 可进一步增强体验:添加 CSS 过渡动画(如 transition: opacity 0.2s ease)、禁用按钮防止连点、或使用 aria-expanded 提升无障碍访问支持。
该方案简洁、兼容性强、语义清晰,完美解决了单词断裂痛点,是 Django 项目中处理长文本折叠的推荐实践。

















