در وردپرس، Custom Nav Walker (کلاس walker سفارشی) ابزار اصلی برای عبور از ساختار پیش‌فرض منو و ساخت منوی مگا (Mega Menu) چندستونه است. بدون درک درست start_el، start_lvl و end_el، هر پیاده‌سازی مگا منو در مقیاس بزرگ به کد شکننده تبدیل می‌شود. منوی مگا بدون ساختار ریسپانسیو و بدون تست سلسله‌مراتب، تجربه کاربری موبایل را نابود می‌کند. کلاس Walker_Nav_Menu هسته‌ای است، اما برای ساختار مگا باید زیرکلاس اختصاصی با متدهای بازنویسی‌شده بسازید. در این راهنما از تعریف walker پایه تا استقرار تولیدی منوی مگا را با نگاه مهندسی و کد عملی پوشش می‌دهیم.

در پروژه‌های واقعی، هر بار که تیم توسعه تصمیم گرفته منوی مگا را با افزونه آماده پیاده کند، در همان ماه اول با محدودیت ساختار یا بدهی فنی مواجه شده‌ایم. Custom Nav Walker (کلاس مشتق‌شده از Walker_Nav_Menu) راه‌حل بومی وردپرس است که کنترل کامل روی خروجی HTML منو می‌دهد. این کنترل، تفاوت بین منویی که فقط زیبا به‌نظر می‌رسد و منویی که در مقیاس سازمانی پایدار می‌ماند را رقم می‌زند.

Custom Nav Walker چیست و چه زمانی به آن نیاز داریم؟

Walker یک الگوی طراحی (Design Pattern) در وردپرس است که وظیفه پیمایش ساختارهای درختی و تولید خروجی HTML را برعهده دارد. Walker_Nav_Menu زیرکلاس تخصصی Walker برای منو است. هسته وردپرس از این کلاس برای تولید <ul> <li> منو استفاده می‌کند.

وقتی منوی مگا می‌سازید، ساختار پیش‌فرض کافی نیست. برای مثال، می‌خواهید زیرمنو در چند ستون نمایش داده شود، یا محتوای سفارشی (تصویر، توضیح، فرم) در هر آیتم داشته باشید. اینجاست که Custom Nav Walker وارد می‌شود.

برای درک پیش‌زمینه این مفهوم، ابتدا راهنمای کلاس Walker سفارشی در وردپرس را مطالعه کنید. همچنین اگر با ساختار پست‌تایپ سفارشی آشنا نیستید، راهنمای ساخت نوع نوشته سفارشی نقطه شروع مناسبی است.

نشانه‌هایی که باید به walker سفارشی فکر کنید

اگر در پروژه خود با این علائم مواجه شدید، احتمالاً زمان ساخت walker اختصاصی رسیده است: نیاز به منوی چندستونه، نیاز به محتوای غنی در آیتم‌ها، نیاز به بارگذاری پویا بر اساس دسته‌بندی محصولات، یا نیاز به ساختار HTML خاص برای ریسپانسیو.

ساختار داخلی Walker_Nav_Menu در هسته وردپرس

کلاس Walker_Nav_Menu پنج متد اصلی دارد: start_lvl، end_lvl، start_el، end_el و walk. برای سفارشی‌سازی، معمولاً سه متد اول بازنویسی می‌شوند.

متد start_lvl در آغاز هر زیرمنو اجرا می‌شود و خروجی پیش‌فرض آن یک <ul class="sub-menu"> است. متد start_el برای هر آیتم منو اجرا می‌شود و ساختار <li> و <a> را می‌سازد. متد end_el نیز بسته‌شدن </li> را مدیریت می‌کند.

در سطوح پیشرفته، پارامتر $depth تعیین می‌کند که آیتم در چه سطحی از درخت قرار دارد. با استفاده از این پارامتر، می‌توانید برای سطح صفر، ساختار مگا و برای سطوح بالاتر، ساختار لیست معمولی تولید کنید.

برای مروری بر ساختار هوک‌های مرتبط با منو، راهنمای تابع wp_nav_menu در وردپرس را ببینید. همچنین برای درک نحوه ثبت منو در قالب، راهنمای تابع register_nav_menus را مطالعه کنید.

چرخه اجرای Walker در فرآیند رندر منو

وقتی wp_nav_menu فراخوانی می‌شود، تابع wp_get_nav_menu_items ابتدا آیتم‌ها را از دیتابیس می‌خواند و به یک درخت تبدیل می‌کند. سپس کلاس Walker با فراخوانی متد walk، درخت را پیمایش می‌کند و برای هر آیتم، start_el و end_el را صدا می‌زند. در نهایت رشته HTML ساخته‌شده به خروجی منو تزریق می‌شود.

ساخت کلاس walker سفارشی از صفر

برای ساخت walker سفارشی، یک کلاس با ارث‌بری از Walker_Nav_Menu تعریف می‌کنیم. این کلاس معمولاً در functions.php قالب یا در یک فایل اختصاصی در پوشه inc قرار می‌گیرد.

ساختار پایه به این شکل است:

class WordPressKar_Mega_Walker extends Walker_Nav_Menu {
    public function start_lvl( &$output, $depth = 0, $args = null ) {
        $indent = str_repeat( "	", $depth );
        $classes = array( "sub-menu" );
        if ( 0 === $depth ) {
            $classes[] = "mega-menu";
        }
        $class_names = implode( " ", $classes );
        $output .= "
$indent<ul class="$class_names">
";
    }

    public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $classes[] = "menu-item-$item->ID";
        if ( 0 === $depth ) {
            $classes[] = "top-level-item";
        }
        $class_names = implode( " ", apply_filters( "nav_menu_css_class", $classes, $item, $args, $depth ) );
        $output .= "<li class="$class_names">";
        $atts = array(
            "title"  => ! empty( $item->attr_title ) ? $item->attr_title : "",
            "target" => ! empty( $item->target ) ? $item->target : "",
            "rel"    => ! empty( $item->xfn ) ? $item->xfn : "",
            "href"   => ! empty( $item->url ) ? $item->url : "",
        );
        $attributes = "";
        foreach ( $atts as $attr => $value ) {
            if ( ! empty( $value ) ) {
                $attributes .= " $attr="" . esc_attr( $value ) . """;
            }
        }
        $output .= "<a$attributes>" . esc_html( $item->title ) . "</a>";
    }
}

این ساختار پایه، به شما اجازه می‌دهد تا با تغییر کلاس‌ها و افزودن منطق شرطی بر اساس $depth، منوی مگا بسازید.

برای مطالعه بیشتر در مورد ساختار ماژولار قالب، راهنمای ساخت ماژول سفارشی در قالب را ببینید.

افزودن متادیتای سفارشی به آیتم‌های منو

برای افزودن توضیحات، تصویر یا آیکون به هر آیتم منو، از فیلترهای wp_setup_nav_menu_item و wp_nav_menu_objects استفاده می‌کنیم. این فیلترها امکان تزریق داده سفارشی به آیتم‌ها را فراهم می‌کنند.

طراحی ساختار منوی مگا با walker اختصاصی

منوی مگا معمولاً از چند ستون تشکیل می‌شود که در هر ستون، یک زیرمنو یا محتوای سفارشی قرار می‌گیرد. برای پیاده‌سازی، معمولاً از ترکیب walker سفارشی و CSS Grid یا Flexbox استفاده می‌کنیم.

الگوی حرفه‌ای این است که در سطح صفر، از کلاس mega-menu استفاده کنیم و در سطح یک، آیتم‌ها را در ستون‌های جدا نمایش دهیم. برای این کار، در متد start_lvl می‌توانیم یک wrapper اضافه کنیم و در CSS، ساختار Grid را تعریف کنیم.

اگر می‌خواهید منوی مگا را با قالب بلاکی ترکیب کنید، راهنمای توسعه قالب بلاکی حرفه‌ای را ببینید.

استفاده از Custom Fields برای داده غنی در منو

یکی از الگوهای قدرتمند، اتصال فیلد سفارشی به هر آیتم منو است. برای این کار، یک فیلد سفارشی روی آیتم منو ثبت می‌کنیم و در walker، آن را می‌خوانیم. راهنمای فیلد سفارشی در قالب و افزونه نقطه شروع مناسبی است.

پیاده‌سازی ریسپانسیو منوی مگا

منوی مگا در دسکتاپ و موبایل دو ساختار متفاوت نیاز دارد. در موبایل، منوی مگا معمولاً به یک آکاردئون ساده تبدیل می‌شود. پیاده‌سازی ریسپانسیو نیازمند منطق CSS و JS است که با ساختار walker هماهنگ باشد.

الگوی پیشنهادی این است که در walker، کلاس‌های مشترک برای موبایل و دسکتاپ قرار دهید و در CSS، رفتار را بر اساس Media Query تغییر دهید. راهنمای منوی موبایل و تجربه ناوبری مدرن را برای الگوهای عملی ببینید.

دکمه همبرگری و مدیریت وضعیت باز و بسته

دکمه همبرگری باید وضعیت منو را مدیریت کند. برای دسترسی‌پذیری، از aria-expanded و aria-controls استفاده کنید. بدون این ویژگی‌ها، کاربران Screen Reader نمی‌توانند وضعیت منو را درک کنند.

CSS و JS موردنیاز برای منوی مگا

ساختار CSS منوی مگا معمولاً شامل سه لایه است: لایه موقعیت‌دهی (positioning)، لایه چیدمان (layout) و لایه تعامل (interaction). برای چیدمان، CSS Grid یا Flexbox بسته به طراحی انتخاب می‌شود.

در JS، نیاز به مدیریت باز و بسته شدن زیرمنو، بستن با کلیک بیرون و پشتیبانی از صفحه‌کلید داریم. برای مطالعه بیشتر در مورد بهینه‌سازی CSS و JS، راهنمای افزودن فایل CSS سفارشی به قالب و افزودن فایل JS سفارشی به قالب را ببینید.

بارگذاری شرطی CSS و JS منو

برای کاهش حجم صفحات، توصیه می‌کنم CSS و JS منوی مگا فقط در صفحاتی که منو ظاهر می‌شود، بارگذاری شود. راهنمای بارگذاری شرطی CSS و JS را مطالعه کنید.

تست و دیباگ منوی مگا

تست منوی مگا باید در سه سطح انجام شود: سطح ساختار HTML، سطح رفتار JS و سطح ریسپانسیو. برای سطح ساختار، می‌توانید از DevTools و اعتبارسنجی W3C استفاده کنید. برای سطح رفتار، تست خودکار با Playwright یا Cypress توصیه می‌شود.

در سطح ریسپانسیو، تست روی چند دستگاه و مرورگر ضروری است. برای مطالعه بیشتر در مورد تست قالب، راهنمای تست E2E وردپرس با Playwright را ببینید.

اشتباهات رایج در تست منوی مگا

اشتباه اول، تست فقط روی دسکتاپ. اشتباه دوم، نادیده گرفتن دسترسی‌پذیری. اشتباه سوم، نبود تست با محتوای واقعی. اشتباه چهارم، نبود تست با تعداد آیتم زیاد. اشتباه پنجم، نبود تست با نام‌های طولانی.

ملاحظات عملکرد و کش در منوی مگا

منوی مگا در هر بار بارگذاری صفحه، ساختار HTML آن تولید می‌شود. اگر تعداد آیتم‌ها زیاد باشد یا فیلدهای سفارشی متعدد داشته باشد، می‌تواند زمان رندر را افزایش دهد. استفاده از Object Cache یا Transient برای کش خروجی منو توصیه می‌شود.

برای مطالعه بیشتر، راهنمای Object Cache در وردپرس را ببینید. همچنین Transients API نقطه شروع خوبی برای کش خروجی منو است.

کش سمت سرور و Invalidation

کش سمت سرور منو باید هنگام تغییر ساختار یا محتوای منو، Invalidate شود. این کار با اتصال به هوک‌های wp_update_nav_menu و wp_delete_nav_menu انجام می‌شود.

امنیت و دسترسی‌پذیری در منوی مگا

در walker سفارشی، همه مقادیر باید Escape شوند. برای URL از esc_url، برای عنوان از esc_html و برای Attributes از esc_attr استفاده کنید. نادیده گرفتن این قواعد، درِ ورود XSS را باز می‌کند.

برای مطالعه بیشتر، راهنمای Escape کردن خروجی برای جلوگیری از XSS را ببینید. همچنین مفهوم Mega Menu را در ویکی‌پدیا مرور کنید.

دسترسی‌پذیری و WCAG در منوی مگا

منوی مگا باید با صفحه‌کلید قابل ناوبری باشد، از ARIA استفاده کند و ترتیب Tab منطقی داشته باشد. راهنمای ARIA در وردپرس و ناوبری با صفحه‌کلید را مطالعه کنید.

پرسش‌های پرتکرار درباره Custom Nav Walker

آیا Custom Nav Walker برای هر پروژه‌ای ضروری است؟

خیر، فقط برای پروژه‌هایی که ساختار منوی پیش‌فرض وردپرس کافی نیست، مثل منوی مگا، منوی چندستونه یا منو با محتوای سفارشی.

تفاوت Walker_Nav_Menu و Walker_Page چیست؟

Walker_Nav_Menu برای منوهای ثبت‌شده با register_nav_menus است، Walker_Page برای فهرست برگه‌ها.

آیا walker سفارشی روی عملکرد سایت اثر دارد؟

در حالت عادی خیر، اما با تعداد زیاد آیتم یا فیلد سفارشی، کش لازم است.

آیا می‌توان walker سفارشی را در قالب بلاکی استفاده کرد؟

بله، اما Navigation Editor در قالب‌های بلاکی رفتار متفاوتی دارد و ممکن است نیاز به رویکرد دیگری داشته باشد.

آیا walker سفارشی با چند زبان سازگار است؟

بله، اگر از توابع ترجمه وردپرس استفاده کنید، با WPML و Polylang کار می‌کند.

نتیجه و مسیر ادامه

Custom Nav Walker ابزار اصلی ساخت منوی مگا حرفه‌ای در وردپرس است. کلید موفقیت، درک درست متدهای Walker_Nav_Menu، طراحی ساختار منطقی برای سطوح مختلف و رعایت اصول امنیت و دسترسی‌پذیری است. اگر این لایه‌ها از ابتدا با دقت ساخته شوند، منوی مگا در طول عمر پروژه پایدار باقی می‌ماند.

پیشنهاد می‌کنم مسیر یادگیری را با کلاس Walker سفارشی در وردپرس ادامه دهید و سپس ساخت منو مگا در قالب را به‌عنوان راهنمای عملی مطالعه کنید.

اگر روی پروژه واقعی خود منوی مگا پیاده کرده‌اید، برایم جالب است بدانید کدام بخش — طراحی walker یا مدیریت ریسپانسیو — بیشترین زمان را از شما گرفته است. تجربه خودتان را در دیدگاه‌ها بنویسید.