Side Project/Troubleshooting

onBlur 이벤트와 마우스 이벤트

Yepchani 2023. 12. 10. 01:06

들어가며

이번에 사이드 프로젝트를 진행하며 겪은 문제에 대해 얘기해 보고자 합니다.

검색어 자동 완성 기능을 구현하는 게 목표였는데요.

검색어를 입력하면 해당 글자를 포함한 상품이 있는 경우 리스트로 표시해 줍니다.

 

검색창

 

검색어 자동 완성

 

리스트 아이템을 클릭하면 해당 검색 결과로 이동합니다.

검색창 외부를 클릭하면 onBlur 이벤트가 발생해 리스트가 사라지도록 했는데요. (onBlur 이벤트는 요소가 포커스를 잃었을 때 발생합니다.)

 

문제

문제는 리스트 아이템을 클릭할 때도 onBlur 이벤트가 발생해 리스트가 사라지고 검색 결과로 이동하지 않았습니다.

 

왜 이런 일이 발생한 걸까요? 마우스 클릭 이벤트에 대해 살펴보겠습니다.

 

마우스 클릭 이벤트는 크게 세 단계로 발생합니다.

onMouseDown -> onMouseUp -> onClick

저는 onClick 이벤트를 사용 중이었는데요. onBlur 이벤트는 onMouseDown 이벤트가 발생하는 즉시 발생합니다.

onMouseDown 이벤트가 발생하면 브라우저는 현재 포커스를 잃는 것으로 판단하고 onBlur 이벤트를 발생시킵니다. 

 

솔루션

그럼 이 문제를 어떻게 해결할 수 있을까요?


바로 onMouseDown 이벤트에서 event.preventDefault()을 호출해 브라우저의 기본 동작을 막아야 합니다. 여기서 브라우저의 기본 동작은 '버튼을 누르면 현재 포커스를 잃는다'라는 동작입니다. 따라서 e.preventDefault()를 호출하면, 버튼을 눌렀을 때 input 요소가 포커스를 잃지 않게 되므로`onBlur 이벤트가 발생하지 않는 거죠.

 

아래는 이를 구현한 예시 코드입니다.

 

  const onButtonMouseDown = (e: MouseEvent<HTMLButtonElement>) => {
    e.preventDefault();
  };
  const onButtonMouseUp = (e: MouseEvent<HTMLButtonElement>) => {
    const target = e.target as HTMLElement;
    const query = target.textContent || '';
    navigateToSearch(query);
  };

<form
      onSubmit={handleSubmit(onValid)}
    >
        <div className="relative">
          <input
            type="text"
            required
            {...register('query', {
              required: true,
              minLength: 2,
              onChange: onInputChange,
              onBlur: onInputBlur
            })}
            placeholder="검색어를 입력해주세요."
            onClick={onInputClick}
            onKeyDown={onInputKeyDown}
          />
          {isOpenSearchList && (
            <div role="list">
              {data.products.map((product, index) => (
                <button
                  key={product.id}
                  ref={(ref) => {
                    itemRefs.current[index] = ref;
                  }}
                  type="button"
                  tabIndex={0}
                  onMouseDown={onButtonMouseDown}
                  onMouseUp={onButtonMouseUp}
                  onKeyDown={(e) => {
                    onButtonKeyDown(e);
                    onInputKeyDown(e);
                  }}
                >
                  {product.name}
                </button>
              ))}
            </div>
          )}
        </div>
</form>

 

마치며

onBlur 이벤트가 onMouseDown 이벤트 바로 다음에 발생한다는 건 몰랐는데요.

이번 기회에 onBlur 이벤트와 마우스 클릭 이벤트가 어떻게 이루어지는지 알 수 있었습니다.

 

지적이나 다른 의견은 언제나 환영합니다 :D

감사합니다.

 

Reference

https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event

https://developer.mozilla.org/en-US/docs/Web/API/Element/mousedown_event

https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event

https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event

https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault

https://www.quirksmode.org/js/events_order.html