Resaltando la Clase Ancestral wp_nav_menu() sin Elementos Hijos en la Estructura de Navegación

18 oct 2010, 21:42:13
Vistas: 31K
Votos: 32

(Nota de los moderadores: Originalmente titulado "Clase ancestral wp_nav_menu sin hijos en la estructura de navegación")

Tengo un wp_nav_menu en mi encabezado que tiene tres páginas. Cuando estoy en una de esas páginas, el li que contiene esa página en el menú obtiene la clase .current_page_item. Estas tres páginas tienen plantillas, y estas plantillas contienen consultas personalizadas para obtener todas las entradas de un cierto tipo de contenido. En efecto, los "hijos" percibidos de esta página de nivel superior no son realmente hijos, son simplemente de un tipo de contenido que he asociado con esa página de nivel superior usando una plantilla.

Me gustaría que los elementos del menú de nivel superior obtengan una clase 'current-ancestor' cuando el usuario está navegando por una página individual de un tipo de entrada específico, nuevamente, asociado con esa página solo en una consulta personalizada en el archivo de plantilla.

Espero que tenga sentido - si no, ¡háganme saber dónde los perdí! Agradezco mucho cualquier ayuda.

--Editado para especificaciones: Por ejemplo, tengo una página estática llamada Talleres que está usando una plantilla. Su slug es talleres. La plantilla tiene una función personalizada get_posts y un bucle dentro de ella, que extrae y muestra todas las entradas de un tipo de contenido personalizado llamado talleres. Si hago clic en el título de uno de estos talleres, me lleva al contenido completo de esa pieza de contenido. La estructura de enlaces permanentes del tipo de entrada personalizada está configurada como talleres/nombre-entrada, por lo que según lo ve el usuario, estas piezas de contenido son hijas de la página Talleres, cuando en realidad son todas de un tipo de contenido pero no están relacionadas con la página. Es esa brecha la que necesito cerrar efectivamente en el menú, resaltando el elemento del menú 'Talleres' cuando se navega por contenido de tipo 'taller'.

Nuevamente, espero que tenga sentido, ¡creo que dije 'taller' más de 20 veces en un párrafo!

3
Comentarios

@Gavin - ¿Podrías incluir algunos detalles más sobre lo que intentas lograr? Es más fácil escribir una respuesta en términos concretos que si lo intentamos de manera abstracta. También sería útil si pudieras explicar la estructura de URLs relacionada con esto.

MikeSchinkel MikeSchinkel
18 oct 2010 23:14:46

@Gavin - Eso ayuda. Entonces tu opción de menú de nivel superior es una lista de talleres en "Talleres" con una ruta /workshops/ y cuando un usuario está en una página de taller (ej. /workshops/ejemplo-taller/) quieres que el ítem de menú "Talleres" tenga asignada la clase current_page_item, ¿correcto?

MikeSchinkel MikeSchinkel
19 oct 2010 07:37:19

wp_nav_menu() expone la clase current-menu-ancestor

Daniel Sachs Daniel Sachs
14 jun 2011 00:06:13
Todas las respuestas a la pregunta 7
4
30

Hay una solución más sencilla. Olvídate de crear páginas para cada tipo de publicación solo para tener elementos de navegación, porque como has aprendido, WordPress no tiene forma de reconocer que los tipos personalizados que estás viendo están relacionados con esa página.

En su lugar, crea un enlace personalizado en Apariencia->Menús. Simplemente coloca la URL que devolverá tu tipo personalizado y dale una etiqueta, luego presiona "Añadir al menú".

http://example.com/talleres/

o para enlaces no amigables:

http://example.com/?post_type=talleres

esto solo creará un botón de navegación que muestra todas las publicaciones con ese tipo de publicación personalizado, y también agregará la clase current-menu-item cuando hayas hecho clic en ese elemento de navegación - pero aún no agregará la clase de navegación en ninguna URL que no sea esta

Luego, una vez creado, ve a la configuración de ese nuevo elemento e ingresa el slug del tipo de publicación personalizado en el campo "Atributo del título" (también podrías usar el campo de descripción, pero ese está oculto en las opciones de la pantalla de administración por defecto).

Ahora, necesitas engancharte al filtro nav_menu_css_class (que se ejecuta para cada elemento de navegación) y verificar si el contenido que se está viendo es del tipo de publicación indicado en tu elemento de navegación personalizado:

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2 );
function current_type_nav_class($classes, $item) {
    $post_type = get_query_var('post_type');
    if ($item->attr_title != '' && $item->attr_title == $post_type) {
        array_push($classes, 'current-menu-item');
    };
    return $classes;
}

En este caso, vamos a verificar que el contenido del campo Atributo del título no esté vacío y si coincide con el post_type que se está consultando actualmente. Si es así, agregamos la clase current-menu-item a su array de clases, luego devolvemos el array modificado.

Podrías modificar esto para simplemente coincidir con el título del elemento de navegación, pero si por alguna razón deseas titular el elemento de navegación de manera diferente al slug simple del tipo de publicación, usar el campo Atributo del título o Descripción te da esa flexibilidad.

Ahora, cada vez que estés viendo un elemento individual (o probablemente incluso listados de archivo) de un tipo de publicación que coincida con un elemento del menú de navegación, a ese elemento se le asignará la clase CSS current-menu-item para que tu resaltado funcione.

No se necesitan páginas ni plantillas de página ;-) La consulta de URL se encarga de obtener las publicaciones correctas. Tu plantilla de loop se encarga de mostrar la salida de la consulta. Esta función se encarga de reconocer lo que se está mostrando y agregar la clase CSS.

BONUS

Incluso puedes automatizar el proceso usando wp_update_nav_menu_item, haciendo que los elementos del menú se generen automáticamente para todos tus tipos de publicaciones. Para este ejemplo, primero necesitarías haber recuperado el $menu_id del menú de navegación al que deseas agregar estos elementos.

$types = get_post_types( array( 'exclude_from_search' => false, '_builtin' => false  ), 'objects' );
foreach ($types as $type) {
    wp_update_nav_menu_item( $menu_id, 0, array(
        'menu-item-type' => 'custom',
        'menu-item-title' => $type->labels->name,
        'menu-item-url' => get_bloginfo('url') . '/?post_type=' . $type->rewrite['slug'],
        'menu-item-attr-title' => $type->rewrite['slug'],
        'menu-item-status' => 'publish'
        )
    );
}
19 oct 2010 08:25:38
Comentarios

¡Eso es lo que necesitaba! Estoy usando plantillas de página solo porque los diseños son bastante complejos para esas páginas y no solo listan las páginas, pero aún puedo utilizar ese filtro que proporcionaste para verificar el ID de la página. La naturaleza de este tema es que las opciones del tema te permiten emparejar páginas ('home' es esta página, 'about' es esta página, etc.), así que eso debería funcionar perfectamente. ¡Gracias por la asistencia (increíblemente detallada)!

Gavin Gavin
19 oct 2010 14:59:47

Tuve que eliminar el current_page_parent del elemento de navegación que era mi blog, pero por lo demás funcionó. Gracias.

Philipp Kyeck Philipp Kyeck
10 oct 2011 17:58:54

Esto no funcionaba para mí, ya que $item->attr_title extraía el TÍTULO, y yo había escrito el título en mayúsculas. Así que cambié el atributo a $item->post_name y ahora funciona bien para mí.

honk31 honk31
29 nov 2013 19:42:24

Intenté hacer que el código funcione para mi tema, pero no puedo conseguirlo. No se aplicará ninguna clase a mi elemento principal en el menú, cuando estoy en el tipo de publicación personalizado portfolio. He usado el código anterior. ¿Cuál puede ser el problema?

Casper Casper
25 mar 2014 17:51:18
0

en lugar de usar

$post_type = get_query_var('post_type');

Podrías probar:

$post_type = get_post_type();

A veces el tipo de publicación no está establecido en la variable de consulta. Este es el caso para el tipo de publicación predeterminado "post", por lo que si deseas resaltar una publicación que se mostró desde una página de listado, necesitarás usar esto. get_query_var() solo devuelve una cadena vacía para tipos de publicaciones que no son personalizados.

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2 );
function current_type_nav_class($classes, $item) {
    $post_type = get_post_type();
    if ($item->attr_title != '' && $item->attr_title == $post_type) {
        array_push($classes, 'current-menu-item');
    };
    return $classes;
}
17 mar 2011 16:18:20
0

@Somatic - ¡eso es fantástico! Modifiqué un poco tu código para que también funcione con una Taxonomía específica (que estoy usando solo para el post_type relacionado). La idea es usar el atributo Title del ítem del menú para almacenar tanto el nombre del post_type COMO el nombre de la taxonomía, separados por punto y coma, y luego procesados por la función.

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2 );
function current_type_nav_class($classes, $item) {

    # Obtener variables de consulta
    $post_type = get_query_var('post_type');  
    $taxonomy = get_query_var('taxonomy');

    # Obtener y procesar atributo Title del ítem del menú
    $title = $item->attr_title; // atributo Title del ítem del menú, como post_type;taxonomy
    $title_array = explode(";", $title);
    $title_posttype = $title_array[0];
    $title_taxonomy = $title_array[1];

    # Agregar clase si es necesario
    if ($title != '' && ($title_posttype == $post_type || $title_taxonomy == $taxonomy)) {
        array_push($classes, 'current-menu-item');
    };
    return $classes;
}
13 abr 2011 03:12:54
0

Aquí está mi solución si quieres trabajar con wp_list_pages.

Añade esto en tu functions.php

add_filter('page_css_class', 'my_page_css_class', 10, 2);
function my_page_css_class($css_class, $page){
    $post_type = get_post_type();
    if($post_type != "page"){
        $parent_page = get_option('page_for_custom_post_type-'.$post_type);
        if($page->ID == $parent_page)
            $css_class[] = 'current_page_parent';
    }
    return $css_class;
}

Ahora solo añade en la tabla wp_options una nueva fila con un option_name de page_for_custom_post_type-xxxx y un option_value con el ID de la página que quieras conectar.

Quizás hayas notado que ya existe una opción llamada page_for_posts. Si solo tienes 1 tipo de entrada personalizada, puedes configurar tu página en /wp-admin/options-reading.php en el menú desplegable y la navegación establecerá correctamente el current_page.

Creo que el núcleo de WordPress debería extender esta sección con un menú desplegable para cada tipo de entrada registrado.

13 jun 2011 21:58:05
2

Decidí continuar usando páginas y utilizar el nombre de la plantilla de página como una clase en el elemento de navegación. Esto me permite evitar saturar el atributo title, algo que no me gustaba de otras soluciones.

add_filter('nav_menu_css_class', 'mbudm_add_page_type_to_menu', 10, 2 );
// Si un elemento del menú es una página, añade el nombre de la plantilla como clase CSS
function mbudm_add_page_type_to_menu($classes, $item) {
    if($item->object == 'page'){
        $template_name = get_post_meta( $item->object_id, '_wp_page_template', true );
        $new_class =str_replace(".php","",$template_name);
        array_push($classes, $new_class);
        return $classes;
    }   
}

También tengo clases para el body añadidas en header.php

<body <?php body_class(); ?>>

Finalmente, esta solución requiere CSS adicional para aplicar el estado seleccionado/activo a los elementos del menú de navegación. Lo uso para mostrar archivos de taxonomía y tipos de contenido personalizado relacionados con la página como hijos de esta página:

/* estados seleccionados - incluye subpáginas para todo lo relacionado con productos */
#nav-main li.current-menu-item a,
body.single-mbudm_product #nav-main li.lp_products a,
body.tax-mbudm_product_category #nav-main li.lp_products a,
#nav-main li.current_page_parent a{color:#c00;}
15 oct 2011 05:28:44
Comentarios

Esto me dio el siguiente error:

Warning: join() [function.join]: Invalid arguments passed in /home/path/to/wp-includes/nav-menu-template.php on line 76

¿Alguna idea de qué pasó aquí?

Jeff K. Jeff K.
21 oct 2011 03:04:45

Ah, creo que entiendo lo que está pasando. Es porque estás devolviendo $classes dentro de la sentencia if. Simplemente moviendo return $classes afuera y después de ese if parece resolver el error anterior.

Jeff K. Jeff K.
21 oct 2011 04:14:08
0

@Somatic - ¡Excelente código! Hice un cambio por mi parte. Quería mantener el Atributo de Título para su propósito original, así que en su lugar coloqué el slug del Tipo de Entrada Personalizado en la Relación de Enlace (XFN) dentro de las propiedades avanzadas del menú que puedes activar en Opciones de Pantalla. Modifiqué

if ($item->attr_title != '' && $item->attr_title == $post_type) {

y lo cambié a

if ($item->xfn != '' && $item->xfn == $post_type) {
23 sept 2011 22:30:40
0

Buen trabajo Somatic.

Desafortunadamente, no entiendo cómo puedes listar tus tipos de posts personalizados en una página de la manera que explicas. Si no uso un page-portfolio.php y lo añado a una página, solo obtengo un error 404.

Si hago como Gavin, he modificado un poco tu función para también eliminar la clase "current_page_parent" de la página del blog así:

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2);
function current_type_nav_class($css_class, $item) {
$post_type = get_query_var('post_type');

if (get_post_type()=='portfolio') {
    $current_value = "current_page_parent"; 
    $css_class = array_filter($css_class, function ($element) use ($current_value) { return ($element != $current_value); } );
}

if ($item->attr_title != '' && $item->attr_title == $post_type) {       
    array_push($css_class, 'current_page_parent');
};
return $css_class;

}

10 dic 2010 14:36:55