填写您的邮件地址,订阅我们的精彩内容:

为WordPress文章添加目录索引锚文字列表

820

在之前的文章无需插件为wordpress文章添加目录索引中,我介绍了如何实现为文章添加目录索引。为何要添加目录索引就不再赘述。本文详细的来介绍实现该目标的逻辑思路。为了描述方便,我们将上面这篇文章成为“文A”。而这种目录在我的另一个SEO博客嗖录网中已经利用起来,有兴趣的朋友可以去看一下效果。

一、使用add_filter为文章中的特定标识添加锚

在我们的文章中,使用目录索引的情况很常见,一般都是把某个节点的标题(类似学术论文中的章节标题)锚点,点击对应的目录条目,就自动跳转到那个标题。因此,我们需要先为这些标题添加锚点。

为谁添加?一般来说,我们会将文章的标题用<h1>标起来,而在文章中,我们用<h2><h3><h4>等作为子标题,因此,我们将这些h标签进行处理,使他们成为锚点对象。而实际上,我们一般只会构建两层目录,即章、节两级。我们的博客文章不比学术论文那么长,因此,也不会设置三级以上的目录,所以我们只负责对<h2><h3>进行处理,形成二级目录,效果如下图。

2013-05-28-195608

图1 文章目录索引列表

第二层对于第一层网右缩进了一个字符。对应的的一级目录是<h2>标签,二级则对应<h3>标签。在SEO中,h3标签以下的权重就几乎和一般文字一样了。

接下来就用一段代码来实现为文章内容里的h2、h3添加锚点。

function filter_article_index($content) {
?? ?global $post,$page,$numpages,$multipage;
?? ?$link = is_singular() ? ' <a href="#article-index" title="返回目录" style="color:red;font-size:12px;">↑</a>' : '';
?? ?$parentMatch = "/<h2>([^<]+)</h2>/im";
?? ?$childMatch = "/<h3>([^<]+)</h3>/im";
?? ?if(preg_match_all($parentMatch,$post->post_content,$parents)){
?? ??? ?foreach($parents[1] as $num => $title){
?? ??? ??? ?$tmp = $num + 1;
?? ??? ??? ?if(!is_singular())$content = str_replace($parents[0][$num],"<h3>{$title}{$link}</h3>",$content);
?? ??? ??? ?else $content = str_replace($parents[0][$num],"<h2 id="title-{$tmp}">{$title}{$link}</h2>",$content);
?? ??? ?}
?? ?}
?? ?$subContents = preg_split($parentMatch,$post->post_content);
?? ?if(!empty($subContents))foreach($subContents as $num => $subContent){
?? ??? ?if(preg_match_all($childMatch,$subContent,$children)){
?? ??? ??? ?foreach($children[1] as $n => $child){
?? ??? ??? ??? ?$tmp = $n + 1;
?? ??? ??? ??? ?if(!is_singular())$content = str_replace($children[0][$n],"<h4>{$child}{$link}</h4>",$content);
?? ??? ??? ??? ?else $content = str_replace($children[0][$n],"<h3 id="title-{$num}-{$tmp}">{$child}{$link}</h3>",$content);
?? ??? ??? ?}
?? ??? ?}
?? ?}
?? ?return $content;
}
add_filter("the_content","filter_article_index",99);

上面的代码中,有一些比较难理解之处我已经用红色标注出来。难理解并非指该php语句难理解,而是指为何该处要这样去处理和考虑。

$link:是在文章中该锚点后面添加一个返回索引列表位置的链接,当用户阅读到某一个节点的时候,想回去再看下文章的目录,可以通过点击该链接跳回目录区域。

if(!is_singular()):要进行这个判断是为何?你会发现在此后的php代码,实现将原来的<h2>转换为<h3>,将<h3>转换为<h4>,这是因为在首页、分类、标签等列表页,我们通常不会在文章列表的内容内部还是用<h2>,而会把h2给该文章的标题,因此将列表中文章内容里面的h2降级为h3,h3的处理同样是这个道理。而且在列表页,明显有着样式的不同。

在文A中,我们以h3为锚点,但实际上这样只能实现一级目录,最难的是实现二级过程中,如何区分该二级目录属于哪一个一级目录。因此,在之后的红色代码就是关键性的,$subContents就是根据h2来进行内容切割,以确定h3将会出现在哪一个h2下面。

还有一点需要指出来:该代码是以id作为锚点,也就是说如果点击<a href=”#title-1-2″>…</a>,那么就会跳转到<h3 id=”title-1-2″></h3>处,可是在一些老的IE浏览器中,并不会首先支持id作为锚,而更倾向使用<a name=”title-1-2″></a>作为锚点,其实这也无妨,无非是将上面的h2、h3前面使用<a name>来实现即可。

二、在文章中打印出目录结构

这一节显得非常复杂,实现起来也非常难。目前尚未完全实现自动化,因为我发现在实现时可能存在一种可能的错误,这可能与主题相关。

要实现图1的效果真的不轻松,不单单因为要实现这种样式,更关键的有如下两点:

  1. 如何分级(看上去在上面已经解决,实际上我们会遇到问题)
  2. 分页的情况下如何办:这是最棘手的问题,文章分页将会给文A的实现方法带来致命打击

接下来我们来看下代码:

[payfor price="10"]function which_page_string_in_post($string,$postContent){
?? ?$pageNum = substr_count($postContent,'<!--nextpage-->') + 1;
?? ?if($pageNum > 1){
?? ??? ?$everyPages = explode('<!--nextpage-->',$postContent);
?? ??? ?foreach($everyPages as $page => $everyPage){
?? ??? ??? ?if(strpos($everyPage,$string) !== false)return $page + 1;
?? ??? ?}
?? ?}else{
?? ??? ?return 1;
?? ?}
}

function get_article_index(){
?? ?global $post,$page,$numpages,$multipage;
?? ?$match = "/<h2>([^<]+)</h2>/im";
?? ?$content = $post->post_content;
?? ?$link = get_permalink($post->ID);
?? ?$index = '<div';
?? ?if(is_singular())$index .= ' id="article-index"';
?? ?$index .= "><strong>目录:</strong> n <ul class='article-index-root'>n";
?? ?$subContents = preg_split($match,$content);
?? ?if(preg_match_all($match,$content,$matches)){
?? ??? ?foreach($matches[1] as $num => $title) {
?? ??? ??? ?$whichPage = which_page_string_in_post($title,$post->post_content);
?? ??? ??? ?if(is_singular()){
?? ??? ??? ??? ?$currentPage = $page;
?? ??? ??? ??? ?if($whichPage == $currentPage)$link = '';
?? ??? ??? ??? ?elseif($whichPage != 1) $link = get_permalink($post->ID)."{$whichPage}/";
?? ??? ??? ?}else{
?? ??? ??? ??? ?if($whichPage != 1)$link = get_permalink($post->ID)."{$whichPage}/";
?? ??? ??? ?}
?? ??? ??? ?$poserNum = $num + 1;
?? ??? ??? ?$index .= "<li><a href="{$link}#title-{$poserNum}" title="{$title}" catalog="true">{$title}</a></li>n";
?? ??? ??? ?$subContent = $subContents[$num+1];
?? ??? ??? ?if(preg_match_all("/<h3>([^<]+)</h3>/im",$subContent,$children)){
?? ??? ??? ??? ?$index .= "<ul class='article-index-children'>n";
?? ??? ??? ??? ?foreach($children[1] as $n => $child){
?? ??? ??? ??? ??? ?$whichPage = which_page_string_in_post($child,$post->post_content);
?? ??? ??? ??? ??? ?if(is_singular()){
?? ??? ??? ??? ??? ??? ?$currentPage = $page;
?? ??? ??? ??? ??? ??? ?if($whichPage == $currentPage)$link = '';
?? ??? ??? ??? ??? ??? ?elseif($whichPage != 1) $link = get_permalink($post->ID)."{$whichPage}/";
?? ??? ??? ??? ??? ?}else{
?? ??? ??? ??? ??? ??? ?if($whichPage != 1)$link = get_permalink($post->ID)."{$whichPage}/";
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?$poserChild = $n + 1;
?? ??? ??? ??? ??? ?$index .= "<li><a href="{$link}#title-{$poserNum}-{$poserChild}" title="{$child}" catalog="true">{$child}</a></li>n";
?? ??? ??? ??? ?}
?? ??? ??? ??? ?$index .= "</ul>n";
?? ??? ??? ?}
?? ??? ?}
?? ?}else{
?? ??? ?if(preg_match_all("/<h3>([^<]+)</h3>/im",$content,$children)){
?? ??? ??? ?foreach($children[1] as $n => $child){
?? ??? ??? ??? ?$whichPage = which_page_string_in_post($child,$post->post_content);
?? ??? ??? ??? ?if(is_singular()){
?? ??? ??? ??? ??? ?$currentPage = $page;
?? ??? ??? ??? ??? ?if($whichPage == $currentPage)$link = '';
?? ??? ??? ??? ??? ?elseif($whichPage != 1) $link = get_permalink($post->ID)."{$whichPage}/";
?? ??? ??? ??? ?}else{
?? ??? ??? ??? ??? ?if($whichPage != 1)$link = get_permalink($post->ID)."{$whichPage}/";
?? ??? ??? ??? ?}
?? ??? ??? ??? ?$poserChild = $n + 1;
?? ??? ??? ??? ?$index .= "<li><a href="{$link}#title-0-{$poserChild}" title="{$child}" catalog="true">{$child}</a></li>n";
?? ??? ??? ?}
?? ??? ?}else{
?? ??? ??? ?return NULL;
?? ??? ?}
?? ?}
?? ?return $index."</ul>n</div>";
}
function the_article_index(){
?? ?echo get_article_index();
}[/payfor]

我曾尝试过使用add_filter(‘the_content’,’xxxfunction’),利用get_article_index来实现自动化,但可能是由于主题原因,未能实现。因此要使用目录列表,如图1中一样打印出来,你必须修改主题文件,在需要显示的地方使用the_article_index函数,且这个函数必须使用在the_post之后。

上述的代码确实很复杂,如果你不静下心来阅读,或许会被里面多层的foreach搞的一塌糊涂。需要解释的一点是,我考虑到了文章翻页的情况,这是最不妙的,不过已经被我解决了。当我们的文章有翻页的时候,我们很难用直接的锚来实现跳转,而实际上,目录中的锚可能是第二页的直接连接,而非真的锚。例如目录中<a href=”#title-1-3″>是跳转到第一章第三节,但<a href=”http://yourname.com/postname/2/#title-3-2″>才能真正翻到第三章第二节,用<a href=”#title-3-2″>是无法翻到的。为了获得postname后面的/2/这个页数,简直耗费了我两天的时间。这个得益于我写的一个函数which_page_string_in_post。这个还是用来实现确定某一个字符串$string在文章内容$postContent中的第几页。如果文章本来就不翻页的话,这个函数当然返回1,就是说该字符串就在第一页。

其他的程序我就不再多做解释,有的要解释起来实在太难,希望你花点时间加以理解,如果实在理解不透,可在下方评论中留下你的问题。

三、前台样式和动作的实现

我总希望能稍加完美,因此将前端的css样式和jquery动作脚本也贡献出来。只有这样,你才能真正实现和图1的效果相同。

css样式部分:

.article-index{border:#dedede solid 1px;padding:10px;margin:1em 0;width:280px;margin-right:1em;float:left;background:#fff;}
.article-index ul{margin:0;padding:0;margin-left:15px;}
.article-index .toggle-btn{position:absolute;right:10px;bottom:0;display:block;width:96%;text-align:right;background:#fff;}
.article-index-toggle{position:relative;padding-bottom:20px;}
.article-index-hidden{max-height:250px;_height:expression(this.width > "250" ? "250" : true);overflow:hidden;}

JS(jquery)脚本部分:

window.jQuery || document.write('<script type="text/javascript" src="http://lib.sinaapp.com/js/jquery/1.7.2/jquery.min.js">x3C/script>');
jQuery(function($){
?? ?$('.article-index').each(function(){
?? ??? ?if($(this).height() > 250)$(this).addClass('article-index-hidden article-index-toggle')
?? ??? ??? ?.append('<a href="javascript:void(0)">展开▼</a>');
?? ?});
?? ?$('.article-index a.toggle-btn').live('click',function(){
?? ??? ?var $this = $(this),$text = $this.text(),$parent = $this.parent();
?? ??? ?if($text == '展开▼'){
?? ??? ??? ?$parent.removeClass('article-index-hidden');
?? ??? ??? ?$this.text('收起▲');
?? ??? ?}
?? ??? ?if($text == '收起▲'){
?? ??? ??? ?$parent.addClass('article-index-hidden');
?? ??? ??? ?$this.text('展开▼');
?? ??? ?}
?? ?});
});

最终效果,请到嗖录网体验。

四、撰写文章时应该如何使用

在撰写文章时,你只需要将需要作为一级目录的子标题用二级标题(h2)括起来,将要作为二级目录的子标题用三级标题(h3)括起来即可。同时,在主题文件的文章内容输出开头加入函数the_article_index()。

最后,你可能还需要注意一些问题:

  1. 样式中使用了float:left,你可能需要解决表现形式的错位问题;
  2. 你可能面临函数与你的主题不兼容的问题,你只需要读懂本代码的逻辑思路即可解决。
  3. 你可能面临放在自己的网站效果不如我的嗖录网中的好,这也需要你自己逐渐调整。

总之,通过本文,你肯定已经掌握了一种新的思路,即which_page_string_in_post,利用这个思路,你将在遇到文章分页时,立于不败之地。

在朋友的建议下,将它作为插件【下载插件】,已经在嗖录网发布,你可以去阅读如何使用WordPress SEO文章内容目录索引插件,并提供宝贵意见。

下面我简单说几句