Función @menu para Blogger

Función utilitaria diseñada para generar menús desplegables multinivel de forma nativa en Blogger a partir de las listas de enlaces proporcionadas por el widget Páginas (PageList). Permite estructurar submenús limpios y jerárquicos interpretando prefijos de texto, reduciendo drásticamente la dependencia de scripts complejos en el cliente.

Esta función forma parte del proyecto Hamlet. Puedes revisar el código fuente en GitHub.

Definición de la función

<b:includable id='@menu'>
  <b:comment>
  Create a menu from a list of links
  @author zkreations
  @param {array} links - Array of links
  @param {string} [prefix="__"] - Prefix for filter links
  @param {string} [id] - Id for the menu
  @param {string} [class] - Class for the menu
  @param {String} [itemClass] - Base class for the items (li)
  @param {String} [subnavClass] - Class for the nested submenus (ul)
  @param {String} [linkClass] - Base class for the anchors (a)
  @param {String} [hereClass] - State class when the link matches the current URL
  @param {String} [externalClass] - State class for external references
  </b:comment>
  <b:with value='data:prefix ?: "__"' var='prefix'>
    <b:if cond='data:links[0].title contains data:prefix'>
      <b:comment render='true'>The first element can't have the prefix</b:comment>
    <b:elseif cond='data:links any (l => l.title + 0 == l.title)'/>
      <b:comment render='true'>The element can't be a number</b:comment>
    <b:elseif cond='!data:links.any'/>
      <b:comment render='true'>The array can't be empty</b:comment>
    <b:else/>
      <ul class='nav'>
        <b:class cond='data:class' expr:name='data:class'/>
        <b:attr name='id' expr:value='data:id'/>
        <b:loop index='i' values='data:links' var='link'>
          <b:with value='{
            current: (data:link.title contains data:prefix),
            next: (data:links[data:i + 1].title contains data:prefix)
          }' var='state'>
            <b:with value='{
              start: (data:state.next and !data:state.current),
              end: (!data:state.next and data:state.current)
            }' var='menu'>
              <b:eval expr='"<li class=\"nav-item " + (data:menu.start ? "has-subnav " : "") + data:itemClass + "\">"'/>
              <a class='nav-link' expr:href='data:link.href'>
                <b:class cond='data:linkClass' expr:name='data:linkClass'/>
                <b:if cond='data:link.isCurrentPage or data:view.url.canonical == data:link.href'>
                  <b:class name='is-here'/>
                  <b:class cond='data:hereClass' expr:name='data:hereClass'/>
                </b:if>
                <b:if cond='data:link.href contains "?ref="'>
                  <b:class name='is-external'/>
                  <b:class cond='data:externalClass' expr:name='data:externalClass'/>
                  <b:attr name='target' value='_blank'/>
                  <b:attr name='rel' value='noopener noreferrer'/>
                </b:if>
                <data:link.title/>
              </a>
              <b:eval expr='data:menu.end ? "</li></ul>" : ""'/>
              <b:eval expr='data:menu.start ? "<ul class=\"nav-subnav " + data:subnavClass + " \">" : "</li>"'/>
            </b:with>
          </b:with>
        </b:loop>
      </ul>
    </b:if>
  </b:with>
</b:includable>

Especificación de Parámetros

Parámetro Tipo Descripción
links array Enlaces del widget PageList. Obligatorio.
prefix string Prefijo para los subelementos. Por defecto "__".
id string Valor del atributo id del <ul>.
class string Clases CSS de la lista principal.
itemClass string Clase base de los elementos <li>.
subnavClass string Clases CSS de las listas anidadas.
linkClass string Clase base de los enlaces <a>.
hereClass string Clase del enlace activo.
externalClass string Clase para enlaces externos.

Modo de uso

Para implementar esta función, añade el fragmento en los marcados predeterminados (<b:defaultmarkups type='Common'>) de tu tema.

links

Recibe la matriz nativa de un widget de tipo PageList. La función procesará la colección iterando sus elementos y evaluando la precedencia de los prefijos de cadena.

<b:widget id='PageList1' title='Menú' type='PageList'>
  <b:includable id='main'>
    <b:include name='@menu' data='{ links: data:links }'/>
  </b:includable>
</b:widget>
<ul class='nav'>
  <li class='nav-item'><a class='nav-link' href='/'>Inicio</a></li>
</ul>

prefix

Permite personalizar el token o separador que indica el anidamiento dentro de los títulos de enlaces configurados en el panel de Blogger.

<b:include data='{ links: data:links, prefix: "---" }' name='@menu'/>
<!-- Interpreta elementos con '---' como submenús -->

id

Asigna un identificador único al elemento raíz del menú principal para su manipulación estructural mediante estilos o scripts globales.

<b:include data='{ links: data:links, id: "primary-nav" }' name='@menu'/>
<ul class='nav' id='primary-nav'></ul>

class

Permite inyectar nombres de clase personalizados sobre el contenedor padre del menú.

<b:include data='{ links: data:links, class: "menu-flex" }' name='@menu'/>
<ul class='nav menu-flex'></ul>

itemClass

Agrega clases adicionales a todas las etiquetas de elemento de lista (<li>) del primer nivel y subniveles de navegación.

<b:include data='{ links: data:links, itemClass: "transition-all" }' name='@menu'/>
<li class='nav-item transition-all'></li>

subnavClass

Aplica clases CSS personalizadas de manera exclusiva a los contenedores anidados (<ul>) que representan los menús desplegables de segundo nivel.

<b:include data='{ links: data:links, subnavClass: "dropdown-shadow" }' name='@menu'/>
<ul class='nav-subnav dropdown-shadow'></ul>

linkClass

Permite expandir o modificar las clases base aplicadas a todas las etiquetas de enlace (<a>) presentes en la navegación.

<b:include data='{ links: data:links, linkClass: "text-bold" }' name='@menu'/>
<a class='nav-link text-bold' href='#'></a>

hereClass

Inyecta una clase CSS de estado adicional cuando la dirección del enlace coincide plenamente con la página activa que está visualizando el usuario en ese instante.

<b:include data='{ links: data:links, hereClass: "active-item" }' name='@menu'/>
<a class='nav-link is-here active-item' href='#'></a>

externalClass

Asigna un nombre de clase personalizado al elemento si su atributo de enlace incluye parámetros de redirección externa (?ref=), agregando además los atributos de seguridad correspondientes.

<b:include data='{ links: data:links, externalClass: "ext-link" }' name='@menu'/>
<a class='nav-link is-external ext-link' href='?ref=example' target='_blank' rel='noopener noreferrer'></a>

Observaciones

Al renderizar las ramificaciones del árbol de navegación, los caracteres asignados como prefijo permanecerán legibles dentro del texto del hipervínculo en el HTML generado. Para removerlos de forma elegante en la interfaz de usuario final, inyecta JavaScript como por ejemplo:

document.querySelectorAll('.nav-subnav .nav-link').forEach(link => {
  link.textContent = link.textContent.trim().replace(/^_+/, '')
})