Búsqueda de sitios web

html - Diferenciando entre eventos de teclado y de ratón durante onFocus en React


Estaba intentando crear un contorno de enfoque de navegación por teclado para accesibilidad. La clase pseudo :focus-visible funciona bien en todos los elementos excepto en los input, como texto o textarea. Parece que el input siempre tendrá esta clase pseudo activa, ya que requiere entrada de teclado. Buscando no pude encontrar una manera de solucionarlo, para que se comporte como todos los demás elementos interactivos. Así que pensé en simplemente manejarlo con onFocus durante el renderizado, mostrando/ocultando una clase CSS personalizada que aparecerá y desaparecerá dependiendo de cómo el usuario enfocó el input. He logrado hacer hasta cierto punto, pero el problema es que no sé cómo puedo ahora diferenciar de dónde proviene el input, teclado o ratón. Esto es lo que tengo hasta ahora. Primero hooks de React:

  const [keyboardFocus, setKeyboardFocus] = useState(false);
  const toggleKeyboardFocusOn = () => setKeyboardFocus(true);
  const toggleKeyboardFocusOff = () => setKeyboardFocus(false);

Entonces tenemos el hook keyboardFocus, y toggles para onFocus y onBlur que deberían definir si la entrada debería tener enfoque de teclado o no. Luego tengo el selector de clase para determinar si el contorno solo del teclado está activo o no:

const inputClassNames = classNames({
    ...
    [css.keyboardOnlyFocus]: keyboardFocus,
  });

Y finalmente en la representación de la entrada que tengo

      onBlur={toggleKeyboardFocusOff}
      onFocus={toggleKeyboardFocusOn}

Hasta ahora, enfocar y desenfocar funciona, y mostrará el contorno. Lo que me está costando es entender cómo puedo ahora en onFocus determinar si el evento vino del ratón, en cuyo caso debería ser ignorado, o si vino del teclado.

Una de las soluciones que intenté fue algo así:

onFocus={(e) => ({
   if(e.key === 'Tab') {
     toggleKeyboardFocusOn;
   }
})

Sin embargo, nada estaba pasando, y la salida de la consola de e.key mostraba undefined. Entonces, ¿cuál sería la forma correcta de indicar en onFocus que el evento proviene del mouse o del teclado?

Respuesta #1

Me encontré con un tipo de situación similar, la única solución que pude idear fue:

  • Usa onMouseDown para saber si es un evento de ratón. Ya que se ejecutará antes de los callbacks onChange y onFocus.

  • Usa onKeyDown para saber si es un evento de teclado (si es necesario en tu caso, fue necesario en mi caso). La función de devolución de llamada onKeyDown, similar a onMouseDown, se ejecutará antes de las funciones de devolución de llamada onChange y onFocus.

Espero que esto ayude.

Respuesta #2

Creo que la mejor oportunidad que tienes es averiguar en qué orden ocurren los eventos de clic del ratón y los eventos de enfoque, y si son síncronos (ocurriendo durante el mismo ciclo de eventos)

Aquí hay un poco de código para ayudarte a empezar:

const eventObserved = useRef(false);
const checkOther = () => {
    if (eventObserved.current) {
        console.log('synchronous!');
    } else {
        eventObserved.current = true;
        setTimeout(() => { eventObserved.current = false });
    }
};

return (
  <input
    onClick={() => {
      console.log('clicked');
      checkOther();
    }}
    onFocus={() => {
      console.log('focus');
      checkOther();
    }}
  />
);

Y también podrías considerar manejar MouseDown y MouseUp por separado del click. Tal vez necesites ir al nivel más básico y usar eventos nativos en lugar de los eventos sintéticos de React.

Artículos relacionados