onkeyup, onkeydown과 button
상황
Trigger
컴포넌트는 기본적으로 button
태그를 이용합니다. 그런데, 저희의 프로젝트 특성 상 어떠한 태그로 변경해도 괜찮을 수 있도록 컴포넌트를 만들고 있었는데 이 경우에 button
태그가 기본적으로 가지고 있는 키보드 이벤트를 막아야 했습니다. - 이를 이용할 시, 태그를 임의적으로 div, sapn
등으로 변경할 때에 이벤트가 원활히 작동되지 않을 것이라 생각했기 때문입니다. -
(keyPress
의 경우 Deprecated
되어 사용하지 않았습니다. )
// Trigger ... const handleClick = (e: React.MouseEvent) => { setIsOpen(!isOpen); console.log("handle click 실행됨"); // 클릭을 할때에, 이 함수가 실행됩니다. }; const handleKeyup = (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); } if (e.key === " ") { e.preventDefault(); } // 이런 방식으로 기본적으로 가지고 있는 이벤트를 막을 수 있다고 생각했습니다. }; return ( <button onClick={handleClick} ref={mergeRefs([triggerRef, forwardRef])} id={labelRef.current?.id} role='combobox' aria-controls={optionsRef.current?.id} aria-expanded={isOpen} aria-labelledby={labelRef.current?.id ?? labelledby} aria-haspopup='listbox' className={className} onKeyUp={handleKeyup} // 일반적으로 onKeyUp을 아무 생각 없이 사용해왔었기에, // onKeyUp으로 해당 핸들러를 적용해 주었습니다. tabIndex={0} > {selectedValue ?? placeHolder} {children} </button> );
위와 같이 e.preventDefault()
를 통해 기본적으로 작동하는 동작을 막을 수 있을 것이라 생각했는데, onKeyUp
으로 이벤트 핸들러를 등록하면 onClick
에 등록되어져 있는 핸들러 함수가 작동되었습니다.
버튼에 포커스를 두고, Enter
를 하게 되면 아래와 같이 handleClick
함수가 실행되었습니다.
사실 위와 같은 부분은 버튼의 기본적인 이벤트이므로, Enter
를 했을 때에 자동적으로 버튼이 클릭되는 것처럼 동작하는 것으로 당연한 결과였습니다.
그런데, 이를 keyDown
으로 변경하면 의도한 바대로 이벤트의 동작이 잘 막아집니다.
const handleClick = (e: React.MouseEvent) => { setIsOpen(!isOpen); console.log("handle click 실행됨"); }; const handleKeyup = (e: React.KeyboardEvent) => { console.log('keydown 실행됨') if (e.key === "Enter") { e.preventDefault(); } if (e.key === " ") { e.preventDefault(); } }; return ( <button onClick={handleClick} ref={mergeRefs([triggerRef, forwardRef])} id={labelRef.current?.id} role='combobox' aria-controls={optionsRef.current?.id} aria-expanded={isOpen} aria-labelledby={labelRef.current?.id ?? labelledby} aria-haspopup='listbox' className={className} onKeyDown={handleKeyup} // 이 부분만 onKeyDown으로 바꿔주었습니다. tabIndex={0} > {selectedValue ?? placeHolder} {children} </button> ); }, );
왜 이런 방식이 가능한걸까요? 사실 지금까지는 아무 생각 없이 onClick, onKeyUp
을 기반으로 사용하고 있었습니다. 둘의 무슨 차이점이 있었기에, onKeyDown
을 사용하게 되면 onClick
이 일어나는 것을 막을 수 있었던 것일까요?
keyUp, keyDown 의 차이점
기본적으로 onKeyUp, onKeyDown의 차이점은 다음과 같습니다.
-
onKeyUp
유저가 키보드에서, 버튼을
release
했을때에 이벤트가 실행됩니다.이벤트는 한번만 실행됩니다.
-
onKeyDown
유저가 키보드에서, 버튼을
press
했을때에 이벤트가 실행됩니다.버튼을 계속 누르고 있다면, 이벤트는 계속해서 실행됩니다.
이를, 마우스 이벤트와 연관지어 보겠습니다.
-
onMouseDown
유저가 마우스 버튼을 눌렀을때 이벤트가 실행욉니다.
-
onMouseUp
유저가 마우스 버튼을 뗐을때에 이벤트가 실행됩니다.
→ 사실 위 두가지 요소는, 지금과 관련은 없지만 간단히 정리했습니다.
-
onClick
유저가 마우스 버튼을 눌렀다 땠을 때에 이벤트가 실행됩니다.
onMouseDown, onMouseUp
의 조합으로 이루어져 있습니다.
여기서, button
이라는 태그는 기본적으로 가지고 있는 동작은 onClick
입니다. 이때 Enter
키보드 이벤트는 onClick
을 실행합니다. 그런데, onClick
은 사용자가 마우스로 버튼을 눌렀을때 (enter를 했을때) 와 떼었을 때 모두 실행됩니다.
따라서, onKeyUp
에 e.preventDefault()
를 한다고 해도 onClick
이벤트기 살행됩니다. onKeyUp
이 실행되는 시점은 onKeyDown
다음인데, onKeyDown
이 이루어진 시점에 onClick
이 이를 감지에 실행되었기 때문입니다.
위와 같은 이유로, button
의 기본적인 enter
이벤트를 막기 위해서는 onKeyDown
을 막아야 합니다!