कस्टम वॉकर के साथ wp_nav_menu को विभाजित करें


16

मैं एक मेनू बनाने की कोशिश कर रहा हूं जो अधिकतम 5 आइटम दिखाता है। यदि अधिक आइटम हैं तो <ul>ड्रॉपडाउन बनाने के लिए उन्हें दूसरे तत्व में लपेट देना चाहिए ।

5 आइटम या उससे कम:

ड्रॉप डाउन

6 आइटम या अधिक

ड्रॉप डाउन

मुझे पता है कि इस तरह की कार्यक्षमता आसानी से एक वॉकर के साथ बनाई जा सकती है जो मेनू आइटम को गिनता है और लपेटता है यदि अधिक है तो 5 एक अलग में रीमांग करते हैं <ul>। लेकिन मुझे नहीं पता कि इस वॉकर को कैसे बनाया जाए।

वर्तमान में मेरा मेनू दिखाने वाला कोड निम्नलिखित है:

<?php wp_nav_menu( array( 'theme_location' => 'navigation', 'fallback_cb' => 'custom_menu', 'walker' =>new Custom_Walker_Nav_Menu ) ); ?>

मैंने देखा कि यदि मेनू उपयोगकर्ता द्वारा परिभाषित नहीं है और यह फॉलबैक फ़ंक्शन का उपयोग करता है तो इसके बजाय वॉकर का कोई प्रभाव नहीं पड़ता है। मुझे दोनों मामलों में काम करने की जरूरत है।


1
कस्टम मेनू वॉकर एक वर्ग है जो विस्तारित होता है Walker_Nav_Menuऔर कोडेक्स में एक उदाहरण है । आपका क्या मतलब है "मुझे नहीं पता कि वॉकर कैसे बनाया जाए"?
साइबरमेट

Btw, +1 क्योंकि विचार वास्तव में काफी भयानक है। उस पर ठोकर कैसे लगी? क्या आपके पास कोई सोर्स पोस्ट या कुछ है? यदि हां, तो मुझे यह पढ़कर खुशी होगी। अग्रिम में धन्यवाद।
केसर

@kaiser सिर्फ एक गंदा डिजाइन विचार :) कोई स्रोत पोस्ट नहीं, इसलिए मैं पूछ रहा हूं।
स्नोबॉल

@cybmeta मुझे वाकर बनाना और साथ ही यह भी पता है कि कोडेक्स में एक उदाहरण है, लेकिन इस विशिष्ट समस्या के लिए कोई उदाहरण नहीं है। इसलिए मैं नहीं जानता कि कस्टम वॉकर कैसे बनाया जाए जो मुझे एक समाधान देता है
स्नोबॉल

आपको इस विचार के बारे में UX.SE लोगों से पूछना चाहिए और सत्यापित करना चाहिए कि क्या उपयोगकर्ता के लिए कोई समस्या है। UX एक बहुत ही कमाल की साइट है जो प्रयोज्यता / अनुभव और नियमित रूप से अच्छी तरह से सोचा जवाब और समस्याओं पर एक बहुत अच्छी वास्तविकता की जांच लाता है। आप तब भी वापस आ सकते हैं और हम सभी उस विचार को एक साथ परिष्कृत करते हैं। (यह वास्तव में भयानक होगा!)।
कैसर

जवाबों:


9

एक कस्टम वॉकर का उपयोग करते हुए, start_el()विधि की पहुंच है$depth परम : जब यह 0एक शीर्ष एक है, तो हम इस जानकारी का उपयोग आंतरिक काउंटर बनाए रखने के लिए कर सकते हैं।

जब काउंटर एक सीमा तक पहुंच जाता है, तो हम उपयोग कर सकते हैं DOMDocument पूर्ण HTML आउटपुट से प्राप्त करने के लिए बस अंतिम तत्व जोड़ा गया है, इसे एक सबमेनू में लपेटें और इसे फिर से HTML में जोड़ें।


संपादित करें

जब तत्वों की संख्या ठीक उसी संख्या होती है जिसकी हमें आवश्यकता होती है + 1, उदाहरण के लिए हमें 5 तत्वों की आवश्यकता होती है और मेनू में 6 होते हैं, तो मेनू को विभाजित करने का कोई मतलब नहीं होता है, क्योंकि तत्व 6 तरह से होंगे। यह पता करने के लिए कोड संपादित किया गया था।


यहाँ कोड है:

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 = '<a href="#">&hellip;</a>') {
      $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.'</ul></li>' : $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.'</li>');
       $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 .= '<li class="'.implode(' ', $classes).'">';
       // add the "more" link
       $wrappedOutput .= $this->button;
       // add the last item wrapped in a submenu and return
       return $wrappedOutput . '<ul class="sub-menu">'. $last;
   }
}

उपयोग बहुत सरल है:

// 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, '<a href="#">more...</a>')
));

उत्कृष्ट काम करता है! बहुत बढ़िया काम Giuseppe। इसके बारे में महान बात यह है कि यह पहले 5 मेनू तत्वों में एक सबमेनू होने पर भी काम करता है। और अगर यह इसके लिए कोई ज़रूरत नहीं है, तो एक सबमेनू में सिर्फ एक मेनू बिंदु नहीं लपेटता है। बस एक मामूली बात: डिफ़ॉल्ट रूप से यह 6 तत्वों को दिखाता है $split_at = 5लेकिन $countसूचकांक 0. से शुरू होता है
स्नोबॉल

धन्यवाद @Snowball मैंने तय किया कि मामूली समस्या, अब मेनू सटीक संख्या को $split_atतर्क के रूप में दिखाता है, डिफ़ॉल्ट रूप से 5।
गमजप

10

यहां तक ​​कि अकेले सीएसएस के साथ इसे संभव बनाने का एक तरीका है। इसकी कुछ सीमाएँ हैं, लेकिन मुझे अभी भी लगा कि यह एक दिलचस्प तरीका हो सकता है:

सीमाएं

  • आपको ड्रॉपडाउन की चौड़ाई को हार्डकोड करने की आवश्यकता है
  • ब्राउज़र समर्थन। आपको मूल रूप से CSS3 चयनकर्ताओं की आवश्यकता है । लेकिन IE8 से सब कुछ काम करना चाहिए, हालांकि मैंने यह परीक्षण नहीं किया है।
  • यह एक सबूत की अवधारणा से अधिक है। उप-आइटम नहीं होने पर केवल काम करने जैसी कई कमियां हैं।

पहुंच

हालाँकि मैं वास्तव में "क्वांटिटी क्वेरीज़" का रचनात्मक उपयोग नहीं कर रहा हूँ :nth-childऔर ~मैंने सीएसएस के लिए हाल की क्वांटिटी क्वेरीज़ में पढ़ा है जो मुझे इस समाधान तक ले गए।

दृष्टिकोण मूल रूप से यह है:

  1. 4 के बाद सभी आइटम छिपाएँ
  2. ...एक beforeछद्म तत्व का उपयोग करके डॉट्स जोड़ें ।
  3. डॉट्स (या किसी भी छिपे हुए तत्व) को मँडराते समय अतिरिक्त आइटम उर्फ ​​सबमेनू दिखाते हैं।

यहाँ एक डिफ़ॉल्ट वर्डप्रेस मेनू मार्कअप के लिए सीएसएस कोड है। मैंने इनलाइन टिप्पणी की है।

/* 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;
}

मैंने इसे एक्शन में दिखाने के लिए एक jsfield भी बनाया है: http://jsfiddle.net/jg6pLfd1/

यदि आपके पास कोई और सवाल है कि यह कैसे काम करता है तो कृपया एक टिप्पणी छोड़ें, मुझे आगे कोड स्पष्ट करने में खुशी होगी।


आपके दृष्टिकोण के लिए धन्यवाद। मैंने पहले से ही इसे सीएसएस के साथ करने के बारे में सोचा था लेकिन मुझे लगता है कि इसका क्लीनर इसे सीधे php के भीतर करना है। अतिरिक्त यह समाधान 5 वें मेनू बिंदु को एक सबमेनू में डालता है, इसके लिए भी कोई आवश्यकता नहीं है अगर सिर्फ पांच मेनू आइटम हैं।
स्नोबॉल

केवल 5+ वस्तुओं के लिए ही इसे सक्षम करना संभवत: तय किया जा सकता है। वैसे भी मुझे पता है कि यह सही नहीं है और एक PHP दृष्टिकोण क्लीनर हो सकता है। लेकिन मुझे अभी भी यह काफी दिलचस्प लगा कि इसे संपूर्णता के लिए शामिल किया जाए। एक और विकल्प हमेशा अच्छा होता है। :)
क्राफ्टर

2
बेशक। Btw। अगर आप इसमें एक और सबमेनू जोड़ते हैं तो यह टूट जाता है
स्नोबॉल

1
ज़रूर। यह अब तक की अवधारणा का प्रमाण है। एक चेतावनी जोड़ी।
क्राफ्टर

8

आप wp_nav_menu_itemsफ़िल्टर का उपयोग कर सकते हैं । यह मेनू आउटपुट और तर्कों को स्वीकार करता है जो मेनू विशेषताओं को रखता है, जैसे मेनू स्लग, कंटेनर, आदि।

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 <li></li> elements from menu output
    preg_match_all('/<li[^>]*>.*?<\/li>/iU', $items, $matches);

    // if menu has less the 5 items, just do nothing
    if (! isset($matches[0][5])) {
        return $items;
    }

    // add <ul> after 5th item (can be any number - can use e.g. site-wide variable)
    $matches[0][5] = '<li class="menu-item menu-item-type-custom">&hellip;<ul>'
          . $matches[0][5];

    // $matches contain multidimensional array
    // first (and only) item is found matches array
    return implode('', $matches[0]) . '</ul></li>';
}

1
मैंने कुछ मामूली मुद्दों को संपादित किया है, हालांकि यह तभी काम करता है जब सभी मेनू आइटम में कोई उप आइटम न हों। क्योंकि रेगेक्स पदानुक्रम को मान्यता नहीं देता है। इसका परीक्षण करें: यदि पहले 4 मेनू आइटमों में कोई बाल आइटम है, तो मेनू बहुत नष्ट हो गया है।
gmazzap

1
यह सच है। उस मामले में DOMDocumentइस्तेमाल किया जा सकता है। हालांकि, इस प्रश्न में कोई उप मेनू नहीं हैं, इस प्रकार इस विशिष्ट मामले के लिए उत्तर सही है। DOMDocument "सार्वभौमिक" समाधान होगा लेकिन मेरे पास अभी कोई समय नहीं है। आप जांच कर सकते हैं;) एलआई आइटमों के माध्यम से लूपिंग, अगर किसी के पास उल बच्चा है, तो इसका समाधान होगा, लेकिन लिखित संस्करण की आवश्यकता होगी :)
mjakic

1
(ए) आप नहीं जानते कि क्या ओपी में सबमेनू हैं। सबमेनस तब दिखाई देता है जब माउस खत्म हो जाता है, इसलिए ... (b) हां, DOMDocument काम कर सकता है, लेकिन उस स्थिति में आपको आंतरिक रूप से जांच करने के लिए पुनरावर्ती लूप आइटम की आवश्यकता होती है ul। वर्डप्रेस पहले ही मेनू वॉकर में मेनू आइटम को लूप करता है। यह पहले से ही प्रति धीमी गति से संचालन है , मुझे लगता है कि अतिरिक्त लूप है और मुझे लगता है कि सही समाधान नहीं है, इसके विपरीत, एक कस्टम वॉकर बहुत क्लीनर और कुशल समाधान होगा।
gmazzap

धन्यवाद दोस्तों, लेकिन @gmazzap सच है, इस बात की संभावना है कि अन्य मेनू बिंदु (पहले 4 या अन्य दोनों) में एक और सबमेनू हो। तो यह आत्मा काम नहीं करेगा।
स्नोबॉल

आप दो मेनू भी रख सकते हैं, मुख्य एक और "छिपा हुआ" एक। तीन डॉट्स "..." के साथ एक स्टाइल बटन जोड़ें और दूसरे मेनू पर क्लिक या हॉवर शो करें। सुपर आसान होना चाहिए।
मजाकिक

5

एक काम कर रहे फ़ंक्शन मिला, लेकिन यह निश्चित नहीं है कि यह सबसे अच्छा समाधान है।

मैंने एक कस्टम वॉकर का इस्तेमाल किया:

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 <ul>
     */
    global $menu_items;
    $menu_items = substr_count($output,'<li');
    if ($menu_items == 4) {
      $output .= '<li class="tooltip"><span>...</span><ul class="tooltip-menu">';
    }

    $output .= $indent . '<li' . $id . $class_names .'>';

    $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 .= '<a'. $attributes .'>';
    $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
    $item_output .= '</a>';
    $item_output .= $args->after;

    $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );

  }
}

वास्तविक मेनू दिखाने वाला फ़ंक्शन निम्न है:

        <?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 </li> and </ul> if there are more then 4 items in the menu
        if ($menu_items > 4) {
            echo "</li></ul>";
        }
        ?>

मैंने वैश्विक चर $ menu_items घोषित किया और इसका उपयोग समापन <li>और -टैग दिखाने के लिए किया <ul>। इसका संभावित रूप से कस्टम वॉकर के अंदर भी संभव है, लेकिन मुझे यह नहीं पता था कि कहां और कैसे।

दो समस्याएं: 1. यदि मेनू में सिर्फ 5 आइटम हैं, तो यह अंतिम आइटम को लपेटता है और साथ ही साथ यह भी आवश्यक है कि इसकी कोई आवश्यकता नहीं है।

  1. यह बस काम करता है अगर उपयोगकर्ता ने वास्तव में theme_location को मेनू आवंटित किया है, तो वॉकर आग नहीं देता है यदि wp_nav_menu फॉलबैक फ़ंक्शन दिखा रहा है

क्या आपने कोशिश की है कि यदि पहले 4 वस्तुओं में कुछ सबमेनस हो तो क्या होगा? युक्ति: substr_count($output,'<li')हो जाएगा == 4गलत जगह पर ...
gmazzap
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.