我正在使用包含大型菜单的预制主题。我注意到,当您将鼠标悬停在超级菜单的父类别上时,它会显示菜单中第一个子类别的帖子。当我希望显示父类别中的帖子时。
例如:
如果我的菜单是
-
家庭花园食品的生活
-
然后在花园里
即使我在其他两个类别中有较新的帖子,我也将鼠标悬停在花园上,并且大型菜单将显示“鲜花”类别中的最新帖子。我该如何做,以便将鼠标悬停在花园上时,它将显示父类别花园中的最新帖子?
我的mega-menu.php
<?php
/**
* Mega Menu.
*
* @package Authentic WordPress Theme
* @subpackage Framework
* @since Authentic 3.2.0
* @version 1.0.0
*/
/**
* Mega Menu Class
*/
class CSCO_Mega_Menu {
/**
* Holds our custom fields
*
* @var array
* @access protected
*/
protected static $fields = array();
/**
* Menu Locations
*
* @var array
* @access private
*/
private $locations = array();
/**
* Constructor. Set up cacheable values and settings.
*/
public function __construct() {
// Set locations.
$this->locations = apply_filters( 'csco_mega_menu_locations', array( 'primary-menu' ) );
// Support secondary languages.
$this->locations = $this->_support_languages( (array) $this->locations );
// Enqueue Scripts.
add_action( 'wp_enqueue_scripts', array( $this, 'mega_menu_scripts' ) );
add_action( 'admin_footer', array( $this, 'footer_inline_scripts' ) );
// Admin Hooks.
add_action( 'wp_nav_menu_item_custom_fields', array( $this, '_fields' ), 10, 4 );
add_action( 'wp_update_nav_menu_item', array( $this, '_save' ), 10, 3 );
add_filter( 'wp_edit_nav_menu_walker', array( $this, '_admin_menu_walker' ), 10, 2 );
add_filter( 'manage_nav-menus_columns', array( $this, '_columns' ), 99, 1 );
// Frontend Hooks.
add_filter( 'nav_menu_css_class', array( $this, 'mega_menu_classes' ), 10, 4 );
add_filter( 'nav_menu_link_attributes', array( $this, 'mega_menu_atts' ), 10, 4 );
add_filter( 'walker_nav_menu_start_el', array( $this, 'mega_menu_extra_html' ), 10, 4 );
add_filter( 'wp_nav_menu_objects', array( $this, 'change_mega_menu_children' ), 10, 2 );
// Rest Api.
add_action( 'rest_api_init', array( $this, 'menu_posts_rest_route' ) );
// Ajax.
add_action( 'wp_ajax_csco_reload_menu', array( $this, 'reload_admin_nav_menu' ) );
add_action( 'wp_ajax_nopriv_csco_reload_menu', array( $this, 'reload_admin_nav_menu' ) );
// Custom Fields.
self::$fields = array(
'csco-mega-menu' => array(
'type' => 'checkbox',
'label' => esc_attr( 'Mega Menu', 'authentic' ),
),
);
// Fields Filter.
self::$fields = apply_filters( 'csco_mega_menu_fields', self::$fields );
}
/**
* Add support languages.
*
* @param array $locations List locations.
*/
public function _support_languages( $locations ) {
foreach ( $locations as $location ) {
// Polylang.
if ( function_exists( 'pll_languages_list' ) ) {
$languages = pll_languages_list();
if ( $languages ) {
foreach ( $languages as $language ) {
$locations[] = sprintf( '%s___%s', $location, $language );
}
}
}
}
return $locations;
}
/**
* Custom Menu Fields
*
* @since 1.0.0
*
* @param int $item_id Menu item ID.
* @param object $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param array $args Menu item args.
*/
public function _fields( $item_id, $item, $depth, $args ) {
foreach ( self::$fields as $_key => $field ) {
$key = sprintf( 'menu-item-%s', $_key );
$id = sprintf( 'edit-%s-%s', $key, $item_id );
$name = sprintf( '%s[%s]', $key, $item_id );
$value = get_post_meta( $item_id, $key, true );
$class = sprintf( 'field-%s', $_key );
switch ( $field['type'] ) {
case 'checkbox':
?>
<p class="<?php echo esc_attr( $class ); ?> description description-wide">
<?php
printf(
'<label for="%1$s"><input type="checkbox" id="%1$s" class="%1$s" name="%3$s" value="1" %4$s/>%2$s</label>',
esc_attr( $id ),
esc_html( $field['label'] ),
esc_attr( $name ),
checked( $value, 1, false )
);
?>
</p>
<?php
break;
case 'input':
?>
<p class="<?php echo esc_attr( $class ); ?> description description-wide">
<?php
printf(
'<label for="%1$s">%2$s<br /><input type="text" id="%1$s" class="widefat %1$s" name="%3$s" value="%4$s" /></label>',
esc_attr( $id ),
esc_html( $field['label'] ),
esc_attr( $name ),
esc_attr( $value )
);
?>
</p>
<?php
break;
}
} // End foreach().
}
/**
* Save custom field value
*
* @since 1.0.0
* @wp_hook action wp_update_nav_menu_item
*
* @param int $menu_id Nav menu ID.
* @param int $menu_item_db_id Menu item ID.
* @param array $menu_item_args Menu item data.
*/
public function _save( $menu_id, $menu_item_db_id, $menu_item_args ) {
// Check ajax.
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
// Security.
check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
// Mega Menu Fields.
foreach ( self::$fields as $_key => $field ) {
$key = sprintf( 'menu-item-%s', $_key );
// Sanitize field.
if ( isset( $_POST[ $key ][ $menu_item_db_id ] ) ) { // Input Var ok.
$value = sanitize_text_field( wp_unslash( $_POST[ $key ][ $menu_item_db_id ] ) ); // Input Var ok.
} else {
$value = null;
}
// Update field.
if ( ! is_null( $value ) ) {
update_post_meta( $menu_item_db_id, $key, $value );
} else {
delete_post_meta( $menu_item_db_id, $key );
}
}
}
/**
* Add fields to the screen options toggle
*
* @since 1.0.0
* @wp_hook filter manage_nav-menus_columns
*
* @param array $columns Menu item columns.
* @return array
*/
public static function _columns( $columns ) {
$column_fields = array();
foreach ( self::$fields as $_key => $field ) {
$column_fields[ $_key ] = $field['label'];
}
$columns = array_merge( $columns, $column_fields );
return $columns;
}
/**
* Set Mega Menu Admin Walker
*
* @since 1.0.0
* @wp_hook filter wp_edit_nav_menu_walker
*
* @param string $class The walker class to use. Default 'Walker_Nav_Menu_Edit'.
* @param int $menu_id ID of the menu being rendered.
* @return string Walker class name.
*/
public function _admin_menu_walker( $class, $menu_id ) {
// Get Theme Locations.
$theme_locations = get_nav_menu_locations();
// Check Mega Menu.
$has_mega_menu = false;
foreach ( $theme_locations as $location => $location_menu ) {
if ( $location_menu === $menu_id && in_array( $location, $this->locations, true ) ) {
$has_mega_menu = true;
}
}
// Set New Walker.
if ( $has_mega_menu ) {
$class = 'CSCO_Menu_Item_Walker';
if ( ! class_exists( $class ) ) {
csco_load_admin_menu_walker();
}
}
return $class;
}
/**
* Refresh menu item fields
*
* @since 1.0.0
*/
public function reload_admin_nav_menu() {
$nav_menu_selected_id = isset( $_REQUEST['menu_id'] ) ? (int) $_REQUEST['menu_id'] : 0; // Input var okay.
$menu_name = isset( $_REQUEST['menu_name'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['menu_name'] ) ) : false; // Input var okay.
$menu_checked = isset( $_REQUEST['menu_checked'] ) ? (bool) $_REQUEST['menu_checked'] : false; // Input var okay.
// Get Menu Location.
preg_match( '/^menu-locations\[(.*?)\]/', $menu_name, $matches );
$menu_location = isset( $matches[1] ) ? $matches[1] : false;
if ( is_nav_menu( $nav_menu_selected_id ) && $menu_location ) {
// Load WP Nav Menu.
require_once( ABSPATH . 'wp-admin/includes/nav-menu.php' );
// Get All Locations.
$menu_locations = get_nav_menu_locations();
// Set|Unset Menu to Locations List.
if ( $menu_checked ) {
$menu_locations[ $menu_location ] = $nav_menu_selected_id;
} else {
if ( isset( $menu_locations[ $menu_location ] ) ) {
unset( $menu_locations[ $menu_location ] );
}
}
// Set Edited Menu Locations.
set_theme_mod( 'nav_menu_locations', $menu_locations );
// Get Nav Menu HTML.
$edit_markup = wp_get_nav_menu_to_edit( $nav_menu_selected_id );
if ( ! is_wp_error( $edit_markup ) ) {
wp_die( $edit_markup );
}
}
}
/**
* Is Item is Mega Menu?
*
* @since 1.0.0
*
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @return string $menu_type Type of mega menu item or false.
*/
public function is_mega_menu( $item, $args = null ) {
// Check Mega Menu Location.
if ( isset( $args->theme_location ) ) {
if ( ! $args->theme_location || ! in_array( $args->theme_location, $this->locations, true ) ) {
return false;
}
}
// Classes.
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
// Mega Menu.
$menu_meta_key = 'menu-item-csco-mega-menu';
$mega_menu_class = 'csco-mega-menu';
// Is Item Mega Menu?
if ( in_array( $mega_menu_class, $classes, true ) ) {
$is_mega_menu = true;
} else {
$is_mega_menu = get_post_meta( $item->ID, $menu_meta_key, true );
}
// Mega Menu Type.
$menu_type = false;
if ( $is_mega_menu && in_array( 'menu-item-has-children', $classes, true ) ) {
$menu_type = 'tabs';
} elseif ( $is_mega_menu && 'category' === $item->object ) {
$menu_type = 'category';
} elseif ( isset( $item->mega_menu_child ) ) {
switch ( $item->mega_menu_child ) {
case 'category':
$menu_type = 'child-category';
break;
default:
$menu_type = 'child-item';
break;
}
}
// Result.
return $menu_type;
}
/**
* Filter the sorted list of menu item objects before generating the menu's HTML.
*
* @since 1.0.0
*
* @param array $sorted_menu_items The menu items, sorted by each menu item's menu order.
* @param stdClass $args An object containing wp_nav_menu() arguments.
*/
function change_mega_menu_children( $sorted_menu_items, $args ) {
foreach ( $sorted_menu_items as $key => $item ) {
// Get Mega Menu Type.
$menu_type = $this->is_mega_menu( $item, $args );
// Create Mega menu Children.
if ( 'tabs' === $menu_type && 0 === (int) $item->menu_item_parent ) {
// Children Var.
$item->mega_menu_children = array();
// Add Items to Children Args.
foreach ( $sorted_menu_items as $_key => $_item ) {
if ( $item->ID === (int) $_item->menu_item_parent ) {
// Set Mega Menu Type.
$_item->mega_menu_child = $_item->object;
// Set as child.
$item->mega_menu_children[] = $_item;
unset( $sorted_menu_items[ $_key ] );
}
}
}
}
return $sorted_menu_items;
}
/**
* Mega Menu Classes.
*
* @since 1.0.0
*
* @param array $classes The CSS classes that are applied to the menu item's `<li>` element.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
public function mega_menu_classes( $classes, $item, $args, $depth ) {
// Get Mega Menu Type.
$menu_type = $this->is_mega_menu( $item, $args );
// Add Mega Menu Classes.
if ( $menu_type ) {
if ( 0 === $depth ) {
$classes[] = 'csco-mega-menu';
}
$classes[] = 'csco-mega-menu-' . $menu_type;
// Menu Item Arrow fix.
if ( 'category' === $menu_type ) {
$classes[] = 'menu-item-has-children';
}
// Child Class.
if ( in_array( $menu_type, array( 'child-category', 'child-item' ), true ) ) {
$classes[] = 'csco-mega-menu-child';
}
}
return $classes;
}
/**
* Mega Menu Attributes
*
* @since 1.0.0
*
* @param array $atts The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
public function mega_menu_atts( $atts, $item, $args, $depth ) {
// Get Mega Menu Type.
$menu_type = $this->is_mega_menu( $item, $args );
// Mega Menu attrs.
if ( in_array( $menu_type, array( 'category', 'child-category' ), true ) ) {
$atts['data-cat'] = $item->object_id;
if ( 'category' === $menu_type ) {
$atts['data-numberposts'] = 5;
} elseif ( 'child-category' === $menu_type ) {
$atts['data-numberposts'] = 4;
}
}
return $atts;
}
/**
* Mega Menu Additional Content.
*
* @param string $item_output The menu item's starting HTML output.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
public function mega_menu_extra_html( $item_output, $item, $depth, $args ) {
// Get Mega Menu Type.
$menu_type = $this->is_mega_menu( $item, $args );
// Mega Menu Content.
ob_start();
switch ( $menu_type ) {
case 'tabs':
if ( isset( $item->mega_menu_children ) && ! empty( $item->mega_menu_children ) ) {
?>
<div class="sub-menu">
<div class="tabs-content">
<ul class="sub-cats">
<?php
foreach ( $item->mega_menu_children as $_item ) {
$classes = empty( $_item->classes ) ? array() : (array) $_item->classes;
$classes[] = 'menu-item-' . $_item->ID;
// Filters the CSS class(es) applied to a menu item's list item element.
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $_item, $args, $depth + 1 ) );
$atts = array();
$atts['title'] = ! empty( $_item->attr_title ) ? $_item->attr_title : '';
$atts['target'] = ! empty( $_item->target ) ? $_item->target : '';
$atts['rel'] = ! empty( $_item->xfn ) ? $_item->xfn : '';
$atts['href'] = ! empty( $_item->url ) ? $_item->url : '';
// Filters the HTML attributes applied to a menu item's anchor element.
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $_item, $args, $depth + 1 );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
// Link HTML.
$link = '<a' . $attributes . '>' . apply_filters( 'the_title', $_item->title, $_item->ID ) . '</a>';
$allowed_html = array(
'a' => array(
'href' => true,
'title' => true,
'target' => true,
'rel' => true,
'href' => true,
'data-cat' => true,
'data-numberposts' => true,
),
);
// Output Item.
?>
<li class="<?php echo esc_attr( $class_names ); ?>">
<?php echo wp_kses( $link, $allowed_html ); ?>
</li>
<?php
}
?>
</ul>
<div class="csco-mega-menu-box">
<?php
foreach ( $item->mega_menu_children as $_item ) {
if ( 'category' === $_item->object ) {
?>
<div class="csco-mega-menu-content" data-cat="<?php echo esc_attr( $_item->object_id ); ?>"></div>
<?php
}
}
?>
</div>
</div>
</div>
<?php
}
break;
case 'category':
if ( 'category' === $item->object ) {
?>
<div class="sub-menu">
<div class="csco-mega-menu-content mega-menu-category"></div>
</div>
<?php
}
break;
} // End switch().
// Set Mega Menu Content.
$item_output .= ob_get_clean();
return $item_output;
}
/**
* Get Mega Menu Posts
*
* @param array $request REST API Request.
*/
public function load_menu_posts_restapi( $request ) {
// Default Data.
$data = array(
'status' => 'error',
'content' => '',
);
// Category ID.
$category_id = isset( $_GET['cat'] ) ? (int) $_GET['cat'] : 0; // Input var ok.
if ( $category_id <= 0 ) {
return $data;
}
// Number of posts.
$per_page = isset( $_GET['per_page'] ) ? (int) $_GET['per_page'] : 5; // Input var ok.
// Get Category Posts.
$category_posts = get_posts(
array(
'category' => $category_id,
'posts_per_page' => $per_page,
)
);
ob_start();
if ( ! empty( $category_posts ) ) {
global $post;
// Thumbnail Size Item.
$thumbnail = apply_filters( 'csco_mega_menu_thumbnail_size', 'csco-320-landscape' );
// Each Posts.
foreach ( $category_posts as $post ) {
setup_postdata( $post );
?>
<article <?php post_class( 'mega-menu-item menu-post-item' ); ?>>
<?php if ( has_post_thumbnail() ) { ?>
<div class="post-inner">
<div class="post-thumbnail">
<?php the_post_thumbnail( $thumbnail ); ?>
<?php csco_the_read_more( 'btn-link', null ); ?>
<a href="<?php the_permalink(); ?>"></a>
</div>
</div>
<?php } ?>
<header class="entry-header">
<h6 class="entry-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h6>
<?php csco_get_post_meta( array( 'reading_time', 'views' ), true, true ); ?>
</header>
</article>
<?php
}
wp_reset_postdata();
}
$data = array(
'status' => 'success',
'content' => ob_get_clean(),
);
// Return Result.
return rest_ensure_response( $data );
}
/**
* Register REST MEga Menu Posts Routes
*/
public function menu_posts_rest_route() {
register_rest_route(
'csco/v1', '/menu-posts', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'load_menu_posts_restapi' ),
)
);
}
/**
* Localize mega menu scripts.
*/
public function mega_menu_scripts() {
// Localize Script.
$args = array(
'rest_url' => esc_url( get_rest_url( null, '/csco/v1/menu-posts' ) ),
);
wp_localize_script( 'jquery', 'csco_mega_menu', $args );
}
/**
* Output scripts & styles on the Admin Nav Menu Page
*/
public function footer_inline_scripts() {
global $pagenow;
if ( 'nav-menus.php' === $pagenow ) {
?>
<script>
(function($) {
/* On Document Ready */
$( document ).ready( function() {
/*
* Ajax Menu Reload
*/
$( '.menu-theme-locations input[type="checkbox"]' ).on( 'change', function( event ) {
event.preventDefault();
var cscoMenuID = parseInt( $( this ).val() ),
cscoMenuName = $( this ).attr( 'name' ),
cscoMenuChecked = $( this ).attr( 'checked' );
if( cscoMenuID > 0 ) {
$.ajax({
type: 'POST',
url: ajaxurl,
data: { 'action': 'csco_reload_menu', 'menu_id': cscoMenuID, 'menu_name': cscoMenuName, 'menu_checked': cscoMenuChecked },
beforeSend: function(){
$( '#update-nav-menu' ).addClass( 'menu-ajax-reloading' );
},
success: function( result ) {
if ( result.length > 0 && result != 0 ) {
var resultHtml = $.parseHTML( result );
if ( resultHtml ) {
$.each( resultHtml, function( i, el ) {
if ( $( el ).attr( 'id' ) == 'menu-to-edit' ) {
$( '#menu-to-edit' ).html( $( el ).html() );
}
});
// Refresh Item Buttons
$( '#menu-to-edit .menu-item' ).hideAdvancedMenuItemFields();
if ( typeof wpNavMenu !== 'undefined' ) {
wpNavMenu.refreshKeyboardAccessibility();
wpNavMenu.refreshAdvancedAccessibility();
}
}
}
$( '#update-nav-menu' ).removeClass( 'menu-ajax-reloading' );
},
error: function(e){
$( '#update-nav-menu' ).removeClass( 'menu-ajax-reloading' );
}
});
}
});
});
})(jQuery);
</script>
<style type="text/css">
#update-nav-menu.menu-ajax-reloading {
position: relative;
z-index: 12;
}
#update-nav-menu.menu-ajax-reloading:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba( 255, 255, 255, 0.6 );
z-index: 15;
}
#menu-to-edit li.menu-item:not( .menu-item-depth-0 ) .field-csco-mega-menu {
display: none;
}
</style>
<?php
} // End if().
}
}
/**
* Init Mega Menu
*
* @since 1.0.0
*/
function csco_mega_menu_init() {
new CSCO_Mega_Menu();
}
add_action( 'init', 'csco_mega_menu_init' );
/**
* Mega Menu Admin Walker.
*
* @since 1.0.0
*
* @see Walker_Nav_Menu_Edit
*/
function csco_load_admin_menu_walker() {
/**
* Custom Walker for Nav Menu Editor
*/
class CSCO_Menu_Item_Walker extends Walker_Nav_Menu_Edit {
/**
* Start the element output.
*
* We're injecting our custom fields after the div.submitbox
*
* @see Walker_Nav_Menu::start_el()
* @since 0.1.0
* @since 0.2.0 Update regex pattern to support WordPress 4.7's markup.
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param array $args Menu item args.
* @param int $id Nav menu ID.
*/
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$item_output = '';
parent::start_el( $item_output, $item, $depth, $args, $id );
$output .= preg_replace(
// NOTE: Check this regex from time to time!
'/(?=<(fieldset|p)[^>]+class="[^"]*field-move)/',
$this->get_fields( $item, $depth, $args ),
$item_output
);
}
/**
* Get custom fields
*
* @access protected
* @since 0.1.0
* @uses add_action() Calls 'menu_item_custom_fields' hook
*
* @param object $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param array $args Menu item args.
* @param int $id Nav menu ID.
*
* @return string Form fields
*/
protected function get_fields( $item, $depth, $args = array(), $id = 0 ) {
ob_start();
/**
* Get menu item custom fields
*
* @since 0.1.0
* @since 1.0.0 Pass correct parameters.
*
* @param int $item_id Menu item ID.
* @param object $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param array $args Menu item args.
*
* @return string Custom fields HTML.
*/
do_action( 'wp_nav_menu_item_custom_fields', $item->ID, $item, $depth, $args );
return ob_get_clean();
}
}
}