wordpress进阶教程(三十九):wordpress输出bootstrap的菜单结构

现在自适应网页(即常说的响应式设计,一个网页在PC\平板\手机上显示不同的布局)用的越来越多,然而,对于大多数人来说,写一个自适应的网页并非易事,于是有了bootstrap。

Bootstrap是twitter的工程师利用业余时间制作推出的一个开源的用于前端开发的工具包,即里面已经写好了css js,你只需要引入它的js和css,然后根据要求,给网页的div添加相应的class属性,即可制作一个响应式网页。

说实话,bootstrap很方便,作者使用过一次,但是有一个缺点,那就是你需要引入bootstrap的框架的css和js,然而这个css里面,有很多代码都是你用不到的,这样就会产生很多冗余代码,而去除css的冗余代码绝对是个体力活,所以作者用过一次就不用了。

将bootstrap应用到wordpress上也很简单,唯一可能有困难的就是菜单,因为bootstrap的菜单有他自己的结构和属性,最新的菜单演示页面如下:http://v3.bootcss.com/examples/navbar/

bootcssmenu

查看网页源文件,可以看到菜单的结构大概是这样

  1. <ul class="nav navbar-nav">   
  2.   <li class="active"><a href="#">Link</a></li>   
  3.   <li class="dropdown">   
  4.     <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>   
  5.     <ul class="dropdown-menu">   
  6.       <li><a href="#">Action</a></li>   
  7.       <li><a href="#">Another action</a></li>   
  8.     </ul>   
  9.   </li>   
  10. </ul>  

要在wordpress上实现这个菜单结构看似不难,其实,里面Dropdown后面的<b class="caret"></b>对应网页中下拉菜单的那个到三角形,以及二级菜单的ul标签的class属性。除非你把菜单写死,否则使用wp_nav_menu函数是无法输出这两个内容的。

那要怎么办呢?
wordpress的wp_nav_menu函数参数如下:

  1. <?php   
  2.   
  3. $defaults = array(   
  4.     'theme_location'  => '',   
  5.     'menu'            => '',   
  6.     'container'       => 'div',   
  7.     'container_class' => '',   
  8.     'container_id'    => '',   
  9.     'menu_class'      => 'menu',   
  10.     'menu_id'         => '',   
  11.     'echo'            => true,   
  12.     'fallback_cb'     => 'wp_page_menu',   
  13.     'before'          => '',   
  14.     'after'           => '',   
  15.     'link_before'     => '',   
  16.     'link_after'      => '',   
  17.     'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>',   
  18.     'depth'           => 0,   
  19.     'walker'          => ''  
  20. );   
  21.   
  22. wp_nav_menu( $defaults );   
  23.   
  24. ?>  

其中的walker参数是关键。

更改你的主题菜单输出函数wp_nav_menu的walker参数:

  1. <?php   
  2. wp_nav_menu( array(   
  3. 'theme_location' => 'ashu_menu',   
  4. 'depth' => 2,   
  5. 'container' => false,   
  6. 'menu_class' => 'nav navbar-nav',   
  7. 'fallback_cb' => 'wp_page_menu',   
  8. //添加或更改walker参数   
  9. 'walker' => new wp_bootstrap_navwalker())   
  10. );   
  11. ?>  

上面代码中walker参数的值是一个类,所以接下来你需要添加这个类,在你的主题functions.php文件中添加下面代码:

或者https://github.com/twittem/wp-bootstrap-navwalker/blob/master/wp_bootstrap_navwalker.php

  1. <?php   
  2.   
  3. /**
  4.  * Class Name: wp_bootstrap_navwalker  
  5.  * GitHub URI: https://github.com/twittem/wp-bootstrap-navwalker  
  6.  * Description: A custom WordPress nav walker class to implement the Bootstrap 3 navigation style in a custom theme using the WordPress built in menu manager.  
  7.  * Version: 2.0.4  
  8.  * Author: Edward McIntyre - @twittem  
  9.  * License: GPL-2.0+  
  10.  * License URI: http://www.gnu.org/licenses/gpl-2.0.txt  
  11.  */  
  12.   
  13. class wp_bootstrap_navwalker extends Walker_Nav_Menu {   
  14.   
  15.     /**
  16.      * @see Walker::start_lvl()  
  17.      * @since 3.0.0  
  18.      *  
  19.      * @param string $output Passed by reference. Used to append additional content.  
  20.      * @param int $depth Depth of page. Used for padding.  
  21.      */  
  22.     public function start_lvl( &$output$depth = 0, $args = array() ) {   
  23.         $indent = str_repeat"\t"$depth );   
  24.         $output .= "\n$indent<ul role=\"menu\" class=\" dropdown-menu\">\n";   
  25.     }   
  26.   
  27.     /**
  28.      * @see Walker::start_el()  
  29.      * @since 3.0.0  
  30.      *  
  31.      * @param string $output Passed by reference. Used to append additional content.  
  32.      * @param object $item Menu item data object.  
  33.      * @param int $depth Depth of menu item. Used for padding.  
  34.      * @param int $current_page Menu item ID.  
  35.      * @param object $args  
  36.      */  
  37.     public function start_el( &$output$item$depth = 0, $args = array(), $id = 0 ) {   
  38.         $indent = ( $depth ) ? str_repeat"\t"$depth ) : '';   
  39.   
  40.         /**
  41.          * Dividers, Headers or Disabled  
  42.          * =============================  
  43.          * Determine whether the item is a Divider, Header, Disabled or regular  
  44.          * menu item. To prevent errors we use the strcasecmp() function to so a  
  45.          * comparison that is not case sensitive. The strcasecmp() function returns  
  46.          * a 0 if the strings are equal.  
  47.          */  
  48.         if ( strcasecmp$item->attr_title, 'divider' ) == 0 && $depth === 1 ) {   
  49.             $output .= $indent . '<li role="presentation" class="divider">';   
  50.         } else if ( strcasecmp$item->title, 'divider') == 0 && $depth === 1 ) {   
  51.             $output .= $indent . '<li role="presentation" class="divider">';   
  52.         } else if ( strcasecmp$item->attr_title, 'dropdown-header') == 0 && $depth === 1 ) {   
  53.             $output .= $indent . '<li role="presentation" class="dropdown-header">' . esc_attr( $item->title );   
  54.         } else if ( strcasecmp($item->attr_title, 'disabled' ) == 0 ) {   
  55.             $output .= $indent . '<li role="presentation" class="disabled"><a href="#">' . esc_attr( $item->title ) . '</a>';   
  56.         } else {   
  57.   
  58.             $class_names = $value = '';   
  59.   
  60.             $classes = empty$item->classes ) ? array() : (array$item->classes;   
  61.             $classes[] = 'menu-item-' . $item->ID;   
  62.   
  63.             $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter$classes ), $item$args ) );   
  64.   
  65.             if ( $args->has_children )   
  66.                 $class_names .= ' dropdown';   
  67.   
  68.             if ( in_array( 'current-menu-item', $classes ) )   
  69.                 $class_names .= ' active';   
  70.   
  71.             $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';   
  72.   
  73.             $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item$args );   
  74.             $id = $id ? ' id="' . esc_attr( $id ) . '"' : '';   
  75.   
  76.             $output .= $indent . '<li' . $id . $value . $class_names .'>';   
  77.   
  78.             $atts = array();   
  79.             $atts['title']  = ! empty$item->title )   ? $item->title  : '';   
  80.             $atts['target'] = ! empty$item->target )  ? $item->target : '';   
  81.             $atts['rel']    = ! empty$item->xfn )     ? $item->xfn    : '';   
  82.   
  83.             // If item has_children add atts to a.   
  84.             if ( $args->has_children && $depth === 0 ) {   
  85.                 $atts['href']           = '#';   
  86.                 $atts['data-toggle']    = 'dropdown';   
  87.                 $atts['class']          = 'dropdown-toggle';   
  88.                 $atts['aria-haspopup']  = 'true';   
  89.             } else {   
  90.                 $atts['href'] = ! empty$item->url ) ? $item->url : '';   
  91.             }   
  92.   
  93.             $atts = apply_filters( 'nav_menu_link_attributes', $atts$item$args );   
  94.   
  95.             $attributes = '';   
  96.             foreach ( $atts as $attr => $value ) {   
  97.                 if ( ! empty$value ) ) {   
  98.                     $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );   
  99.                     $attributes .= ' ' . $attr . '="' . $value . '"';   
  100.                 }   
  101.             }   
  102.   
  103.             $item_output = $args->before;   
  104.   
  105.             /*
  106.              * Glyphicons  
  107.              * ===========  
  108.              * Since the the menu item is NOT a Divider or Header we check the see  
  109.              * if there is a value in the attr_title property. If the attr_title  
  110.              * property is NOT null we apply it as the class name for the glyphicon.  
  111.              */  
  112.             if ( ! empty$item->attr_title ) )   
  113.                 $item_output .= '<a'. $attributes .'><span class="glyphicon ' . esc_attr( $item->attr_title ) . '"></span>&nbsp;';   
  114.             else  
  115.                 $item_output .= '<a'. $attributes .'>';   
  116.   
  117.             $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;   
  118.             $item_output .= ( $args->has_children && 0 === $depth ) ? ' <span class="caret"></span></a>' : '</a>';   
  119.             $item_output .= $args->after;   
  120.   
  121.             $output .= apply_filters( 'walker_nav_menu_start_el', $item_output$item$depth$args );   
  122.         }   
  123.     }   
  124.   
  125.     /**
  126.      * Traverse elements to create list from elements.  
  127.      *  
  128.      * Display one element if the element doesn't have any children otherwise,  
  129.      * display the element and its children. Will only traverse up to the max  
  130.      * depth and no ignore elements under that depth.  
  131.      *  
  132.      * This method shouldn't be called directly, use the walk() method instead.  
  133.      *  
  134.      * @see Walker::start_el()  
  135.      * @since 2.5.0  
  136.      *  
  137.      * @param object $element Data object  
  138.      * @param array $children_elements List of elements to continue traversing.  
  139.      * @param int $max_depth Max depth to traverse.  
  140.      * @param int $depth Depth of current element.  
  141.      * @param array $args  
  142.      * @param string $output Passed by reference. Used to append additional content.  
  143.      * @return null Null on failure with no changes to parameters.  
  144.      */  
  145.     public function display_element( $element, &$children_elements$max_depth$depth$args, &$output ) {   
  146.         if ( ! $element )   
  147.             return;   
  148.   
  149.         $id_field = $this->db_fields['id'];   
  150.   
  151.         // Display this element.   
  152.         if ( is_object$args[0] ) )   
  153.            $args[0]->has_children = ! empty$children_elements$element->$id_field ] );   
  154.   
  155.         parent::display_element( $element$children_elements$max_depth$depth$args$output );   
  156.     }   
  157.   
  158.     /**
  159.      * Menu Fallback  
  160.      * =============  
  161.      * If this function is assigned to the wp_nav_menu's fallback_cb variable  
  162.      * and a manu has not been assigned to the theme location in the WordPress  
  163.      * menu manager the function with display nothing to a non-logged in user,  
  164.      * and will add a link to the WordPress menu manager if logged in as an admin.  
  165.      *  
  166.      * @param array $args passed from the wp_nav_menu function.  
  167.      *  
  168.      */  
  169.     public static function fallback( $args ) {   
  170.         if ( current_user_can( 'manage_options' ) ) {   
  171.   
  172.             extract( $args );   
  173.   
  174.             $fb_output = null;   
  175.   
  176.             if ( $container ) {   
  177.                 $fb_output = '<' . $container;   
  178.   
  179.                 if ( $container_id )   
  180.                     $fb_output .= ' id="' . $container_id . '"';   
  181.   
  182.                 if ( $container_class )   
  183.                     $fb_output .= ' class="' . $container_class . '"';   
  184.   
  185.                 $fb_output .= '>';   
  186.             }   
  187.   
  188.             $fb_output .= '<ul';   
  189.   
  190.             if ( $menu_id )   
  191.                 $fb_output .= ' id="' . $menu_id . '"';   
  192.   
  193.             if ( $menu_class )   
  194.                 $fb_output .= ' class="' . $menu_class . '"';   
  195.   
  196.             $fb_output .= '>';   
  197.             $fb_output .= '<li><a href="' . admin_url( 'nav-menus.php' ) . '">Add a menu</a></li>';   
  198.             $fb_output .= '</ul>';   
  199.   
  200.             if ( $container )   
  201.                 $fb_output .= '</' . $container . '>';   
  202.   
  203.             echo $fb_output;   
  204.         }   
  205.     }   
  206. }  

已有8条评论

  1. wordpress僵
    wordpress僵 : 回复

    这个wordpress真是僵啊。。有没有方法像PHPCMS 一样可以遍历 菜单中的LI 以达到仿站啊。

    • 阿树工作室
      阿树工作室 回复wordpress僵: 回复

      你可以自己写函数的。。

      • wordpress僵
        wordpress僵 回复阿树工作室: 回复

        你的意思是写wallker函数? 好难啊 一堆代码都不知道写哪个、

  2. www.touchsth.com
    www.touchsth.com : 回复

    网站menu下拉菜单在手机端突然无法弹出,能帮忙看看这个是什么情况?www.touchsth.com

  3. wwhu
    wwhu : 回复

    溜溜溜。这东西卡了我两天。。。

    • xxing
      xxing 回复wwhu: 回复

      所以说,想做什么功能先上github上面找一找…

  4. qinnek
    qinnek : 回复

    请问下,为什么在后台没有菜单选项!                               

    • 阿树工作室
      阿树工作室 回复qinnek: 回复

      没有菜单选项说明主题不支持自定义菜单。

发表评论