WordPress Megamenü mit zusätzlichen Inhalten
Heute baue ich ein Megamenü in WordPress ohne Plugin.
Ziele
- Normale, im Backend erstellte Menüs sollen verwendet werden können.
- Einstellmöglichkeit pro Menüeintrag auf Ebene 0 für sein Submenü.
- Zusätzliche individuelle Inhalte pro Submenü – Befüllung im Backend möglich.
- Ausgabe in Widget „Individuelle Navigation“ möglich – Volle Systemunterstützung
Datenerfassung und Strukturen im Backend
Menü erstellen
Zuerst erstellen wir ein neues Menü in der WordPress Verwaltungsoberfläche. Das könnte man auch über die functions.php des Templates lösen, aber nachdem der Name des Menüs einen Inhalt darstellt und es sich anschließend als Widget ausgeben lässt, ist es mir lieber es über das Interface einzugeben.

Dynamische Sidebar aus den Menüeinträgen generieren
Dazu initialisieren wir in der functions.php unseres Themes für jeden Level 0 Menüeintrag, der die Kriterien erfüllt, eine neue Sidebar. Sie wird nach der ID des Menüeintrags benannt. So können wir sie später auf der Widgets Seite gut wiederfinden.
Die Idee zu dieser Form der Architektur stammt von Domagoj Gojak. Jedoch ist sie so angepasst, dass statt der „theme_location“ der Menü Name abgefragt wird und Level 0 als zusätzliches Kriterium, damit nicht alle Menüeinträge abgefragt werden müssen, was Ausführungszeit spart.
Natürlich kann ich jeden Klassennamen verwenden – er muß im Menüinterface mit dem in der functions.php verwendeten übereinstimmen. Weiters kann man im eigenen Template den Klassennamen auch über die Theme-Settings einstellbar machen, das ist aber nicht Gegenstand dieses Artikels.
Kriterien
- Ist in der obersten Menüebene – Level 0
- Hat den HTML-Klassennamen „mega-dropdown“
Code
<?php
//...
// init sidebars
function yourTheme_sidebars_init() {
if ( !function_exists('register_sidebar') ) {
return;
}
// ...
// .. other sidebars may be initialized here
/**
* Mega menu with widgets initialisation
* --------------------
* a menu named "main" has been created in wordpress backend
* select the name at creation form and fill in here
* select a class name that activates the mega sub-menu for specific menu items
* Reference for this idea: https://slicejack.com/create-fully-custom-wordpress-mega-menu-no-plugins-attached/
*/
$menu_name = 'main';
$selected_class = 'mega-dropdown';
$menu_main = wp_get_nav_menu_object($menu_name); // get the desired menu
// wp_get_nav_menu_object() returns false if no result
if ( $menu_main ) {
// grab all menu items at once
$menu_items = wp_get_nav_menu_items($menu_main->name);
// wp_get_nav_menu_items() returns false if no result
if ( $menu_items ) {
// loop trough all menu items
foreach ( $menu_items as $menu_item ) {
// Skip if not level 0
// adjust this if a 3rd level sub-menu can also be a mega menu
if ( $menu_item->menu_item_parent === '0' ) {
// only select menu items that has set a specific class name
if ( in_array($selected_class, $menu_item->classes) ) {
// add a sidebar to the wigdets admin page with the menu-item data
// customize "before_widget" and "after_widget" to your needs
register_sidebar(array(
'id' => 'menu-' . $menu_item->ID,
'name' => __('Mega Menu', 'yourTheme') . ' - ' . esc_html(wp_strip_all_tags($menu_item->title)),
'before_widget' => '<div id="%1$s" class="widget %2$s"><div class="inner">',
'after_widget' => '</div></div>',
'before_title' => '<h3 class="title">',
'after_title' => '</h3>',
));
}
}
}
}
}
}
// init sidebars
add_action( 'init', 'yourTheme_sidebars_init' );
CSS Klassen setzen
Damit die ausgewählten Menüpunkte alle Kriterien erfüllen, muß jeweils der Klassennamen eingetragen werden. Die Klassennamen werden voreingestellt nicht angezeigt – daher aktiviere ich deren Anzeige.

Ausgabe im Template
Anstatt ein spezifisches Template zu schreiben, filtern wir die Ausgabe des gewählten Menüs – die Filter kann man natürlich anpassen. Hier wird für das Menü mit dem Namen „main“ eine angepasste Walker Klasse ergänzt.
Da das Menü im Interface erzeugt wurde, hat es als Argument „menu“ beim Erstellen ein vollständigen „WP_Term“ erhalten. Den prüfen wir zuerst ab, damit keine Fehlermeldung bei „WP_Term->slug“ entstehen kann, mit dem der Menü Namen abgefragt werden kann.
Walker Aufruf mit Filter ergänzen
/**
* identify menu by name and add custom walker class if applicaple
* =====================
* $args['menu'] is possibly a int|string|WP_Term
* and filter 'wp_nav_menu_args' is called before it turns into WP_Term
*
* @param array $args Array of nav menu arguments.
*
* @see by menu name https://stackoverflow.com/questions/23375326#23375394
* @see nav menu args https://developer.wordpress.org/reference/functions/wp_nav_menu/#parameters
* @see is_a() http://php.net/manual/en/function.is-a.php
* @see background https://wordpress.stackexchange.com/questions/255244
*/
function yourTheme_nav_menu_args($args){
$menu_name = 'main';
// is $args['menu'] a WP_Term and is the menu name desired
if ( is_a($args['menu'], 'WP_Term') && $args['menu']->slug == $menu_name ) {
// Add custom walker class
$args['walker'] = new Main_menu_nav_walker();
}
return $args;
}
// output mega menu in nav_menu
add_filter( 'wp_nav_menu_args', 'yourTheme_nav_menu_args');
Walker Klasse um die Ausgabe der Sidebars im Menü hinzuzufügen
Die Integration der Ausgabe der Sidebars funktioniert über die Walker Methoden „start_lvl“ und „end_lvl“ – hier kann beliebig HTML Markup vor und nach dem Submenü eingetragen werden.
Wir prüfen wieder die vorher definierten Kriterien – dazu muß der aktuelle Level 0 Eintrag in allen Methoden abrufbar sein, der in der Methode „start_el“ als „$item“ erhältlich ist. Er wird in einer neuen privaten Variable „$current_lvl0_item“ zwischengespeichert. Alle Methoden habe ich zuerst aus der Originalen Walker_Nav_Menu Klasse kopiert und da. wo es notwendig war, angepasst.
Da der Aufruf der Sidebar immer mittels „echo“ ausgegeben wird, sammeln wir die Ausgabe mit dem PHP Output Buffer und speichern diesen in die „$output“ Variable.
/**
* Custom walker class
* ================
* Integrate the output of the Markup and the sidebars into the desired menu
* we need to save the current level 0 menu item from start_el() arguments and provide it to the other methods
* use output buffer for the sidebar because widgets always "echo" their contents
*
* @see build custom walker https://wordpress.stackexchange.com/questions/14037
* @see add container to sub menu https://wordpress.stackexchange.com/questions/78121
* @see class Walker_Nav_Menu https://developer.wordpress.org/reference/classes/walker_nav_menu/
* @see save item to other methods https://wordpress.stackexchange.com/questions/62054
* @see sidebars echo contents https://wordpress.stackexchange.com/questions/104177
*/
class Main_menu_nav_walker extends Walker_Nav_Menu{
private $current_lvl0_item; // saves current level 0 menu item
private $selected_class = 'mega-dropdown'; // class name for item selection
private $dropdown_class = 'dropdown'; // class name for html output
function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
// execute start_el as is
parent::start_el($output,$item,$depth,$args,$id);
// check for level 0
if ( $depth == 0 ) {
// save item
$this->current_lvl0_item = $item;
}
}
// first copied from default Walker_Nav_Menu and then edited
// original part starts here
function start_lvl( &$output, $depth = 0, $args = array() ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "\t";
$n = "\n";
}
$indent = str_repeat( $t, $depth );
// Default class.
$classes = array( 'sub-menu' );
/**
* Filters the CSS class(es) applied to a menu list element.
*
* @since 4.8.0
*
* @param array $classes The CSS classes that are applied to the menu `<ul>` element.
* @param stdClass $args An object of `wp_nav_menu()` arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$class_names = join( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
// original part ends here
// splitted from original
$output .= $n . $indent;
// only for the selected menu items at level 0
if ( $depth == 0 && in_array($this->selected_class, $this->current_lvl0_item->classes) ) {
// wrapper div for the sidebar and the sub menu
$output .= '<div class="' . $this->dropdown_class . '">';
// output the sidebar only if it has widgets
if ( is_active_sidebar('menu-' . $this->current_lvl0_item->ID) ) {
// sidebar wrapper div
$output .= '<div class="sidebar sidebar-menu-' . $this->current_lvl0_item->ID . '">';
// save save the contents of dynamic_sidebar() to output buffer
ob_start();
dynamic_sidebar('menu-' . $this->current_lvl0_item->ID);
$output .= ob_get_contents();
ob_end_clean();
// end sidebar wrapper div
$output .= '</div>';
}
}
// splitted from original
$output .= '<ul ' . $class_names . '>';
}
// first copied from default Walker_Nav_Menu and then edited
// original part starts here
function end_lvl( &$output, $depth = 0, $args = array() ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "\t";
$n = "\n";
}
$indent = str_repeat( $t, $depth );
$output .= "$indent</ul>{$n}";
// original part ends here
// only for the selected menu items at level 0
if ( $depth == 0 && in_array($this->selected_class, $this->current_lvl0_item->classes) ) {
// end wrapper div for the sidebar and the sub menu
$output .= '</div>';
}
}
}
Widgets einfügen
Damit Widgets angezeigt werden können, müssen auch welche eingefügt werden – das sollte jetzt so aussehen

Dadurch werden die Widgets im Quellcode vor dem Submenü ausgegeben
Ausgabe Quellcode
<li id="menu-item-20" class="mega-dropdown menu-item ...">
<a href="...">Home</a>
<div class="dropdown">
<div class="sidebar sidebar-menu-20">
<div id="text-2" class="widget widget_text">
<div class="inner">
<h3 class="title">Hey this is a cool dropdown</h3>
<div class="textwidget">
<p>I can write some more text here</p>
</div>
</div>
</div>
<div id="responsive_lightbox_image_widget-2" class="widget rl-image-widget">
<div class="inner">
<h3 class="title">A teaser in the menu saves the click</h3>
<img class="rl-image-widget-image" src=".../myimage.jpg" width="100%" height="auto" title="My Image Title" alt="" style="float: left;" />
<div class="rl-image-widget-text"></div>
</div>
</div>
</div>
<ul class="sub-menu">
<li id="menu-item-22" class="menu-item ...">
<a href="...">Hallo Welt!</a>
</li>
<li id="menu-item-23" class="menu-item ...">
<a href="...">Allgemein</a>
</li>
</ul>
</div>
</li>
Weitere Schritte
Jetzt kann es los gehen mit Styling und Javascript zum Öffnen/Schließen der neuen Mega-Dropdowns.
Discussion (No comments)