jquery: agrega sistemáticamente un borde CSS diferente a cada A, incluso si están en divs secundarios

CorePress2024-01-16  11

Tengo un problema. Tengo un menú, que está estructurado de la siguiente manera:

<ul class="main-menu">
  <li class="menu-item" <a href="">Menu item 1</a>
  </li>
  <ul class="sub-menu">
    <li class="menu-item"><a href="">Submenu item 1</a></li>
    <li class="menu-item has-children"><a href="">Submenu item 2</a></li>
    <ul class="sub-menu">
      <li class="menu-item"><a href="">Sub-sub menu item 1</a></li>
      <li class="menu-item"><a href="">Sub-sub menu item 2</a></li>
    </ul>
    <li class="menu-item"><a href="">Submenu 3</a></li>
    <li class="menu-item has-children"><a href="">Submenu 4</a></li>
    <ul class="sub-menu">
      <li class="menu-item"><a href="">Sub-sub menu item 3</a></li>
      <li class="menu-item"><a href="">Sub-sub menu item 4</a></li>
    </ul>
  </ul>
  <li class="menu-item" <a href="">Menu item 2</a>
  </li>
</ul>

Necesito asignar sistemáticamente un nuevo color de borde a CADA "a" etiqueta en la lista, para crear un efecto de borde de arco iris (por lo que la primera "A" encontrada (elemento de menú 1) tiene un borde azul, la segunda "a" (elemento de submenú 1) tiene rojo, la tercera (elemento de submenú 1) tiene rosa, el cuarto (elemento 2 del submenú) tiene verde, etc.).

El problema es que, debido a que hay submenús dentro de los submenús, no puedo usar el psuedo :nth-child, porque no incluye niños en los recuentos. Así por ejemplo:

    ul li a:nth-child(1) {
       border: 1px solid blue;
    }

    ul li a:nth-child(2) {
       border: 1px solid pink;
    }

¿Existe alguna manera de revisar sistemáticamente cada sección "a" ¿Dentro de la clase del menú principal y agregar CSS diferente a cada elemento? De una manera a prueba de futuro, así que si el mEnu cambia (por ejemplo, se agrega un segundo elemento de menú de nivel superior o se agrega un elemento de submenú adicional), ¿entonces los colores siguen el mismo orden?

¡Idealmente a través de CSS, pero también abierto a ideas de jQuery!

Su HMTL no es válido/incorrecto. ul no puede tener otros ul como hijos directos.

- Paulie_D

19/03/2021 a las 16:27

Así es como Wordpress crea menús con submenús

Usuario2115227

19/03/2021 a las 16:30



------------------------------------

Si consideramos el hecho de que siempre tenemos una línea por enlace y cada línea contendrá un enlace y se conoce la altura de una línea (o podemos arreglarla), entonces aquí hay una idea exclusiva de CSS:

ul.main-menu {
   --l:1.5em; /* our line-height */

   line-height:var(--l);
   position:relative; /* relative to main element */
   z-index:0;
}

ul.main-menu a {
  clip-path:inset(0); /* clip everything outside the link */
  color:#fff;
  padding:0 5px;
  display:inline-block;
}

ul.main-menu a::before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  /* your color array below */
  background:
    repeating-linear-gradient(0deg,
       red    0 calc(1*var(--l)),
       green  0 calc(2*var(--l)),
       blue   0 calc(3*var(--l)),
       purple 0 calc(4*var(--l)),
       pink   0 calc(5*var(--l)));
}
<ul class="main-menu">
  <li class="menu-item"> <a href="">Menu item 1</a></li>
  <li>
    <ul class="sub-menu">
      <li class="menu-item"><a href="">Submenu item 1</a></li>
      <li class="menu-item has-children"><a href="">Submenu item 2</a></li>
      <li>
        <ul class="sub-menu">
          <li class="menu-item"><a href="">Sub-sub menu item 1</a></li>
          <li class="menu-item"><a href="">Sub-sub menu item 2</a></li>
        </ul>
      </li>
      <li class="menu-item"><a href="">Submenu 3</a></li>
      <li class="menu-item has-children"><a href="">Submenu 4</a></li>
      <li>
        <ul class="sub-menu">
          <li class="menu-item"><a href="">Sub-sub menu item 3</a></li>
          <li class="menu-item"><a href="">Sub-sub menu item 4</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li class="menu-item"> <a href="">Menu item 2</a></li>
</ul>

Para tener borde, necesitamos un contenedor adicional:

ul.main-menu {
   --l:1.5em; /* our line-height */

   line-height:var(--l);
   position:relative;
  z-index:0;
}

ul.main-menu a {
  clip-path:inset(0);
  display:inline-block;
}
ul.main-menu a > span {
  display:block;
  padding:0 4px;
  background:linear-gradient(#fff 0 0) center/calc(100% - 6px) calc(100% - 6px) no-repeat;
}

ul.main-menu a::before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  background:
    repeating-linear-gradient(0deg,
       red    0 calc(1*var(--l)),
       green  0 calc(2*var(--l)),
       blue   0 calc(3*var(--l)),
       purple 0 calc(4*var(--l)),
       pink   0 calc(5*var(--l)));
}
<ul class="main-menu">
  <li class="menu-item"> <a href=""><span>Menu item 1</span></a></li>
  <li>
    <ul class="sub-menu">
      <li class="menu-item"><a href=""><span>Submenu item 1</span></a></li>
      <li class="menu-item has-children"><a href=""><span>Submenu item 2</span></a></li>
      <li>
        <ul class="sub-menu">
          <li class="menu-item"><a href=""><span>Sub-sub menu item 1</span></a></li>
          <li class="menu-item"><a href=""><span>Sub-sub menu item 2</span></a></li>
        </ul>
      </li>
      <li class="menu-item"><a href=""><span>Submenu 3</span></a></li>
      <li class="menu-item has-children"><a href=""><span>Submenu 4</span></a></li>
      <li>
        <ul class="sub-menu">
          <li class="menu-item"><a href=""><span>Sub-sub menu item 3</span></a></li>
          <li class="menu-item"><a href=""><span>Sub-sub menu item 4</span></a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li class="menu-item"> <a href=""><span>Menu item 2</span></a></li>
</ul>

Por supuesto, lo anterior depende de una gran cantidad de valores fijos, por lo que es posible que tengas que ajustar muchas cosas para tu código real:

2

Hay una delgada línea entre el genio y la locura; ¡Bien hecho!

-David Thomas

19/03/2021 a las 20:02

1

@DavidsaysreinstateMonica gracias, y creo que es 100% locura ;)

Temani Afif

19/03/2021 a las 20:05



------------------------------------

Hasta donde yo sé, esto no es posible actualmente solo con CSS. Si bien CSS puede seleccionar con precisión todos los elementos <a> elementos dentro de la lista con el selector ul.main-menu a, y es posible que podamos incrementar un contador con CSS, e incluso mostrar ese contador (en 2021 eso ha sido posible desde hace bastante tiempo):

ul.main-menu {
  counter-reset: anchorElementIndex;
}

ul.main-menu a::before {
  counter-increment: anchorElementIndex;
  content: " (" counter(anchorElementIndex) ") ";
}
<ul class="main-menu">
  <li class="menu-item"><a href="">Menu item 1</a>
  </li>
  <ul class="sub-menu">
    <li class="menu-item"><a href="">Submenu item 1</a></li>
    <li class="menu-item has-children"><a href="">Submenu item 2</a></li>
    <ul class="sub-menu">
      <li class="menu-item"><a href="">Sub-sub menu item 1</a></li>
      <li class="menu-item"><a href="">Sub-sub menu item 2</a></li>
    </ul>
    <li class="menu-item"><a href="">Submenu 3</a></li>
    <li class="menu-item has-children"><a href="">Submenu 4</a></li>
    <ul class="sub-menu">
      <li class="menu-item"><a href="">Sub-sub menu item 3</a></li>
      <li class="menu-item"><a href="">Sub-sub menu item 4</a></li>
    </ul>
  </ul>
  <li class="menu-item"><a href="">Menu item 2</a>
  </li>
</ul>

Todavía no hay manera de definir una matriz de colores en CSS a través de la cual podamos recorrer, porque desafortunadamente, si bien podemos seleccionar los elementos, no hay manera de iterar a través de ellos en función de su lugar dentro de la colección de elementos con los que coincide el selector. . Si fueran elementos hermanos entonces, de manera detallada, podríamos seleccionarlos:

*,
 ::before,
 ::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  counter-reset: anchorIndex;
}

a {
  background-color: red;
  color: black;
  padding: 0.5em;
  font-weight: 600;
}

a::before {
  counter-increment: anchorIndex;
  content: "(" counter(anchorIndex, decimal-leading-zero) ") ";
}

a:nth-child(7n + 1) {
  background-color: red;
}

a:nth-child(7n + 2) {
  background-color: orange;
}

a:nth-child(7n+3) {
  background-color: yellow;
}

a:nth-child(7n+4) {
  background-color: green;
}

a:nth-child(7n+5) {
  background-color: blue;
}

a:nth-child(7n+6) {
  background-color: indigo;
}

a:nth-child(7n+7) {
  background-color: violet;
}
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>
<a href="">link element</a>

Incluso si el <a> elementos en sí no eran hermanos, pero eran todos descendientes de un grupo de elementos hermanos comunes, aún podíamos seleccionar el grupo <a> elementos, seleccionando primero los hermanos (con una sintaxis similarmente detallada para seleccionar los hermanos, y usando un combinador descendiente (espacio en blanco) aún selecciona los elementos <a>:

*,
 ::before,
 ::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  counter-reset: anchorIndex;
  list-style-type: none;
}

a {
  color: black;
  font-weight: 600;
}

a::before {
  counter-increment: anchorIndex;
  content: "(" counter(anchorIndex, decimal-leading-zero) ") ";
}

li:nth-child(7n + 1) a {
  background-color: red;
}

li:nth-child(7n + 2) a {
  background-color: orange;
}

li:nth-child(7n+3) a {
  background-color: yellow;
}

li:nth-child(7n+4) a {
  background-color: green;
}

li:nth-child(7n+5) a {
  background-color: blue;
}

li:nth-child(7n+6) a {
  background-color: indigo;
}

li:nth-child(7n+7) a {
  background-color: violet;
}
<ul>
  <li>
    <div>
      <span>
    <em><a href="">Link </a></em>
  </span>
    </div>
  </li>
  <li>
    <a href="">Link</a>
  </li>
  <li>
    <p>
      <a href="">Link</a>
    </p>
  </li>
  <li>
    <a href="">Link</a>
  </li>
  <li>
    <div>
      <p><a href="">Link</a></p>
    </div>
  </li>
  <li>
    <div>
      <span>
    <em><a href="">Link </a></em>
  </span>
    </div>
  </li>
  <li>
    <a href="">Link</a>
  </li>
  <li>
    <p>
      <a href="">Link</a>
    </p>
  </li>
  <li>
    <a href="">Link</a>
  </li>
  <li>
    <div>
      <p><a href="">Link</a></p>
    </div>
  </li>
  <li>
    <article><a href="">Link</a></article>
  </li>
</ul>

Dicho esto, la única forma que creo que es posible es usar JavaScript o una de sus bibliotecas. En JavaScript nativo:

// initialising the array of colours:
const rainbow = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];

// selecting all the <a> elements on the page:
document.querySelectorAll('a')
  // iterating over the NodeList returned by document.querySelectorAll()
  // with NodeList.prototype.forEach():
  .forEach(
    // using an Arrow function, passing in the arguments of:
    // 'a': a reference to the current Node (the <a>) of the
    // NodeList over which we're iterating,
    // 'i': the index of the current Node in the NodeList:
    (a, i) => {
      // here we retrieve the index of the appropriate colour in the
      // Array of colours, using the remainder operator:
      let index = i % rainbow.length;

      // setting the background-colour of the current <a> element
      // to the appropriate colour:
      a.style.backgroundColor = rainbow[index];
    });
*,
 ::before,
 ::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

ul.main-menu {
  width: 50vw;
  overflow: hidden;
}

ul,
li {
  margin-left: 0.5em;
}

a {
  color: #000;
  font-weight: 700;
  display: block;
}
<ul class="main-menu">
  <li class="menu-item"><a href="">Menu item 1</a>
  </li>
  <ul class="sub-menu">
    <li class="menu-item">
      <a href="">Submenu item 1</a>
    </li>
    <li class="menu-item has-children">
      <a href="">Submenu item 2</a>
    </li>
    <ul class="sub-menu">
      <li class="menu-item">
        <a href="">Sub-sub menu item 1</a>
      </li>
      <li class="menu-item">
        <a href="">Sub-sub menu item 2</a>
      </li>
    </ul>
    <li class="menu-item">
      <a href="">Submenu 3</a>
    </li>
    <li class="menu-item has-children">
      <a href="">Submenu 4</a>
    </li>
    <ul class="sub-menu">
      <li class="menu-item">
        <a href="">Sub-sub menu item 3</a>
      </li>
      <li class="menu-item">
        <a href="">Sub-sub menu item 4</a>
      </li>
    </ul>
  </ul>
  <li class="menu-item">
    <a href="">Menu item 2</a>
  </li>
</ul>

Lo mismo es, por supuesto, bastante sencillo usando jQuery si así lo deseas:

// initialising the array of colours:
const rainbow = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];

// selecting all the <a> elements, and then chaining the css()
// method to set the background-colour of each <a> element:
$('a').css('background-color', function(i) {
  // as the css() method internally iterates over the
  // collection an index - 'i' - is made available to
  // the anonymous function; here we determine the
  // index of the color Array (as in the native JavaScript
  // approach):
  let index = i % rainbow.length;

  // returning the appropriate colour to the css() method:
  return rainbow[index];
});
*,
 ::before,
 ::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

ul.main-menu {
  width: 50vw;
  overflow: hidden;
}

ul,
li {
  margin-left: 0.5em;
}

a {
  color: #000;
  font-weight: 700;
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="main-menu">
  <li class="menu-item"><a href="">Menu item 1</a>
  </li>
  <ul class="sub-menu">
    <li class="menu-item">
      <a href="">Submenu item 1</a>
    </li>
    <li class="menu-item has-children">
      <a href="">Submenu item 2</a>
    </li>
    <ul class="sub-menu">
      <li class="menu-item">
        <a href="">Sub-sub menu item 1</a>
      </li>
      <li class="menu-item">
        <a href="">Sub-sub menu item 2</a>
      </li>
    </ul>
    <li class="menu-item">
      <a href="">Submenu 3</a>
    </li>
    <li class="menu-item has-children">
      <a href="">Submenu 4</a>
    </li>
    <ul class="sub-menu">
      <li class="menu-item">
        <a href="">Sub-sub menu item 3</a>
      </li>
      <li class="menu-item">
        <a href="">Sub-sub menu item 4</a>
      </li>
    </ul>
  </ul>
  <li class="menu-item">
    <a href="">Menu item 2</a>
  </li>
</ul>

Referencias:

JavaScript: Función de flecha. NodeList.prototype.forEach(). Operador resto (%). jQuery: en().

4

Aún podemos hacer algo de magia con CSS :) (incluso si es un poco complicado y no es fácilmente escalable)

Temani Afif

19/03/2021 a las 19:57

1

Honestamente, usted y, aunque no he visto su presencia en un par de años, Boltclock son las razones por las que especifiqué "hasta donde yo sé, no actualmente"."En realidad es posible". :)

-David Thomas

19/03/2021 a las 20:01

Esta es una muy buena respuesta, ¡gracias! Olvidé mencionar en mi OP que NO debería incluir los elementos del menú de nivel superior, solo los de los submenús. ¿Es eso posible? (con el ejemplo de jQuery en particular)

Usuario2115227

20 de marzo de 2021 a las 6:17

Ignora eso, me di cuenta de que acabo de agregar .submenú a. ¡Muchas gracias!

Usuario2115227

20 de marzo de 2021 a las 6:52

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare