Cómo deshabilitar enlaces | Programar Plus

El tema de la desactivación de enlaces apareció en mi trabajo el otro día. De alguna manera, el año pasado se agregó un estilo de ancla “discapacitado” a nuestros estilos de tipografía cuando no estaba mirando. Sin embargo, hay un problema: no hay una forma real de deshabilitar un <a> enlace (con un href atributo) en HTML. Sin mencionar, ¿por qué querrías hacerlo? Los enlaces son la base de la web.

En cierto momento, parecía que mis compañeros de trabajo no iban a aceptar este hecho, así que comencé a pensar en cómo se podría lograr. Sabiendo que se necesitaría mucho, quería demostrar que no valía la pena el esfuerzo y el código para respaldar una interacción tan poco convencional, pero temía que, al mostrar que se podía hacer, ignorarían todas mis advertencias y solo usarían mi ejemplo como prueba de que estaba bien. Esto aún no me ha afectado del todo, pero pensé que podríamos seguir con mi investigación.

Lo primero es lo primero:

Simplemente no lo hagas.

Un enlace desactivado no es un enlace, es solo texto. Necesita repensar su diseño si requiere deshabilitar un enlace.

Bootstrap tiene ejemplos de aplicación de .disabled class para anclar etiquetas, y las odio por ello. Al menos mencionan que la clase solo proporciona un estilo para discapacitados, pero esto es engañoso. Debe hacer algo más que hacer que un enlace parezca deshabilitado si realmente desea deshabilitarlo.

Manera segura: eliminar el href

Si ha decidido que va a ignorar mi advertencia y continuar con la desactivación de un enlace, entonces la eliminación de la href atributo es la mejor manera que conozco.

Directamente de la especificación oficial de hipervínculo:

El href atributo en a y area no se requieren elementos; cuando esos elementos no tienen href atributos que no crean hipervínculos.

Una definición más fácil de entender de MDN:

Este atributo se puede omitir (a partir de HTML5) para crear un enlace de marcador de posición. Un vínculo de marcador de posición se parece a un hipervínculo tradicional, pero no conduce a ninguna parte.

Aquí está el código JavaScript básico para configurar y eliminar el href atributo:

/* 
 * Use your preferred method of targeting a link
 *
 * document.getElementById('MyLink');
 * document.querySelector('.link-class');
 * document.querySelector('[href="https://unfetteredthoughts.net"]');
 */
// "Disable" link by removing the href property
link.href="";
// Enable link by setting the href property
link.href="https://unfetteredthoughts.net";

Diseñar esto a través de CSS también es bastante sencillo:

a {
  /* Disabled link styles */
}
a:link, a:visited { /* or a[href] */
  /* Enabled link styles */
}

¡Eso es todo lo que necesitas hacer!

Eso no es suficiente, quiero algo más complejo para poder lucir más inteligente.

Si simplemente tiene que diseñar en exceso alguna solución extrema, aquí hay algunas cosas a considerar. Con suerte, prestará atención y reconocerá que lo que estoy a punto de mostrarles no vale la pena el esfuerzo.

Primero, necesitamos diseñar nuestro enlace para que parezca deshabilitado.

.isDisabled {
  color: currentColor;
  cursor: not-allowed;
  opacity: 0.5;
  text-decoration: none;
}
<a class="isDisabled" href="https://unfetteredthoughts.net">Disabled Link</a>

Ajuste color a currentColor debe restablecer el color de la fuente a su color normal de texto sin vínculo. También estoy colocando el cursor del mouse en not-allowed para mostrar un buen indicador al pasar el mouse de que la acción normal no está permitida. Ya hemos dejado de lado a los usuarios que no utilizan el mouse y que no pueden desplazarse, principalmente el tacto y el teclado, por lo que no obtendrán esta indicación. A continuación, la opacidad se reduce a la mitad. Según WCAG, los elementos deshabilitados no necesitan cumplir con las pautas de contraste de color. Creo que esto es muy arriesgado ya que es básicamente texto sin formato en este punto, y reducir la opacidad a la mitad haría muy difícil de leer para los usuarios con baja visión, otra razón por la que odio esto. Por último, se elimina el subrayado de la decoración del texto, ya que suele ser el mejor indicador de que algo es un enlace. ¡Esto parece un enlace deshabilitado!

¡Pero no está realmente discapacitado! Un usuario aún puede hacer clic / tocar en este enlace. Te escucho gritar sobre pointer-events.

.isDisabled {
  ...
  pointer-events: none;
}

Ok, hemos terminado! ¡Enlace desactivado logrado! Excepto, solo está realmente deshabilitado para los usuarios del mouse que hacen clic y tocan a los usuarios que tocan. ¿Qué pasa con los navegadores que no admiten pointer-events? Según caniuse, esto no es compatible con Opera Mini e IEpointer-events a menos que display se establece en block o inline-block. Además, la configuración pointer-events a none sobrescribe nuestro bonito not-allowed cursor, por lo que ahora los usuarios del mouse no obtendrán esa indicación visual adicional de que el enlace está deshabilitado. Esto ya está empezando a desmoronarse. Ahora tenemos que cambiar nuestro marcado y CSS …

.isDisabled {
  cursor: not-allowed;
  opacity: 0.5;
}
.isDisabled > a {
  color: currentColor;
  display: inline-block;  /* For IE11/ MS Edge bug */
  pointer-events: none;
  text-decoration: none;
}
<span class="isDisabled"><a href="https://unfetteredthoughts.net">Disabled Link</a></span>

Envolviendo el enlace en un <span> y agregando el isDisabled clase nos da la mitad de nuestro estilo visual discapacitado. Un buen efecto secundario aquí es que la clase deshabilitada ahora es genérica y se puede usar en otros elementos, como botones y elementos de formulario. La etiqueta de ancla real ahora tiene el pointer-events y text-decoration ajustado a none.

¿Qué pasa con los usuarios de teclado? Los usuarios de teclado utilizarán el ENTER clave para activar enlaces. pointer-events son solo para punteros, no hay eventos de teclado. También debemos evitar la activación de navegadores antiguos que no son compatibles pointer-events. Ahora tenemos que introducir algo de JavaScript.

Trae el JavaScript

// After using preferred method to target link
link.addEventListener('click', function (event) {
  if (this.parentElement.classList.contains('isDisabled')) {
    event.preventDefault();
  }
});

Ahora nuestro enlace parece deshabilitado y no responde a la activación mediante clics, toques y el ENTER llave. ¡Pero todavía no hemos terminado! Los usuarios de lectores de pantalla no tienen forma de saber que este enlace está desactivado. Necesitamos describir este enlace como desactivado. El disabled El atributo no es válido en los enlaces, pero podemos usar aria-disabled="true".

<span class="isDisabled"><a href="https://unfetteredthoughts.net" aria-disabled="true">Disabled Link</a></span>

Ahora voy a aprovechar esta oportunidad para diseñar el enlace en función de la aria-disabled atributo. Me gusta usar atributos ARIA como ganchos para CSS porque tener elementos con un estilo incorrecto es un indicador de que falta una accesibilidad importante.

.isDisabled {
  cursor: not-allowed;
  opacity: 0.5;
}
a[aria-disabled="true"] {
  color: currentColor;
  display: inline-block;  /* For IE11/ MS Edge bug */
  pointer-events: none;
  text-decoration: none;
}

Ahora nuestros enlaces se ven deshabilitados, actúan deshabilitados y se describen como deshabilitados.

Desafortunadamente, aunque el enlace se describe como deshabilitado, algunos lectores de pantalla (JAWS) aún anunciarán que se puede hacer clic. Lo hace para cualquier elemento que tenga un detector de clics. Esto se debe a la tendencia del desarrollador a crear elementos no interactivos como div y span como elementos pseudo-interactivos con un simple oyente. No podemos hacer nada al respecto aquí. Todo lo que hemos hecho para eliminar cualquier indicio de que se trata de un vínculo ha sido frustrado por la tecnología de asistencia que estábamos tratando de engañar, irónicamente porque hemos intentado engañarlo antes.

Pero, ¿y si trasladamos al oyente al cuerpo?

document.body.addEventListener('click', function (event) {
  // filter out clicks on any other elements
  if (event.target.nodeName == 'A' && event.target.getAttribute('aria-disabled') == 'true') {
    event.preventDefault();
  }
});

¿Terminamos? Bueno en realidad no. En algún momento, necesitaremos habilitar estos enlaces, por lo que debemos agregar código adicional que alternará este estado / comportamiento.

function disableLink(link) {
// 1. Add isDisabled class to parent span
  link.parentElement.classList.add('isDisabled');
// 2. Store href so we can add it later
  link.setAttribute('data-href', link.href);
// 3. Remove href
  link.href="";
// 4. Set aria-disabled to 'true'
  link.setAttribute('aria-disabled', 'true');
}
function enableLink(link) {
// 1. Remove 'isDisabled' class from parent span
  link.parentElement.classList.remove('isDisabled');
// 2. Set href
  link.href = link.getAttribute('data-href');
// 3. Remove 'aria-disabled', better than setting to false
  link.removeAttribute('aria-disabled');
}

Eso es. Ahora tenemos un enlace deshabilitado que está deshabilitado visual, funcional y semánticamente para todos los usuarios. Solo tomó 10 líneas de CSS, 15 líneas de JavaScript (incluido 1 oyente en el cuerpo) y 2 elementos HTML.

En serio amigos, simplemente no lo hagas.