Menu desplegable con el objeto links

Una de las características más solicitadas es la posibilidad de crear un menú desplegable en Blogger y los que desarrollamos para Blogger sabemos lo difícil que es conseguir esto sin abusar de javascript.

Es por eso que hoy te comparto el método que ideé para crear un menú desplegable que llevo aplicando en mis trabajos desde hace años, y mas recientemente en mis nuevos temas para Blogger.

Situación

En Blogger tenemos el widget Páginas que permite al usuario configurar una lista de enlaces, sin embargo, este widget solo permite crear un menú de primer nivel.

Antes para permitir esta característica teníamos que usar el widget HTML para que el usuario cree el menú manualmente, pero esto es muy poco práctico y con frecuencia los usuarios cometían errores.

Función en Blogger

Para solucionar esto, he creado la siguiente inclusión que permite crear un menú desplegable a partir de una matriz de enlaces, misma que debe provenir de un widget Páginas.

<b:includable id='@menu'>
  <b:comment><!--
  // @param {array} links - Array of links
  // @param {string} [prefix="__"] - Prefix for filter links
  // @param {string} [id] - Identificador único
  // @param {string} [class] - Clases adicionales
  --></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 is 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='hasPrefix'>
          <b:with value='{
              start: (data:hasPrefix.next and !data:hasPrefix.current),
              end: (!data:hasPrefix.next and data:hasPrefix.current)
            }' var='menu'>
            &lt;li class="nav-item<b:if cond='data:menu.start'> has-subnav</b:if>"&gt;
            <a class='nav-link' expr:href='data:link.href'><data:link.title/></a>
            <b:if cond='data:menu.start'>
              &lt;ul class="nav-subnav"&gt;
            </b:if>
            <b:if cond='data:menu.end'>&lt;/li&gt;&lt;/ul&gt;</b:if>
            <b:if cond='!data:menu.start'>&lt;/li&gt;</b:if>
          </b:with>
          </b:with>
        </b:loop>
      </ul>
    </b:if>
  </b:with>
</b:includable>

Explicación

Para empezar, realizamos algunas comprobaciones importantes, asi evitamos errores graves en Blogger Lo primero es asegurarse que el primer elemento del menu no tenga prefijo. Luego comprobamos que el título sea texto y no un número, porque de lo contrario obtendríamos un error al evaluarlo con el operador contains. Por último verificamos que la matriz no esté vacía.

La función recibe 4 parámetros, los cuales son: prefix, links, id y class. Siendo solo links obligatorio y debe ser una matriz de enlaces. Los otros parámetros son opcionales.

Se recorre la matriz y se crea un menú de primer nivel, pero si el título del enlace contiene el prefijo configurado __, se crea un nuevo menú, que va a contener todos los enlaces siguientes que tengan el mismo prefijo. Si uno de los elementos siguientes no tiene el prefijo, el menú se cierra:

Para que esto sea posible, he aprovechado la naturaleza de la sintaxis de Blogger, en donde cualquier etiqueta HTML expresada como string, es interpretada como HTML. Puedes aprender más de esta función en mi tema para desarrolladores Hamlet.

Modo de uso

Primero agrega la inclusión como un marcado predeterminado tipo "Common". Ahora podrás incluir la función en widgets tipo Páginas como se muestra a continuación.

Matriz de enlaces

La matriz corresponde al dato tipo array de Blogger que forma parte del widget de páginas. Este dato se puede obtener dentro de cualquier instancia del widget:

<b:widget id='PageList1'  title='Menu desplegable' type='PageList'>
  <b:includable id='main'>
    <b:include name='@menu' data='{ links: data:links }'/>
  </b:includable>
</b:widget>

Prefijo

El prefijo es el texto que se usa al principio del título para identificar los enlaces que pertenecen a un menú desplegable. Este valor es opcional y por defecto son dos guiones bajos __ cuando no se especifica.

<b:include data='{
    links: data:links,
    prefix: "---"
  }' name='@menu'/>

Identificador y clases

Estos valores se agregan al contenedor principal del menú.

<b:include data='{
    links: data:links,
    id: "test",
    class: "ejemplo"
  }' name='@menu'/>

Observaciones

Es importante tener en cuenta que al generar los menus de esta forma, el prefijo también se incluye en el título del enlace, por lo cual recomiendo eliminarlo usando javascript:

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

Conclusión

Con esta función más unos cuantos estilos CSS, podemos crear un menú desplegable. Y lo mejor de todo es que el usuario puede configurar el menú de forma sencilla usando el widget Páginas. Por supuesto te motivo a que realices todas las modificaciones que desees para adaptar esta inclusion a tu proyecto. Tambien puedes experimentar con la plantilla de pruebas que encontrarás en los datos adjuntos.

Si eres desarrollador y esto te ha sido útil, me ayudaría mucho que me menciones en tus proyectos o bien que compartas este artículo con tus seguidores, ya que no solo me motivas a seguir compartiendo más, sino que también me ayudas a crecer.