Ich versuche ein Menü mit maximal 5 Elementen zu erstellen. Wenn es mehr Elemente gibt, sollten Sie sie in ein anderes
5 Artikel oder weniger:
6 Gegenstände oder mehr
Ich weiß, dass diese Art von functionalität leicht mit einem Walker erstellt werden könnte, der die Menüelemente zählt und Wraps, wenn es mehr als 5 gibt, in eine separate
Der Code, der mein Menü im Moment anzeigt, ist folgender:
'navigation', 'fallback_cb' => 'custom_menu', 'walker' =>new Custom_Walker_Nav_Menu ) ); ?>
Ich habe bemerkt, dass, wenn das Menü nicht vom Benutzer definiert wird und die Fallback-function verwendet, der Walker keine Wirkung hat. Ich brauche es in beiden Fällen.
Mit einem benutzerdefinierten Walker hat die Methode start_el()
Zugriff auf $depth
param: Wenn es 0
ist, ist das Element ein oberster und wir können diese Informationen verwenden, um einen internen Zähler zu verwalten.
Wenn der Zähler ein Limit erreicht, können wir DOMDocument
, um aus der vollständigen HTML-Ausgabe nur das letzte hinzugefügte Element zu erhalten, es in ein Untermenü umzubrechen und es erneut zu HTML hinzuzufügen.
Wenn die Anzahl der Elemente genau die Anzahl ist, die wir + 1 benötigen, zB wenn 5 Elemente sichtbar sein müssen und Menü 6 hat, macht es keinen Sinn, das Menü zu teilen, da die Elemente 6 oder so sein werden. Der Code wurde bearbeitet, um das zu beheben.
Hier ist der Code:
class SplitMenuWalker extends Walker_Nav_Menu { private $split_at; private $button; private $count = 0; private $wrappedOutput; private $replaceTarget; private $wrapped = false; private $toSplit = false; public function __construct($split_at = 5, $button = '…') { $this->split_at = $split_at; $this->button = $button; } public function walk($elements, $max_depth) { $args = array_slice(func_get_args(), 2); $output = parent::walk($elements, $max_depth, reset($args)); return $this->toSplit ? $output.'' : $output; } public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0 ) { $this->count += $depth === 0 ? 1 : 0; parent::start_el($output, $item, $depth, $args, $id); if (($this->count === $this->split_at) && ! $this->wrapped) { // split at number has been reached generate and store wrapped output $this->wrapped = true; $this->replaceTarget = $output; $this->wrappedOutput = $this->wrappedOutput($output); } elseif(($this->count === $this->split_at + 1) && ! $this->toSplit) { // split at number has been exceeded, replace regular with wrapped output $this->toSplit = true; $output = str_replace($this->replaceTarget, $this->wrappedOutput, $output); } } private function wrappedOutput($output) { $dom = new DOMDocument; $dom->loadHTML($output.''); $lis = $dom->getElementsByTagName('li'); $last = trim(substr($dom->saveHTML($lis->item($lis->length-1)), 0, -5)); // remove last li $wrappedOutput = substr(trim($output), 0, -1 * strlen($last)); $classes = array( 'menu-item', 'menu-item-type-custom', 'menu-item-object-custom', 'menu-item-has-children', 'menu-item-split-wrapper' ); // add wrap li element $wrappedOutput .= ''; // add the "more" link $wrappedOutput .= $this->button; // add the last item wrapped in a submenu and return return $wrappedOutput . '
Die Verwendung ist ziemlich einfach:
// by default make visible 5 elements wp_nav_menu(array('menu' => 'my_menu', 'walker' => new SplitMenuWalker())); // let's make visible 2 elements wp_nav_menu(array('menu' => 'another_menu', 'walker' => new SplitMenuWalker(2))); // customize the link to click/over to see wrapped items wp_nav_menu(array( 'menu' => 'another_menu', 'walker' => new SplitMenuWalker(5, 'more...') ));
Es gibt sogar eine Möglichkeit, dies mit CSS allein zu ermöglichen. Dies hat einige Einschränkungen, aber ich dachte immer noch, dass es ein interessanter Ansatz sein könnte:
Obwohl ich nicht wirklich “Quantity Queries” verwende, hat mich die kreative Verwendung von :nth-child
und ~
in den letzten Quantity Queries for CSS zu dieser Lösung geführt.
Der Ansatz ist im Grunde Folgendes:
before
-Pseudoelement verwenden. Hier ist der CSS-Code für eine Standard-WordPress-Menüauszeichnung. Ich habe inline kommentiert.
/* Optional: Center the navigation */ .main-navigation { text-align: center; } .menu-main-menu-container { display: inline-block; } /* Float menu items */ .nav-menu li { float:left; list-style-type: none; } /* Pull the 5th menu item to the left a bit so that there isn't too much space between item 4 and ... */ .nav-menu li:nth-child(4) { margin-right: -60px; } /* Create a pseudo element for ... and force line break afterwards (Hint: Use a symbol font to improve styling) */ .nav-menu li:nth-child(5):before { content: "...\A"; white-space: pre; } /* Give the first 4 items some padding and push them in front of the submenu */ .nav-menu li:nth-child(-n+4) { padding-right: 15px; position: relative; z-index: 1; } /* Float dropdown-items to the right. Hardcode width of dropdown. */ .nav-menu li:nth-child(n+5) { float:right; clear: right; width: 150px; } /* Float Links in dropdown to the right and hide by default */ .nav-menu li:nth-child(n+5) a{ display: none; float: right; clear: right; } /* When hovering the menu, show all menu items from the 5th on */ .nav-menu:hover li:nth-child(n+5) a, .nav-menu:hover li:nth-child(n+5) ~ li a{ display: inherit; } /* When hovering one of the first 4 items, hide all items after it so we do not activate the dropdown on the first 4 items */ .nav-menu li:nth-child(-n+4):hover ~ li:nth-child(n+5) a{ display: none; }
Ich habe auch eine jsfiddle erstellt, um es in Aktion zu zeigen: http://jsfiddle.net/jg6pLfd1/
Wenn Sie weitere Fragen haben, wie das funktioniert, hinterlassen Sie bitte einen Kommentar, ich würde mich freuen, den Code weiter zu klären.
Sie können den Filter wp_nav_menu_items
. Er akzeptiert Menüausgaben und Argumente, die Menüattribute enthalten, wie Menü-Slug, Container usw.
add_filter('wp_nav_menu_items', 'wpse_180221_nav_menu_items', 20, 2); function wpse_180221_nav_menu_items($items, $args) { if ($args->menu != 'my-menu-slug') { return $items; } // extract all elements from menu output preg_match_all('/]*>.*?< \/li>/iU', $items, $matches); // if menu has less the 5 items, just do nothing if (! isset($matches[0][5])) { return $items; } // add after 5th item (can be any number - can use eg site-wide variable) $matches[0][5] = '
'; }
Haben eine funktionierende function, aber nicht sicher, ob es die beste Lösung ist.
Ich habe einen benutzerdefinierten Walker verwendet:
class Custom_Walker_Nav_Menu extends Walker_Nav_Menu { function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) { global $wp_query; $indent = ( $depth ) ? str_repeat( "\t", $depth ) : ''; $classes = empty( $item->classes ) ? array() : (array) $item->classes; $classes[] = 'menu-item-' . $item->ID; $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) ); $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : ''; $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args, $depth ); $id = $id ? ' id="' . esc_attr( $id ) . '"' : ''; /** * This counts the $menu_items and wraps if there are more then 5 items the * remaining items into an extra */ global $menu_items; $menu_items = substr_count($output,'- '; $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 : ''; $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth ); $attributes = ''; foreach ( $atts as $attr => $value ) { if ( ! empty( $value ) ) { $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value ); $attributes .= ' ' . $attr . '="' . $value . '"'; } } $item_output = $args->before; $item_output .= ''; $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after; $item_output .= ''; $item_output .= $args->after; $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args ); } }
Die function, die das aktuelle Menü anzeigt, ist folgende:
< ?php wp_nav_menu( array( 'container' => false, 'theme_location' => 'navigation', 'fallback_cb' => 'custom_menu', 'walker' =>new Custom_Walker_Nav_Menu ) ); global $menu_items; // This adds the closing and if there are more then 4 items in the menu if ($menu_items > 4) { echo ""; } ?>
Ich habe die globale Variable $ menu_items deklariert und verwendet, um die abschließenden
und
-Tags anzuzeigen. Es ist wahrscheinlich möglich, das auch innerhalb des Custom Walker zu tun, aber ich habe nicht gefunden wo und wie.
Zwei Probleme: 1. Wenn nur 5 Elemente im Menü vorhanden sind, wird auch das letzte Element in ein Objekt eingeschlossen, obwohl dies nicht erforderlich ist.