星期三 , 22 1 月 2025

wp给菜单添加字段并控制二级菜单的输出方式wp nav menu class 教程 mega menu 教程

菜单也是wp_post的对象,首先给菜单添加字段:

添加字段:

add_action('wp_nav_menu_item_custom_fields', 'add_mega_menu_type_field', 10, 4);
function add_mega_menu_type_field($item_id, $item, $depth, $args) {
    $mega_menu_type = get_post_meta($item_id, '_mega_menu_type', true);
    ?>
    <p class="field-mega-menu-type description description-wide">
        <label for="edit-menu-item-mega-menu-type-<?php echo $item_id; ?>">
            <?php _e('Mega Menu Type'); ?><br />
            <select id="edit-menu-item-mega-menu-type-<?php echo $item_id; ?>"
                    name="menu-item-mega-menu-type[<?php echo $item_id; ?>]">
                <option value="" <?php selected($mega_menu_type, ''); ?>><?php _e('None'); ?></option>
                <option value="product" <?php selected($mega_menu_type, 'product'); ?>><?php _e('Product'); ?></option>
                <option value="category" <?php selected($mega_menu_type, 'category'); ?>><?php _e('Category'); ?></option>
                <option value="post" <?php selected($mega_menu_type, 'post'); ?>><?php _e('Post'); ?></option>
            </select>
        </label>
    </p>
    <?php
}

保存字段:

/**
 * Step 2: Save Custom Menu Field
 */
add_action('wp_update_nav_menu_item', 'save_mega_menu_type_field', 10, 3);
function save_mega_menu_type_field($menu_id, $menu_item_db_id, $menu_item_args) {
    if (isset($_POST['menu-item-mega-menu-type'][$menu_item_db_id])) {
        $mega_menu_type = sanitize_text_field($_POST['menu-item-mega-menu-type'][$menu_item_db_id]);
        update_post_meta($menu_item_db_id, '_mega_menu_type', $mega_menu_type);
    }
}

用字段控制输出

首先要在class里面定义属性:

protected $current_item = null;
    protected $current_item_ancestors = array();

下面这个函数很重要,在class里面追踪字段: 没有就无法给class的属性赋于字段值

 /**
     * Track the current item before displaying it
     */
    public function display_element($element, &$children_elements, $max_depth, $depth, $args, &$output) {
        // Store current item
        $this->current_item = $element;
        
        // Store ancestors for this item
        $this->current_item_ancestors = array();
        if (isset($element->menu_item_parent) && $element->menu_item_parent > 0) {
            $ancestors = array();
            $ancestor_id = $element->menu_item_parent;
            
            while ($ancestor_id > 0) {
                foreach ($children_elements as $children) {
                    foreach ($children as $child) {
                        if ($child->ID == $ancestor_id) {
                            $ancestors[] = $child;
                            $ancestor_id = $child->menu_item_parent;
                            continue 2;
                        }
                    }
                    $ancestor_id = 0;
                }
            }
            $this->current_item_ancestors = array_reverse($ancestors);
        }
        
        parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
    }

用字段来控制二级ul的输出方式:

$mega_menu_type = '';
        
        // Get mega menu type from parent item
        if (!empty($this->current_item_ancestors)) {
            $parent_item = end($this->current_item_ancestors);
            $mega_menu_type = get_post_meta($parent_item->ID, '_mega_menu_type', true);
        } elseif ($this->current_item) {
            $mega_menu_type = get_post_meta($this->current_item->ID, '_mega_menu_type', true);
        }
        
        if ($depth === 0) {
            switch ($mega_menu_type) {
                case 'product':
                    $output .= '<div class="mega-menu mega-menu-products-container">';
                    break;
                    
                case 'category':
                    $output .= '<div class="mega-menu mega-menu-categories-container">';
                    break;
                    
                case 'post':
                    $output .= '<div class="mega-menu mega-menu-posts-container">';
                    break;
                    
                default:
                    $output .= '<ul class="sub-menu">';
                    break;
            }
        } else {
            // For nested levels
            switch ($mega_menu_type) {
                case 'product':
                    $output .= '<div class="mega-menu-product-submenu">';
                    break;
                    
                case 'category':
                    $output .= '<div class="mega-menu-category-submenu">';
                    break;
                    
                case 'post':
                    $output .= '<div class="mega-menu-post-submenu">';
                    break;
                    
                default:
                    $output .= '<ul class="sub-menu">';
                    break;
            }
        }

end_lv1 里面同样操作一遍:

 $mega_menu_type = '';
        
        // Get mega menu type from parent item
        if (!empty($this->current_item_ancestors)) {
            $parent_item = end($this->current_item_ancestors);
            $mega_menu_type = get_post_meta($parent_item->ID, '_mega_menu_type', true);
        } elseif ($this->current_item) {
            $mega_menu_type = get_post_meta($this->current_item->ID, '_mega_menu_type', true);
        }
        
        if ($mega_menu_type && ($mega_menu_type === 'product' || $mega_menu_type === 'category' || $mega_menu_type === 'post')) {
            $output .= '</div>';
        } else {
            $output .= '</ul>';
        }

start_el1 里面用 output变量重写内容:

 $mega_menu_type = get_post_meta($item->ID, '_mega_menu_type', true);
        $parent_mega_type = '';
        
        // Get parent mega menu type
        if ($depth > 0 && !empty($this->current_item_ancestors)) {
            $parent_item = end($this->current_item_ancestors);
            $parent_mega_type = get_post_meta($parent_item->ID, '_mega_menu_type', true);
        }
        
        // Classes
        $classes = empty($item->classes) ? array() : (array) $item->classes;
        
        if ($mega_menu_type && $depth === 0) {
            $classes[] = 'has-mega-menu';
            $classes[] = 'mega-menu-' . $mega_menu_type;
        }
        
        $class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item, $args, $depth));
        $class_names = $class_names ? ' class="' . esc_attr($class_names) . '"' : '';
        
        // Determine container element
        $container_element = 'li';
        if ($depth > 0 && $parent_mega_type) {
            switch ($parent_mega_type) {
                case 'product':
                    $container_element = 'div';
                    $class_names = ' class="mega-product-item"';
                    break;
                    
                case 'category':
                    $container_element = 'div';
                    $class_names = ' class="mega-category-item"';
                    break;
                    
                case 'post':
                    $container_element = 'div';
                    $class_names = ' class="mega-post-item"';
                    break;
            }
        }
        
        $output .= '<' . $container_element . $class_names . '>';
        
        // Build menu item
        $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 : '';
        
        $attributes = '';
        foreach ($atts as $attr => $value) {
            if (!empty($value)) {
                $attributes .= ' ' . $attr . '="' . esc_attr($value) . '"';
            }
        }
        
        $title = apply_filters('the_title', $item->title, $item->ID);
        
        $item_output = $args->before;
        
        // Generate content based on type
        if ($depth > 0 && $parent_mega_type) {
            switch ($parent_mega_type) {
                case 'product':
                    $item_output .= $this->get_product_item_content($item);
                    break;
                    
                case 'post':
                    $item_output .= $this->get_post_item_content($item);
                    break;
                    
                default:
                    $item_output .= '<a' . $attributes . '>';
                    $item_output .= $args->link_before . $title . $args->link_after;
                    $item_output .= '</a>';
            }
        } else {
            $item_output .= '<a' . $attributes . '>';
            $item_output .= $args->link_before . $title . $args->link_after;
            $item_output .= '</a>';
        }
        
        $item_output .= $args->after;
        
        $output .= apply_filters('walker_nav_menu_start_el', $item_output, $item, $depth, $args);
    }

用不同的函数来加载不同内容:

 /**
     * Helper method for product content
     */
    private function get_product_item_content($item) {
        $output = '<div class="mega-product-content">';
        if (has_post_thumbnail($item->object_id)) {
            $output .= get_the_post_thumbnail($item->object_id, 'thumbnail');
        }
        $output .= '<h4><a href="' . get_permalink($item->object_id) . '">' . $item->title . '</a></h4>';
        
        if (function_exists('get_woocommerce_price')) {
            $product = wc_get_product($item->object_id);
            if ($product) {
                $output .= '<span class="price">' . $product->get_price_html() . '</span>';
            }
        }
        
        $output .= '</div>';
        return $output;
    }

    /**
     * Helper method for post content
     */
    private function get_post_item_content($item) {
        $output = '<div class="mega-post-content">';
        if (has_post_thumbnail($item->object_id)) {
            $output .= get_the_post_thumbnail($item->object_id, 'thumbnail');
        }
        $output .= '<h4><a href="' . get_permalink($item->object_id) . '">' . $item->title . '</a></h4>';
        $output .= '<p>' . wp_trim_words(get_post_field('post_excerpt', $item->object_id), 10) . '</p>';
        $output .= '</div>';
        return $output;
    }

完整版

<?php
class Mega_Menu_Walker extends Walker_Nav_Menu {
// Add properties to track current item and its ancestors
protected $current_item = null;
protected $current_item_ancestors = array();

/**
* Track the current item before displaying it
*/
public function display_element($element, &$children_elements, $max_depth, $depth, $args, &$output) {
// Store current item
$this->current_item = $element;
// Store ancestors for this item
$this->current_item_ancestors = array();
if (isset($element->menu_item_parent) && $element->menu_item_parent > 0) {
$ancestors = array();
$ancestor_id = $element->menu_item_parent;
while ($ancestor_id > 0) {
foreach ($children_elements as $children) {
foreach ($children as $child) {
if ($child->ID == $ancestor_id) {
$ancestors[] = $child;
$ancestor_id = $child->menu_item_parent;
continue 2;
}
}
$ancestor_id = 0;
}
}
$this->current_item_ancestors = array_reverse($ancestors);
}
parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
}
/**
* Start level based on mega menu type
*/
public function start_lvl(&$output, $depth = 0, $args = null) {
$mega_menu_type = '';
// Get mega menu type from parent item
if (!empty($this->current_item_ancestors)) {
$parent_item = end($this->current_item_ancestors);
$mega_menu_type = get_post_meta($parent_item->ID, '_mega_menu_type', true);
} elseif ($this->current_item) {
$mega_menu_type = get_post_meta($this->current_item->ID, '_mega_menu_type', true);
}
if ($depth === 0) {
switch ($mega_menu_type) {
case 'product':
$output .= '<div class="mega-menu mega-menu-products-container">';
break;
case 'category':
$output .= '<div class="mega-menu mega-menu-categories-container">';
break;
case 'post':
$output .= '<div class="mega-menu mega-menu-posts-container">';
break;
default:
$output .= '<ul class="sub-menu">';
break;
}
} else {
// For nested levels
switch ($mega_menu_type) {
case 'product':
$output .= '<div class="mega-menu-product-submenu">';
break;
case 'category':
$output .= '<div class="mega-menu-category-submenu">';
break;
case 'post':
$output .= '<div class="mega-menu-post-submenu">';
break;
default:
$output .= '<ul class="sub-menu">';
break;
}
}
}
/**
* End level based on mega menu type
*/
public function end_lvl(&$output, $depth = 0, $args = null) {
$mega_menu_type = '';
// Get mega menu type from parent item
if (!empty($this->current_item_ancestors)) {
$parent_item = end($this->current_item_ancestors);
$mega_menu_type = get_post_meta($parent_item->ID, '_mega_menu_type', true);
} elseif ($this->current_item) {
$mega_menu_type = get_post_meta($this->current_item->ID, '_mega_menu_type', true);
}
if ($mega_menu_type && ($mega_menu_type === 'product' || $mega_menu_type === 'category' || $mega_menu_type === 'post')) {
$output .= '</div>';
} else {
$output .= '</ul>';
}
}
/**
* Start element with proper container
*/
public function start_el(&$output, $item, $depth = 0, $args = null, $id = 0) {
$mega_menu_type = get_post_meta($item->ID, '_mega_menu_type', true);
$parent_mega_type = '';
// Get parent mega menu type
if ($depth > 0 && !empty($this->current_item_ancestors)) {
$parent_item = end($this->current_item_ancestors);
$parent_mega_type = get_post_meta($parent_item->ID, '_mega_menu_type', true);
}
// Classes
$classes = empty($item->classes) ? array() : (array) $item->classes;
if ($mega_menu_type && $depth === 0) {
$classes[] = 'has-mega-menu';
$classes[] = 'mega-menu-' . $mega_menu_type;
}
$class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item, $args, $depth));
$class_names = $class_names ? ' class="' . esc_attr($class_names) . '"' : '';
// Determine container element
$container_element = 'li';
if ($depth > 0 && $parent_mega_type) {
switch ($parent_mega_type) {
case 'product':
$container_element = 'div';
$class_names = ' class="mega-product-item"';
break;
case 'category':
$container_element = 'div';
$class_names = ' class="mega-category-item"';
break;
case 'post':
$container_element = 'div';
$class_names = ' class="mega-post-item"';
break;
}
}
$output .= '<' . $container_element . $class_names . '>';
// Build menu item
$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 : '';
$attributes = '';
foreach ($atts as $attr => $value) {
if (!empty($value)) {
$attributes .= ' ' . $attr . '="' . esc_attr($value) . '"';
}
}
$title = apply_filters('the_title', $item->title, $item->ID);
$item_output = $args->before;
// Generate content based on type
if ($depth > 0 && $parent_mega_type) {
switch ($parent_mega_type) {
case 'product':
$item_output .= $this->get_product_item_content($item);
break;
case 'post':
$item_output .= $this->get_post_item_content($item);
break;
default:
$item_output .= '<a' . $attributes . '>';
$item_output .= $args->link_before . $title . $args->link_after;
$item_output .= '</a>';
}
} else {
$item_output .= '<a' . $attributes . '>';
$item_output .= $args->link_before . $title . $args->link_after;
$item_output .= '</a>';
}
$item_output .= $args->after;
$output .= apply_filters('walker_nav_menu_start_el', $item_output, $item, $depth, $args);
}
/**
* End element with proper container
*/
public function end_el(&$output, $item, $depth = 0, $args = null) {
$parent_mega_type = '';
if ($depth > 0 && !empty($this->current_item_ancestors)) {
$parent_item = end($this->current_item_ancestors);
$parent_mega_type = get_post_meta($parent_item->ID, '_mega_menu_type', true);
}
$container_element = 'li';
if ($depth > 0 && in_array($parent_mega_type, ['product', 'category', 'post'])) {
$container_element = 'div';
}
$output .= "</{$container_element}>";
}
/**
* Helper method for product content
*/
private function get_product_item_content($item) {
$output = '<div class="mega-product-content">';
if (has_post_thumbnail($item->object_id)) {
$output .= get_the_post_thumbnail($item->object_id, 'thumbnail');
}
$output .= '<h4><a href="' . get_permalink($item->object_id) . '">' . $item->title . '</a></h4>';
if (function_exists('get_woocommerce_price')) {
$product = wc_get_product($item->object_id);
if ($product) {
$output .= '<span class="price">' . $product->get_price_html() . '</span>';
}
}
$output .= '</div>';
return $output;
}
/**
* Helper method for post content
*/
private function get_post_item_content($item) {
$output = '<div class="mega-post-content">';
if (has_post_thumbnail($item->object_id)) {
$output .= get_the_post_thumbnail($item->object_id, 'thumbnail');
}
$output .= '<h4><a href="' . get_permalink($item->object_id) . '">' . $item->title . '</a></h4>';
$output .= '<p>' . wp_trim_words(get_post_field('post_excerpt', $item->object_id), 10) . '</p>';
$output .= '</div>';
return $output;
}

}

Check Also

Canva slider

https://www.can …

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注