在我们公司建站系统中,前台文章详情模块,有个功能是文章详情内容自动按照字数做分页(比如:每1000字做分页),对于某些长文章来说可以提高加载速度。
由于文章详情内容是用富文本内容,包含html代码,所以不能直接做文字切分,需要对html进行解析后处理,比较复杂,这块我们以前的实现是使用正则自己去解析文字然后做分页,一旦出现表格,段落,div等组合标签就可能导致文章结构错乱,所以在以前的实现里,我们只好一刀切,判断如果有部分html标签的文章,就不进行分页,就导致这个功能时灵时不灵,形同虚设。
这一次我决定重新实现这个功能,首先,我决定不自己解析html代码,而使用第三方html解析类库,于是我在网上找到了simplehtmldom这个类库
这个类库可以将html文本转换成DOM Node树,所有的文本都在Text Node里,只需要计算Text Node中文字的数量,可以避免把标签文字计算为内容文字数量,于是整个流程简化为如下四步:
1. 遍历整个DOM数,遇到Text Node就计算字数,并判断该Node所在的页数。
2. 如果这个Node在需要显示的页面内,保留这个Node以及他所有的父节点,反之删除节点,
3. 在遍历结束后可以获取文章详情的总页数
4. 将保留下的DOM树元素重新转成html代码并显示
下面是文章分页器 php代码 :
count_text_num=0; $this->dom_cur_page=1; $thedom=str_get_html($mybody); $root=$thedom->root; $this->parseDomTree($root); return $thedom->root->innertext(); } public function getTotalPage(){ return $this->dom_cur_page; } private function parseDomTree($dom,$isinsepcialel=false){ if(empty($dom->nodes)) return; if(!$isinsepcialel && $this->count_text_num>=$this->count_per_page){ $this->dom_cur_page+=1; $this->count_text_num=0; } $domtag=strtolower($dom->tag); if($domtag=='table' || $domtag=='ul' || $domtag=='ol'){ $isinsepcialel=true; } foreach($dom->nodes as $child){ if($this->dom_cur_page!=$this->show_page) $child->is_del=1; if($child->tag=='text'){ $outertext=$child->outertext(false); $txtlen=mb_strlen($outertext,'UTF-8'); $this->count_text_num+=$txtlen; if(!$isinsepcialel && $this->count_text_num>=$this->count_per_page){ $this->dom_cur_page+=1; $this->count_text_num=0; } } $this->parseDomTree($child,$isinsepcialel); } if($dom->is_del==1){ foreach($dom->nodes as $child){ if(!$child->is_del){ $dom->is_del=0; break; } } } }}
使用代码示例:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbaaaaaaaaaaaaaavvvvvvvvvvvvvvvvvvEOD;$articlespliter=new ArticleSpliter();$articlespliter->count_per_page=10;$articlespliter->show_page=6;$splitbody=$articlespliter->split($body);$totalpage=$articlespliter->getTotalPage();
项目的github地址:
所有代码我都打包到zip包里,有需要的可以下载获取
经过了这番修改后,我们的分页逻辑变得非常的清晰,代码量减少了很多,分页效果也好了很多,线上运行了一段时间,也没有再出现格式错乱的问题了。