Mega Menu Walker

10

Próbuję stworzyć mega walker menu. Niestety, spacerowicze całkowicie unikają mojej wiedzy na temat kodowania. Naprawdę przydałaby mi się pomoc w uruchomieniu. Oto funkcje, których potrzebuję:

  • Owinąć drugiego poziomu <ul>w <section>. [KOMPLETNY]
  • Kiedy użytkownik ustawi klasę „break” na <li>drugim poziomie <ul>, uczyń, aby <li>początek był nowy <ul>. Jeśli jest to pierwszy <li>na liście, nie rób nic, aby zapobiec tworzeniu pustych nieuporządkowanych list. [KOMPLETNY]
  • Gdy użytkownik ustawi „widżet” klasy na <li>pierwszym poziomie, który ma podrzędny <ul>, dołącz widżet na końcu tego <ul>. [KOMPLETNY]
  • Dodaj klasę mega-menu-columns-#do <li>elementów pierwszego poziomu , które zawierają listy rozwijane z wieloma kolumnami i / lub widżetem. # Reprezentuje liczbę <ul>elementów, +1 dla widżetu, jeśli istnieje. [KOMPLETNY]

Mam trochę kodu, aby to zrobić, ale nie wszystko. Poniżej znajdują się wycięte sekcje:

Zawiń drugi poziom <ul>w <section>:

function start_lvl(&$output, $depth = 0, $args = array()) {
    if ($depth == 0) {
        $output .= "<section>";
    }
    $output .= "<ul class=\"sub-menu\">";
}
function end_lvl(&$output, $depth = 0, $args = array()) {
    $output .= "</ul>";
    if ($depth == 0) {
        $output .= "</section>\n";
    }
}

Wygeneruj HTML widgetu:

ob_start();
dynamic_sidebar("Navigation Callout");
$widget = ob_get_contents();
ob_end_clean();

Wyjściowy kod HTML to:

<ul>
    <li id="menu-item-1" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-1 mega-menu-columns-2">
        <a href="http://www.example.com/about/">
            About Us
        </a>
        <section>
            <ul class="sub-menu">
                <li id="menu-item-2" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2">
                    <a href="http://www.example.com/about/company-profile/">
                        Company Profile
                    </a>
                </li>
                <li id="menu-item-3" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-3">
                    <a href="http://www.example.com/about/leadership-team/">
                        Leadership Team
                    </a>
                </li>
                <li id="menu-item-4" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-4">
                    <a href="http://www.example.com/about/professional-affiliations/">
                        Professional Affiliations
                    </a>
                </li>
            </ul>
            <ul class="sub-menu">
                <li id="menu-item-5" class="break menu-item menu-item-type-post_type menu-item-object-page menu-item-5">
                    <a href="http://www.example.com/about/clients/">
                        Clients
                    </a>
                </li>
                <li id="menu-item-6" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-6">
                    <a href="http://www.example.com/about/partnerships/">
                        Partnerships
                    </a>
                </li>
            </ul>
        </section>
    </li>
    <li id="menu-item-7" class="widget menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-7 mega-menu-columns-3">
        <a href="http://www.example.com/services/">
            Services
        </a>
        <section>
            <ul class="sub-menu">
                <li id="menu-item-8" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-8">
                    <a href="http://www.example.com/services/civil-engineering/">
                        Civil Engineering
                    </a>
                </li>
                <li id="menu-item-9" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-9">
                    <a href="http://www.example.com/services/land-planning/">
                        Land Planning
                    </a>
                </li>
                <li id="menu-item-10" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-10">
                    <a href="http://www.example.com/services/surveying/">
                        Surveying
                    </a>
                </li>
            </ul>
            <ul class="sub-menu">
                <li id="menu-item-11" class="break menu-item menu-item-type-post_type menu-item-object-page menu-item-11">
                    <a href="http://www.example.com/services/information-technology/">
                        Information Technology
                    </a>
                </li>
                <li id="menu-item-12" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-12">
                    <a href="http://www.example.com/services/subsurface-utility-engineering/">
                        Subsurface Utility Engineering
                    </a>
                </li>
            </ul>
            <aside>
                <h6>Widget Title</h6>
                <p>Maecenas quis semper arcu. Quisque consequat risus nisi. Sed venenatis urna porta eros malesuada euismod. Nulla sollicitudin fringilla posuere. Nulla et tellus eu nisi sodales convallis non vel tellus.</p>
            </aside>
        </section>
    </li>
    <li id="menu-item-13" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-15">
        <a href="http://www.example.com/contact/">
            Contact Us
        </a>
    </li>
</ul>

AKTUALIZACJA: Moje liczniki budzą we mnie smutek. Liczą się dopiero po wygenerowaniu podmenu, co mi nie pomaga. Zobacz ten zrzut ekranu, aby zrozumieć, co mam na myśli:

Wciągane są najlepsze numery start_el. Wciągane są dolne liczby end_el. Jak widać, najwyższe liczby nie liczą mojego .breakstak, jak powinny. Liczą klasę widżetów, ponieważ są one liczone $depth = 0. Niech ktoś uratuje mnie od tej okropności!

// mega menu walker
/*
    ONE REMAINING BUG:
    - Need to add class to LI containing mega-menu-columns-#
*/
class megaMenuWalker extends Walker_Nav_Menu {
    private $column_limit = 3; /* needs to be set for each site */
    private $show_widget = false;
    private $column_count = 0;
    static $li_count = 0;
    function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
        $classes = empty($item->classes) ? array() : (array) $item->classes;
        $item_id = $item->ID;
        if ($depth == 0) self::$li_count = 0;
        if ($depth == 0 && in_array("widget", $classes)) {
            $this->show_widget = true;
            $this->column_count++;
        }
        if ($depth == 1 && self::$li_count == 1) {
            $this->column_count++;
        }
        if ($depth == 1 && in_array("break", $classes) && self::$li_count != 1 && $this->column_count < $this->column_limit) {
            $output .= "</ul><ul class=\"sub-menu\">";
            $this->column_count++;
        }
        if ($depth == 0 && $this->column_count > 0) {
            $mega_menu_class = " mega-menu-columns-" . $this->column_count;
        }
        $class_names = join(" ", apply_filters("nav_menu_css_class", array_filter($classes), $item));
        $class_names = " class=\"" . esc_attr($class_names . $mega_menu_class) . "\"";
        $output .= sprintf(
            "<li id=\"menu-item-%s\"%s><a href=\"%s\">%s</a>",
            $item_id,
            $class_names,
            $item->url,
            $item->title
        );
        self::$li_count++;
    }
    function start_lvl(&$output, $depth = 0, $args = array()) {
        if ($depth == 0) {
            $output .= "<section>";
        }
        $output .= "<ul class=\"sub-menu\">";
    }
    function end_lvl(&$output, $depth = 0, $args = array()) {
        $output .= "</ul>";
        if ($depth == 0) {
            if ($this->show_widget) {
                ob_start();
                dynamic_sidebar("Navigation Callout");
                $widget = ob_get_contents();
                ob_end_clean();
                $output .= $widget;
                $this->show_widget = false;
            }
            $output .= "</section>";
        }
    }
    function end_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
        if ($depth == 0 && $this->column_count > 0) {
            /* needs to be added to opening level 0 li */
            $column_count_class = " mega-menu-columns-" . $this->column_count;
            $output .= $column_count_class;
            /* end */
            $this->column_count = 0;
        }
        $output .= "</li>";
    }
}   

AKTUALIZACJA 2: Oto przykład wyniku z komentarzami opisującymi, jak mega-menu-columns-klasa powinna liczyć rzeczy:

<ul>
    <!-- +1 because this has a class of "widget" -->
    <li id="menu-item-1" class="widget menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-1 mega-menu-columns-3">
        <a href="http://www.example.com/about/">
            About Us
        </a>
        <!-- +1 because a drop down exists -->
        <!-- gets added by my walker -->
        <section>
        <!-- end gets added by my walker -->
            <ul class="sub-menu">
                <!-- +0 because this "break" is the first child -->
                <li id="menu-item-2" class="break menu-item menu-item-type-post_type menu-item-object-page menu-item-2">
                    <a href="http://www.example.com/about/company-profile/">
                        Company Profile
                    </a>
                    <ul>
                        <!-- +0 because this "break" is in level 2 -->
                        <li id="menu-item-3" class="break menu-item menu-item-type-post_type menu-item-object-page menu-item-3">
                            <a href="http://www.example.com/about/our-team/">
                                Our Team
                            </a>
                        </li>
                    </ul>
                </li>
                <li id="menu-item-4" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-4">
                    <a href="http://www.example.com/about/leadership-team/">
                        Leadership Team
                    </a>
                </li>
                <li id="menu-item-5" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-5">
                    <a href="http://www.example.com/about/professional-affiliations/">
                        Professional Affiliations
                    </a>
                </li>
            <!-- gets added by my walker -->
            </ul>
            <ul class="sub-menu">
            <!-- end gets added by my walker -->
                <!-- +1 because this "break" is in level 1 and not the first child -->
                <li id="menu-item-6" class="break menu-item menu-item-type-post_type menu-item-object-page menu-item-6">
                    <a href="http://www.example.com/about/clients/">
                        Clients
                    </a>
                </li>
                <li id="menu-item-7" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-7">
                    <a href="http://www.example.com/about/partnerships/">
                        Partnerships
                    </a>
                </li>
            </ul>
            <!-- gets added by my walker for .widget -->
            <section>
                <header>
                    <h1>Widget Title</h1>
                </header>
                <p>This is a widget. It was hard to make appear!</p>
            </section>
            <!-- end gets added by my walker for .widget -->
        <!-- gets added by my walker -->
        </section>
        <!-- end gets added by my walker -->
    </li>
</ul>

AKTUALIZACJA: Oto mój ostatni Walker i funkcje. To robi dokładnie to , czego chciałem. Dzięki za pomoc!

// mega menu walker
class megaMenuWalker extends Walker_Nav_Menu {
    private $column_limit = 3;
    private $show_widget = false;
    private $column_count = 0;
    static $li_count = 0;
    function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
        $classes = empty($item->classes) ? array() : (array) $item->classes;
        $item_id = $item->ID;
        if ($depth == 0) {
            self::$li_count = 0;
        }
        if ($depth == 0 && in_array("widget", $classes)) {
            $this->show_widget = true;
            $this->column_count++;
        }
        if ($depth == 1 && self::$li_count == 1) {
            $this->column_count++;
        }
        if ($depth == 1 && in_array("break", $classes) && self::$li_count != 1 && $this->column_count < $this->column_limit) {
            $output .= "</ul><ul class=\"sub-menu\">";
            $this->column_count++;
        }
        $class_names = join(" ", apply_filters("nav_menu_css_class", array_filter($classes), $item)); // set up the classes array to be added as classes to each li
        $class_names = " class=\"" . esc_attr($class_names) . "\"";
        $output .= sprintf(
            "<li id=\"menu-item-%s\"%s><a href=\"%s\">%s</a>",
            $item_id,
            $class_names,
            $item->url,
            $item->title
        );
        self::$li_count++;
    }
    function start_lvl(&$output, $depth = 0, $args = array()) {
        if ($depth == 0) {
            $output .= "<section>";
        }
        $output .= "<ul class=\"sub-menu\">";
    }
    function end_lvl(&$output, $depth = 0, $args = array()) {
        $output .= "</ul>";
        if ($depth == 0) {
            if ($this->show_widget) {
                ob_start();
                dynamic_sidebar("Navigation Callout");
                $widget = ob_get_contents();
                ob_end_clean();
                $output .= $widget;
                $this->show_widget = false;
            }
            $output .= "</section>";
        }
    }
    function end_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
        if ($depth == 0 && $this->column_count > 0) {
            $this->column_count = 0;
        }
        $output .= "</li>";
    }
}

// add mega-menu-columns-# classes
function add_column_number($items, $args) {
    static $column_limit = 3;
    static $post_id = 0;
    static $x_key = 0;
    static $column_count = 0;
    static $li_count = 0;
    $tmp = array();
    foreach($items as $key => $item) {
        if (0 == $item->menu_item_parent) {
            $x_key = $key;
            $post_id = $item->ID;
            $column_count = 0;
            $li_count = 0;
            if (in_array("widget", $item->classes, 1)) {
                $column_count++;
            }
        }
        if ($post_id == $item->menu_item_parent) {
            $li_count++;
            if ($column_count < $column_limit && $li_count == 1) {
                $column_count++;
            }
            if (in_array("break", $item->classes, 1) && $li_count > 1 && $column_count < $column_limit) {
                $column_count++;
            }
            $tmp[$x_key] = $column_count;
        }
    }
    foreach($tmp as $key => $value) {
        $items[$key]->classes[] = sprintf("mega-menu-columns-%d", $value);
    }
    unset($tmp);
    return $items;
};

// add the column classes
add_filter("wp_nav_menu_args", function($args) {
    if ($args["walker"] instanceof megaMenuWalker) {
        add_filter("wp_nav_menu_objects", "add_column_number");
    }
    return $args;
});

// stop the column classes function
add_filter("wp_nav_menu", function( $nav_menu ) {
    remove_filter("wp_nav_menu_objects", "add_column_number");
    return $nav_menu;
});
JacobTheDev
źródło
1
To jest super niesamowite i dokładnie to, co chciałem budować od dłuższego czasu! Nie byłem pewien, jak poradzić sobie z tworzeniem nowych kolumn i takie, więc naprawdę podoba mi się pomysł, aby po prostu ustawić nazwę klasy na przedmiotach, aby utworzyć nowe kolumny, a także możliwość dodawania obszarów widżetów, po prostu genialne! Mam jednak problemy z gotowym kodem ... wyświetla on poprawny kod HTML, jednak pojawia się błąd PHP ... Warning: Missing argument 2 for add_column_number() Czy napotkałeś ten problem?
JasonDavis
Nie sądzę, wydaje się, że działa dobrze na dwóch stronach, na których dotychczas go używałem. Zaktualizowałem powyższy kod, na wypadek gdyby coś zmieniłem podczas jego wdrażania. Spróbuj ponownie i daj mi znać, jeśli nadal nie działa; Nie jestem zbyt dobry w PHP, ale chętnie pomogę :)
JacobTheDev
1
To, co musiałem zrobić, to zmienić to function add_column_number($items, $args) {na function add_column_number($items) {usunięcie $argsi to działa dobrze dla mnie z tą zmianą, dość dziwne! Dzięki za udostępnienie twojego kodu, ale to jest to, czego potrzebowałem od dłuższego czasu
JasonDavis
Nie ma problemu, wiem, jak trudno to zrobić, dlatego udostępniłem swój pełny kod :) Cieszę się, że to pomogło!
JacobTheDev

Odpowiedzi:

7

Jeśli dobrze rozumiem konfigurację problemu, możesz spróbować dokonać liczenia klas przerw i widżetów w wp_nav_menu_objectsfiltrze.

Oto zaktualizowany przykład, który został raczej rozszerzony z powodu dodatkowej części debugowania:

add_filter( 'wp_nav_menu_objects', 
    function( $items, $args  ) {

        // Only apply this for the 'primary' menu:    
        if( 'primary' !== $args->theme_location ) 
            return $items;

        // Here "x_" means the latest root li (depth 0)
        static $x_pid           = 0;      // post ID of the latest root li (depth 0) 
        static $x_key           = 0;      // array key of the latest root li (depth 0)
        static $x_cols          = 0;      // n breaks or widgets gives n+1 columns  
        static $x_has_dropdown  = false;  // if the latest root li (depth 0) has dropdown

        // Internals:
        $tmp            = array();  
        $debug_string   = '';
        $show_debug     = true;  // Edit this to your needs:

        foreach( $items as $key => $item )
        {
            // Debug:
            $debug                              = array();
            $debug['ID']                        = $item->ID;
            $debug['title']                     = $item->title;
            $debug['key']                       = $key;
            $debug['x_key']                     = $x_key;
            $debug['depth']                     = '';
            $debug['menu_item_parent']          = $item->menu_item_parent;
            $debug['has_widget_class']          = 0;
            $debug['is_depth_1_first_child']    = 0;
            $debug['x_has_dropdown']            = 0;
            $debug['has_break_class']           = 0;
            $debug['x_cols_increase']           = 0;

            // Collect columns increaments:
            $inc = 0;

            // Depth 0:
            if( 0 == $item->menu_item_parent )
            {
                $debug['depth'] = 0;

                // Resets:
                $x_key          = $key;
                $x_pid          = $item->ID;                            
                $x_cols         = 0;
                $x_has_dropdown = false;

                // If widget class exists:
                if( in_array( 'widget', $item->classes, 1 ) )
                {
                    $debug['has_widget_class'] = '1';
                    $inc++; 
                }       
            }

            // Depth 1:
            if( $x_pid == $item->menu_item_parent )
            {   
                $debug['depth'] = 1;

                // Increase the columns count for an existing dropdown:
                if( ! $x_has_dropdown )
                {
                    $inc++;
                    $x_has_dropdown = true;
                }

                // Check for the 'break' class: 
                if( in_array( 'break', $item->classes, 1 ) )
                {
                    $debug['x_has_break_class'] = 1;

                    // First li child:
                    if( $x_key+1 == $key+0 )
                    {
                        $debug['is_depth_1_first_child'] = 1;
                    }
                    else
                    {
                        $debug['is_depth_1_first_child'] = 0;
                        $inc++; 
                    }
                }

                $t[$x_key] = $x_cols;
            }           

            $debug['x_has_dropdown'] = (int) $x_has_dropdown;

            // Increase the columns count:
            $debug['x_cols_increase'] = $inc;           
            $x_cols += $inc;
            $debug['x_cols'] = $x_cols;

            // Collect the debug:
            $debug_string .= print_r( $debug, 1 );
        } // end foreach

        // Show debug info:
        if( $show_debug ) 
            printf( "<!-- debug: %s -->", $debug_string );

        // Insert the new 'mega menu' class to the corresponding menu object:
        foreach( $t as $key => $value )
        {
            $items[$key]->classes[] = sprintf( 'mega-menu-columns-%d', $value );
        }

        return $items;

    }
, PHP_INT_MAX, 2 );

W twoim bieżącym drzewie menu otrzymuję te informacje debugowania:

<!-- debug: Array
(
    [ID] => 3316
    [title] => About Us
    [key] => 1
    [x_key] => 0
    [depth] => 0
    [menu_item_parent] => 0
    [has_widget_class] => 1
    [is_depth_1_first_child] => 0
    [x_has_dropdown] => 0
    [has_break_class] => 0
    [x_cols_increase] => 1
    [x_cols] => 1
)
Array
(
    [ID] => 3317
    [title] => Company Profile
    [key] => 2
    [x_key] => 1
    [depth] => 1
    [menu_item_parent] => 3316
    [has_widget_class] => 0
    [is_depth_1_first_child] => 1
    [x_has_dropdown] => 1
    [has_break_class] => 0
    [x_cols_increase] => 1
    [x_has_break_class] => 1
    [x_cols] => 2
)
Array
(
    [ID] => 3318
    [title] => Our Team
    [key] => 3
    [x_key] => 1
    [depth] => 
    [menu_item_parent] => 3317
    [has_widget_class] => 0
    [is_depth_1_first_child] => 0
    [x_has_dropdown] => 1
    [has_break_class] => 0
    [x_cols_increase] => 0
    [x_cols] => 2
)
Array
(
    [ID] => 3319
    [title] => Leadership Team
    [key] => 4
    [x_key] => 1
    [depth] => 1
    [menu_item_parent] => 3316
    [has_widget_class] => 0
    [is_depth_1_first_child] => 0
    [x_has_dropdown] => 1
    [has_break_class] => 0
    [x_cols_increase] => 0
    [x_cols] => 2
)
Array
(
    [ID] => 3320
    [title] => Professional Affiliations
    [key] => 5
    [x_key] => 1
    [depth] => 1
    [menu_item_parent] => 3316
    [has_widget_class] => 0
    [is_depth_1_first_child] => 0
    [x_has_dropdown] => 1
    [has_break_class] => 0
    [x_cols_increase] => 0
    [x_cols] => 2
)
Array
(
    [ID] => 3321
    [title] => Clients
    [key] => 6
    [x_key] => 1
    [depth] => 1
    [menu_item_parent] => 3316
    [has_widget_class] => 0
    [is_depth_1_first_child] => 0
    [x_has_dropdown] => 1
    [has_break_class] => 0
    [x_cols_increase] => 1
    [x_has_break_class] => 1
    [x_cols] => 3
)
Array
(
    [ID] => 3322
    [title] => Partnerships
    [key] => 7
    [x_key] => 1
    [depth] => 1
    [menu_item_parent] => 3316
    [has_widget_class] => 0
    [is_depth_1_first_child] => 0
    [x_has_dropdown] => 1
    [has_break_class] => 0
    [x_cols_increase] => 0
    [x_cols] => 3
)
 -->

Jeśli chcesz sprawdzić, czy obiekt walker należy do megaMenuWalkerklasy, możesz użyć:

if( ! is_object( $args->walker ) || ! is_a( $args->walker, 'megaMenuWalker' ) )
    return $items;

zamiast

if( 'primary' !== $args->theme_location ) 
            return $items;

Mam nadzieję, że to pomoże.

birgire
źródło
O dziwo daje to 1 jako liczbę. Zobacz hfracquetandfitness.myweblinx.net (sprawdź pierwsze li w górnej nawigacji). EDYCJA - Myślę, że wiem dlaczego. Przerwa zostaje dodana do bezpośrednich <li>elementów potomnych , a nie bieżących <li>. Musi sprawdzić, czy jest to bezpośrednie dziecko, a nie pierwsze dziecko, które również ma klasę break.
JacobTheDev,
Zaktualizowałem odpowiedź, miałem literówkę (&& zamiast ||).
birgire,
Ach, to trochę pomogło, ale widzę, że to się liczy, jeśli bieżący element listy ma klasę break. Faktycznie musi się liczyć, jeśli bezpośrednie dzieci mają klasę break, a nie same, i wykluczyć pierwsze dziecko liw dziecku ul, jeśli ma to sens.
JacobTheDev,
Zakładam, że <li>elementy główne znajdują się na głębokości 0, a następnie liczę klasy przerwania / widgetu elementów podrzędnych <li>na głębokości 1. Myślę, że powinienem rozpocząć $x_colssię 1zamiast 0, ponieważ nprzerwy daje n+1kolumn.
birgire,
1
Nie ma za co. Zaktualizowałem odpowiedź, podając przykład, jak sprawdzić klasę walkera zamiast lokalizacji motywu. Powodzenia w projekcie.
birgire