现在自适应网页 (即常说的响应式设计,一个网页在 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/

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

  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 ></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" "$depth );   
  24.         $output .= "
    $indent<ul role="menu" class=" dropdown-menu">
    "
    ;   
  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" "$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. }