优化侧栏显示:实现更加美观的分类展示

提示:由于主题最新版本与撰写本文参考的版本存在差异,请预先了解本文更新指导部分。

引言

本博客使用的handsome主题十分美观,遗憾的是其默认情况下对多级分类的显示并不那么友好,优化的想法也是由来已久,昨日终于克服惰性写了简单实现。

先看看修改之前的样式:

侧栏修改前

简洁,但多重分类下展现并不是那么好,多重分类中父级分类显得很朴素,在侧栏中比重也相对小了,但我希望分类能够占据侧栏中的优势地位,而且加上图标也不失为一个Great idea,于是就进行简单修改了。

修改之后:

侧栏修改后

代码实现

对于小技巧、小功能的毫无保留,有助于不成熟技术的发展和微小功能的扩大化,进而不断提升自我;对低级操作的故步自封只能导致开发的停滞。
——我自己

以下讨论全部以handsome模板根目录为当前目录,Typecho环境下为usr/themes/handsome/,特此说明。

分析aside.php

  • 文件位置:component/aside.php
  • 功能:侧栏布局显示

结合开发人员工具定位到相关输出位置(第101行至第186行):

              <?php if (!$hideHomeItem): ?>
              <!--主页-->
              <li>
                <a href="<?php $this->options->rootUrl(); ?>/" class="auto">
                  <i class="iconfont icon-zhuye icon text-md"></i>
                  <span><?php _me("首页") ?></span>
                </a>
              </li>
              <!-- /主页 -->
              <?php endif; ?>
              <?php echo @$asideItemsOutput ?>
                <?php if (@!in_array('component',$this->options->asideSetting)): ?>
              <li class="line dk"></li>
            <!--Components-->
              <li class="hidden-folded padder m-t m-b-sm text-muted text-xs">
                <span><?php _me("组成") ?></span>
              </li>
              <!--分类category-->
              <li>
                <a class="auto">
                  <span class="pull-right text-muted">
                    <i class="fontello icon-fw fontello-angle-right text"></i>
                    <i class="fontello icon-fw fontello-angle-down text-active"></i>
                  </span>
                  <i class="glyphicon glyphicon-th"></i>
                  <span><?php _me("分类") ?></span>
                </a>
                <ul class="nav nav-sub dk">
                  <li class="nav-sub-header">
                    <a>
                      <span><?php _me("分类") ?></span>
                    </a>
                  </li>
                  <!--循环输出分类-->
                    <?php
                    $this->widget('Widget_Metas_Category_List')->to($categorys);
                    echo Content::returnCategories($categorys) ?>
                </ul>
              </li>
              <!--独立页面pages-->
              <li>
                <a class="auto">
                  <span class="pull-right text-muted">
                    <i class="fontello icon-fw fontello-angle-right text"></i>
                    <i class="fontello icon-fw fontello-angle-down text-active"></i>
                  </span>
                  <i class="glyphicon glyphicon-file"></i>
                  <span><?php _me("页面") ?></span>
                </a>
                <ul class="nav nav-sub dk">
                  <li class="nav-sub-header">
                    <a data-no-instant>
                      <span><?php _me("页面") ?></span>
                    </a>
                  </li><!--这个字段不会被显示出来-->
                  <!--循环输出独立页面-->
                  <?php $this->widget('Widget_Contents_Page_List')->to($pages); ?>
                   <?php while($pages->next()): ?>
                       <?php if ($pages->fields->navbar == "hide") continue; ?>
                       <li><a href="<?php $pages->permalink(); ?>"><span><?php $pages->title(); ?></span></a></li>
                   <?php endwhile; ?>
                </ul>
              </li>
              <!--友情链接-->
              <li>
                <a class="auto">
                  <span class="pull-right text-muted">
                    <i class="fontello icon-fw fontello-angle-right text"></i>
                    <i class="fontello icon-fw fontello-angle-down text-active"></i>
                  </span>
                  <i class="iconfont icon-links"></i>
                  <span><?php _me("友链") ?></span>
                </a>
                <ul class="nav nav-sub dk">
                  <li class="nav-sub-header">
                    <a data-no-instant>
                      <span><?php _me("友链") ?></span>
                    </a>
                  </li>
                  <!--使用links插件,输出全站友链-->
                 <?php $mypattern1 = "<li data-original-title=\"{title}\" data-toggle=\"tooltip\" 
data-placement=\"top\"><a href=\"{url}\" target=\"_blank\"><span>{name}</span></a></li>";
                  Links_Plugin::output($mypattern1, 0, "ten");?>
                </ul>
              </li>
                <?php endif; ?>

在这里,你可以了解侧栏的基本组成方式,以及其样式的写法。我们可以暂且记住<li class="line dk"></li>生成分割线等特性,准备之后的修改使用。

不难发现分类结构的输出由Content::returnCategories($categorys)方法生成,不同主题输出分类结构不一定相同,所以它不会是Typecho自带方法,猜测为libs/Content.php所实现,不出意料。

改写returnCategories方法

  • 文件位置:libs/Content.php
  • 功能:使用PHP方法输出内容

2262行至第2301行:

    /**
     * @param $categories
     * @return string
     */
    public static function returnCategories($categories){
        $html = "";
        $options = mget();
        while($categories->next()){
            if ($categories->levels === 0){//父亲分类

                $children = $categories->getAllChildren($categories->mid);//获取当前父分类所有子分类
                //print_r($children);
                //var_dump(empty($children));
                if (!empty($children)){//子分类不为空
                    $html .= '<li><a class="auto" href="'.$categories->permalink.'"><span class="pull-right text-muted">
                    <i class="fontello icon-fw fontello-angle-right text"></i>
                    <i class="fontello icon-fw fontello-angle-down text-active"></i>
                  </span><span>'.$categories->name.'</span></a>';
                    //循环输出子分类
                    $childCategoryHtml = '<ul class="nav nav-sub dk child-nav">';
                    //有子分类判断是否输出父分类
                    if (!in_array('noShowParentCategory',$options->featuresetup)){
                        $childCategoryHtml .= '<li><a href="'.$categories->permalink.'"><b class="badge pull-right">'.$categories->count.'</b><span>'.$categories->name.'</span></a></li>';
                    }
                    foreach ($children as $mid){
                        $child = $categories->getCategory($mid);
                        $childCategoryHtml .= '<li><a href="'.$child['permalink'].'"><b class="badge pull-right">'.$child['count'].'</b><span>'.$child['name'].'</span></a></li>';
                    }
                    $childCategoryHtml .= '</ul>';

                    $html .= $childCategoryHtml;
                    $html .= "</li>";
                }else{//没有子分类
                    $html .= '<li><a href="'.$categories->permalink.'"><b class="badge pull-right">'.$categories->count.'</b><span>'.$categories->name.'</span></a></li>';
                }
            }
        }

        return $html;
    }

目标:将父级分类参与侧栏构成修改为一级列表,同时加上自定义图标。

作者的逻辑比较清晰,Modify起来可以省很多事。
先捡软柿子捏,首先,第2281行删除其child-nav样式,将子项目显示等级提高(否则左侧会有不协调的空白);然后,参考aside.php中了解到的样式写法,检查该方法的返回数据。非常幸运,这些数据已经可以直接作为一级侧栏列表项使用了,并不需要结构上的修改。
下面就是比较难以描述的自定义图标实现了。

我的思路是定义一个$icons索引数组,将分类的slug(英文名称)作为索引,结合本博客实际情况,如下:

$icons = array(
    //$slug => 'icon',
    'algorithm-oi' => 'iconfont icon-kinds',
    'project-share' => 'fa fa-tasks',
    'acgn' => 'fa fa-gift',
    'words' => 'fa fa-heartbeat',
    'files' => 'fa fa-folder',
);

方便起见,我已经手动引入Font Awesome图标库。

写好了图标映射表,下一步就是将它插入到返回的分类数据中去。图标我们一般使用<i class="xxx"></i>来实现。插入的过程不好描述,贴出代码,请自行领悟:

    /**
     * @param $categories
     * @return string
     */
    public static function returnCategories($categories){
        $html = "";
        $options = mget();
        $icons = array(
            //$slug => 'icon',
            'algorithm-oi' => 'iconfont icon-kinds',
            'project-share' => 'fa fa-tasks',
            'acgn' => 'fa fa-gift',
            'words' => 'fa fa-heartbeat',
            'files' => 'fa fa-folder',
        );
        while($categories->next()){
            if ($categories->levels === 0){//父亲分类

                $children = $categories->getAllChildren($categories->mid);//获取当前父分类所有子分类
                //print_r($children);
                //var_dump(empty($children));
                if (!empty($children)){//子分类不为空
                    $html .= '<li><a class="auto" href="'.$categories->permalink.'"><span class="pull-right text-muted">
                    <i class="fontello icon-fw fontello-angle-right text"></i>
                    <i class="fontello icon-fw fontello-angle-down text-active"></i>
                  </span><i class="'.$icons[$categories->slug].'"></i><span>'.$categories->name.'</span></a>';//* 这里
                    //循环输出子分类
                    $childCategoryHtml = '<ul class="nav nav-sub dk">
                                            <li class="nav-sub-header">
                                              <a>
                                                <span>'.$categories->name.'</span>
                                              </a>
                                            </li>';
                    //有子分类判断是否输出父分类
                    if (!in_array('noShowParentCategory',$options->featuresetup)){
                        $childCategoryHtml .= '<li><a href="'.$categories->permalink.'"><b class="badge pull-right">'.$categories->count.'</b><i class="'.$icons[$categories->slug].'"></i><span>'.$categories->name.'</span></a></li>';//* 这里
                    }
                    foreach ($children as $mid){
                        $child = $categories->getCategory($mid);
                        $childCategoryHtml .= '<li><a href="'.$child['permalink'].'"><b class="badge pull-right">'.$child['count'].'</b><i class="'.$icons[$child['slug']].'"></i><span>'.$child['name'].'</span></a></li>';//* 这里
                    }
                    $childCategoryHtml .= '</ul>';

                    $html .= $childCategoryHtml;
                    $html .= "</li>";
                }else{//没有子分类
                    $html .= '<li><a href="'.$categories->permalink.'"><b class="badge pull-right">'.$categories->count.'</b><i class="'.$icons[$categories->slug].'"></i><span>'.$categories->name.'</span></a></li>';//* 这里
                }
            }
        }

        return $html;
    }

上述代码中插入图标元素的位置均已使用//* 这里标示。

如果你希望实现类似效果,请不要直接复制粘贴,希望在理解其思想后进行自定义修改,再进行部署。欢迎在评论区和我交流相关问题~

至此,关于难啃的硬骨头Content.phpreturnCategories方法的修改已经全部完成。

修改aside.php

  • 文件位置及功能参考“分析aside.php”中叙述。

原始代码不再贴出,稍作讲解后将贴出修改后代码。

刚刚已经提到,returnCategories方法的返回值现在已经可以直接作为一级列表项使用,于是删除外层套着的<li>元素,并在适当位置添加分割线(还记得吗,分析时已经看到的)即可。

对于页面项目的修改同理可以完成,不再赘述。

这里介绍一个小技巧,在后台编辑页面的“自定义字段”中,新增一条名为navbar的字段,将其值设置为hide,即可在侧栏中隐藏该页面。

本博客视情况隐藏了“友情链接”页面,自定义在了一级列表项“友情链接”下的“总览”中。

              <?php if (!$hideHomeItem): ?>
              <!--主页-->
              <li>
                <a href="<?php $this->options->rootUrl(); ?>/" class="auto">
                  <i class="iconfont icon-zhuye icon text-md"></i>
                  <span><?php _me("首页") ?></span>
                </a>
              </li>
              <!-- /主页 -->
              <?php endif; ?>
              <?php echo @$asideItemsOutput ?>
                <?php if (@!in_array('component',$this->options->asideSetting)): ?>
              <li class="line dk"></li>
            <!--Components-->
              <li class="hidden-folded padder m-t m-b-sm text-muted text-xs">
                <span><?php _me("分类") ?></span>
              </li>
              <!--分类category-->
              <!--循环输出分类-->
              <?php
              $this->widget('Widget_Metas_Category_List')->to($categorys);
              echo Content::returnCategories($categorys) ?>
              <li class="line dk"></li>
              <!--独立页面pages-->
              <li class="hidden-folded padder m-t m-b-sm text-muted text-xs">
                <span><?php _me("页面") ?></span>
              </li>
              <!--循环输出独立页面-->
              <?php
                    $icons = array(
                        //$slug => 'icon',
                        'articles' => 'fa fa-archive',
                        'board' => 'fa fa-commenting',
                        'cross' => 'fa fa-coffee',
                    );
              ?>
              <?php $this->widget('Widget_Contents_Page_List')->to($pages); ?>
              <?php while($pages->next()): ?>
                  <?php if ($pages->fields->navbar == "hide") continue; ?>
                  <li><a href="<?php $pages->permalink(); ?>"><i class="<?php _me($icons[$pages->slug]); ?>"></i><span><?php $pages->title(); ?></span></a></li>
              <?php endwhile; ?>
              <!--友情链接-->
              <li>
                <a class="auto">
                  <span class="pull-right text-muted">
                    <i class="fontello icon-fw fontello-angle-right text"></i>
                    <i class="fontello icon-fw fontello-angle-down text-active"></i>
                  </span>
                  <i class="iconfont icon-links"></i>
                  <span><?php _me("友情链接") ?></span>
                </a>
                <ul class="nav nav-sub dk">
                  <li class="nav-sub-header">
                    <a data-no-instant>
                      <span><?php _me("友情链接") ?></span>
                    </a>
                  </li>
                  <li><a href="/links.html"><span>总览</span></a></li>
                  <!--使用links插件,输出全站友链-->
                 <?php $mypattern1 = "<li data-original-title=\"{title}\" data-toggle=\"tooltip\" 
data-placement=\"top\"><a href=\"{url}\" target=\"_blank\"><span>{name}</span></a></li>";
                  Links_Plugin::output($mypattern1, 0, "ten");?>
                </ul>
              </li>
                <?php endif; ?>

由于我比较懒,在输出总览时直接输出了永久链接,而没有动态获取,需要注意。

部署后即可实现目标效果。

更新指导

Update on 2019-07-22

已更新至handsome 5.2.0,上述内容基本适用,但行号稍有偏差,请联系具体代码上下文确定新版本文件应修改的位置。

Update on 2020-05-10

对于 handsome 6.x 以及后续不再提供“不输出父分类”选项的版本,使用以下代码:

    /**
     * @param $categories
     * @return string
     */
    public static function returnCategories($categories){
        $html = "";
        $options = mget();
        $icons = array(
            //$slug => 'icon',
            'algorithm-oi' => 'iconfont icon-kinds',
            'project-share' => 'fa fa-tasks',
            'acgn' => 'fa fa-gift',
            'words' => 'fa fa-heartbeat',
            'files' => 'fa fa-folder',
        );
        while($categories->next()){
            if ($categories->levels === 0){//父亲分类

                $children = $categories->getAllChildren($categories->mid);//获取当前父分类所有子分类
                //print_r($children);
                //var_dump(empty($children));
                if (!empty($children)){//子分类不为空
                    $html .= '<li><a class="auto" href="'.$categories->permalink.'"><span class="pull-right text-muted">
                    <i class="fontello icon-fw fontello-angle-right text"></i>
                    <i class="fontello icon-fw fontello-angle-down text-active"></i>
                  </span><i class="'.$icons[$categories->slug].'"></i><span>'.$categories->name.'</span></a>';//* 这里
                    //循环输出子分类
                    $childCategoryHtml = '<ul class="nav nav-sub dk">
                                            <li class="nav-sub-header">
                                              <a>
                                                <span>'.$categories->name.'</span>
                                              </a>
                                            </li>';
                    foreach ($children as $mid){
                        $child = $categories->getCategory($mid);
                        $childCategoryHtml .= '<li><a href="'.$child['permalink'].'"><b class="badge pull-right">'.$child['count'].'</b><i class="'.$icons[$child['slug']].'"></i><span>'.$child['name'].'</span></a></li>';//* 这里
                    }
                    $childCategoryHtml .= '</ul>';

                    $html .= $childCategoryHtml;
                    $html .= "</li>";
                }else{//没有子分类
                    $html .= '<li><a href="'.$categories->permalink.'"><b class="badge pull-right">'.$categories->count.'</b><i class="'.$icons[$categories->slug].'"></i><span>'.$categories->name.'</span></a></li>';//* 这里
                }
            }
        }

        return $html;
    }
最后修改:2020 年 05 月 25 日 10 : 34 AM
欢迎投食喵 ~

发表评论

24 条评论

  1. majw

    博主能更新一下吗,支持一下最新的9.2版本

  2. SouFan

    大佬你好,请问怎么让页脚底部的信息可以延长到这个右侧栏呢,我只能显示在中间

    1. 无限UCW
      @SouFan

      我用 Devtools 检查了你的 blog,#footer 这个元素(它是 <footer id="footer" ...> 这个东西)有一个 margin-right: 240px 的样式,把这个 margin-right 调小就可以了。

      1. SouFan
        @无限UCW

        我将/usr/themes/handsome/component/footer.php里的<footer id="footer" class="app-footer" role="footer">,约117行,改为<footer id="footer" class="app-footer" role="footer" style="margin-right: 0px;">后还是不行,可能是什么原因呢

        1. 无限UCW
          @SouFan

          你的修改方法应该是有效的,建议仔细检查数据链路,确保前台 Devtools 中查看到的样式正确生效。

          1. SouFan
            @无限UCW

            好的,谢谢大佬,我再去试试

  3. Kester

    Hi,友链一个可以吗?
    博客名称:乔戈路阔阔;
    博客网址:https://chelgr.com
    博客头像:https://chelgr.com/share/chelgr-com-icon.png
    博客介绍:缅怀过去 | 记录现在 | 心存未来...

  4. Kester

    博主,我想请教一下几个问题
    1.如何实现这个主题的左右侧边栏比原生主题窄了一点/导航文字也是;
    2.还有就是如何添加像你网站盒子里面的框线,例如右边栏,页头,面包屑,博客信息等等位置
    3.然后就是在手机自适应页面下,右上角是个setting的icon,是怎么弄的呢?

    请问这些是怎么设置的呢?主题后台无法直接实现!

    1. 无限UCW
      @Kester

      不好意思,这个具体我也记不太清了 ,有些可能是旧版本 handsome 的特性(我很久没有仔细维护 Blog 了),有些可能是无意中改的。一般这种主题后台无法直接改的东西,需要修改源码实现更高度的客制化,但我印象中后续版本的 handsome 引入源代码混淆加密使得这个操作可能不太轻松。我知道的就只有这些了,很遗憾提供不了什么帮助……

      1. Kester
        @无限UCW

        感激你的回复!你的回答至少也让我清楚了一些关于主题版本有可能造成的差别!要在茫茫博主中得到诚心的回复也不容易!所以你的回复已经对我很nice了!谢谢!我会继续学习的,也会在以后耐心指导别人,倘若遇到比我还新的人!哈哈... 感恩!好人

  5. 熊熊

    在后台编辑页面的“自定义字段”中,新增一条名为navbar的字段,将其值设置为hide,即可在侧栏中隐藏该页面。
    这句帮大忙了!

  6. 二分博客
    该评论仅登录用户及评论双方可见
  7. Gamesme

    前端

    <!--循环输出分类-->
                  <?php
                  $this->widget('Widget_Metas_Category_List')->to($categorys);
                  $icons = array(
                    //$slug => 'icon',
                    'default' => 'iconfont icon-kinds',
                    'default1' => 'fa fa-tasks',
                    'de' => 'fa fa-gift',
                    'words' => 'fa fa-heartbeat',
                    'files' => 'fa fa-folder',
                );
                  echo Content::returnCategories($categorys,$icons) ?>
                  <li class="line dk"></li>
                  <!--独立页面pages-->

    后端

    public static function returnCategories($categories,$icons)

    然后偷偷在后台魔改个自定义设置区域
    是不是就能随便改了

    1. 大白菜
      @Gamesme

      兄嘚,苦思,但难耐学识有限。我看了你的博客“https://gamesme.cn/”, 终于找到了我想要的侧边栏,也看过许多资料,但是如果自己动手的话,真的不知道怎么下。希望,看到如果你看到此留言能够分享一下你的操作经验(*❦ω❦)。(群里没人会,但有很多人问。)

      1. Gamesme
        @大白菜

        兄弟你在哪个群啊 我在群里没看到你 我所在群号 553158260

        1. 小熊猫
          @Gamesme

          搞定了,哎呀,痛苦的过程 ̄﹃ ̄

    2. 无限
      @Gamesme

      其实你魔改出自定义设置区域以后就没必要再传参了,写在配置里面(可以考虑插件)然后手改returnCategories方法让它自动获取出来可能会舒服一点

    3. 无限
      @Gamesme

      对√
      不过我比较懒就没写

  8. Gamesme

    轮子偷走了

    1. 无限
      @Gamesme

      欢迎指点

  9. 站着茅坑看大片

    博主用的是哪一款的代码高亮鸭,蛮喜欢的

    1. 无限
      @站着茅坑看大片

      手改的……不过不是我改的……
      https://moe.best/ 参考这位大佬Orz

  10. 求文件emmm

    博主我很喜欢你这个侧边栏,能直接伸手1比1拿走吗OωO,我又菜又懒耶

    1. 无限
      @求文件emmm

      上面就是全部文件了……OωO
      这个操作定制化比较强,我即使把我改过的代码发给你,也是不会显示图标的,而你按照我上面所叙述的方式进行修改也可以得到相同的效果,改掉$icons数组就可以实现图标了。