Split wp_nav_menu mit benutzerdefinierten Walker

Ich versuche ein Menü mit maximal 5 Elementen zu erstellen. Wenn es mehr Elemente gibt, sollten Sie sie in ein anderes

    -Element einfügen, um ein Dropdown-Menü zu erstellen.

    5 Artikel oder weniger:

    Dropdown-Liste

    6 Gegenstände oder mehr

    Dropdown-Liste

    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

      einfügt. Aber ich weiß nicht, wie man diesen Walker erstellt.

      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.

      Solutions Collecting From Web of "Split wp_nav_menu mit benutzerdefinierten Walker"

      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.


      Bearbeiten

      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:

      Einschränkungen

      • Sie müssen die Breite des Dropdown-Menüs fest codieren
      • Browser-Unterstützung. Sie benötigen grundsätzlich CSS3-Selektoren . Aber alles von IE8 aufwärts sollte funktionieren, obwohl ich das nicht getestet habe.
      • Dies ist eher ein Proof-of-Concept. Es gibt mehrere Nachteile wie nur funktionieren, wenn es keine Unterpunkte gibt.

      Ansatz

      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:

      1. Alle Gegenstände nach dem 4. verstecken
      2. Fügen Sie Punkte hinzu, indem Sie ein before -Pseudoelement verwenden.
      3. Wenn Sie die Punkte (oder eines der ausgeblendeten Elemente) schweben lassen, werden die zusätzlichen Elemente als Untermenü angezeigt.

      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.

      1. Es funktioniert nur, wenn der Benutzer tatsächlich ein Menü dem theme_location zugewiesen hat. Der walker feuert nicht, wenn wp_nav_menu die Fallback-function anzeigt