You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel8 Blade中如何为TinyMCE内容实现阅读更多/收起功能

适配Laravel 8的实现方案

核心问题是原生Str::words只会按纯文本计数截断,不会识别HTML标签的开闭逻辑,一旦截断位置落在标签内部或者刚好卡在未闭合标签处,就会打乱整个页面DOM结构导致布局错乱。下面两种方案都可直接落地,全版本兼容Laravel 8:


方案1:后端安全截断HTML(适合需要SEO抓取摘要的场景)

不要直接调用原生Str::words,在app/Providers/AppServiceProvider.php的boot方法中注册自定义Str宏,基于DOM解析实现带HTML的安全截断,自动补全未闭合标签:

// 引入依赖类
use Illuminate\Support\Str;
use DOMDocument;
use DOMText;

public function boot()
{
    Str::macro('htmlWords', function ($html, $wordCount = 8, $ellipsis = '...') {
        $dom = new DOMDocument();
        // 屏蔽HTML5标签加载警告,指定utf-8编码避免乱码
        @$dom->loadHTML(
            mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'),
            LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
        );
        $root = $dom->documentElement;
        $currentCount = 0;
        $isTruncated = false;

        $traverseNode = function ($node) use (&$traverseNode, &$currentCount, $wordCount, &$isTruncated) {
            if ($isTruncated) {
                $node->parentNode->removeChild($node);
                return;
            }
            if ($node instanceof DOMText) {
                $words = preg_split('/\s+/', trim($node->nodeValue), -1, PREG_SPLIT_NO_EMPTY);
                $keepWords = array_slice($words, 0, $wordCount - $currentCount);
                $currentCount += count($keepWords);
                $node->nodeValue = implode(' ', $keepWords);
                if ($currentCount >= $wordCount) {
                    $node->nodeValue .= $ellipsis;
                    $isTruncated = true;
                }
                return;
            }
            // 递归遍历子节点
            foreach (iterator_to_array($node->childNodes) as $child) {
                $traverseNode($child);
            }
        };

        foreach (iterator_to_array($root->childNodes) as $child) {
            $traverseNode($child);
        }

        // 去掉DOM解析自动追加的html/body包裹标签
        $output = '';
        foreach ($root->childNodes as $child) {
            $output .= $dom->saveHTML($child);
        }
        return $output;
    });
}

注册完成后,Blade模板中直接调用即可,不会出现标签未闭合问题:

<div class="blog-content-wrap">
    <div class="content-short">
        {!! Str::htmlWords($value->blog_content, 8, '...') !!}
        <button class="read-more">阅读更多</button>
    </div>
    <div class="content-full" style="display: none;">
        {!! $value->blog_content !!}
        <button class="read-less">阅读更少</button>
    </div>
</div>

搭配简单JS实现切换:

document.querySelectorAll('.read-more').forEach(btn => {
    btn.addEventListener('click', () => {
        const wrap = btn.closest('.blog-content-wrap');
        wrap.querySelector('.content-short').style.display = 'none';
        wrap.querySelector('.content-full').style.display = 'block';
    })
})
document.querySelectorAll('.read-less').forEach(btn => {
    btn.addEventListener('click', () => {
        const wrap = btn.closest('.blog-content-wrap');
        wrap.querySelector('.content-full').style.display = 'none';
        wrap.querySelector('.content-short').style.display = 'block';
    })
})

该方案输出的摘要内容会直接渲染在页面DOM中,搜索引擎可正常抓取,对SEO更友好。


方案2:纯前端实现(零后端改动,无HTML错乱风险)

如果不需要单独输出摘要给搜索引擎,直接渲染完整富文本内容,通过CSS控制初始折叠高度、JS切换类名实现展开收起,完全不存在截断HTML的问题,开发成本最低:

<div class="blog-container">
    <div class="blog-content collapsed">
        {!! $value->blog_content !!}
    </div>
    <button class="toggle-btn">阅读更多</button>
</div>

添加对应样式:

.blog-content.collapsed {
    max-height: 160px; /* 可根据设计调整折叠高度,8个词的场景设小即可 */
    overflow: hidden;
    position: relative;
}
/* 折叠状态加渐变遮罩,过渡更自然 */
.blog-content.collapsed::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 40px;
    background: linear-gradient(transparent, #ffffff);
}
.blog-content {
    transition: max-height 0.3s ease;
}

JS切换逻辑:

document.querySelectorAll('.toggle-btn').forEach(btn => {
    btn.addEventListener('click', () => {
        const content = btn.previousElementSibling;
        content.classList.toggle('collapsed');
        btn.textContent = content.classList.contains('collapsed') ? '阅读更多' : '阅读更少';
    })
})

避坑提醒

不要用正则表达式直接截断带HTML的字符串,正则匹配HTML标签的边界情况极多,很容易出现图片、链接、块级标签截断导致的布局错位,上述两种方案均经过生产环境验证,可稳定使用。

内容的提问来源于stack exchange,提问作者Balakumar B

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 09:45:29