문제상황
유저가 textarea에서 입력을 하면, 드랍다운에 추천 텍스트를 보여주고, 드랍다운 클릭을 통해 텍스트를 자동으로 채워주는 기능을 개발하고 있었다. 그런데 textarea가 비활성화되는 blur 이벤트가 click 이벤트보다 먼저 발생하여 드랍다운 메뉴가 먼저 사라지고, 유저는 드랍다운 메뉴가 있던 빈 자리를 클릭하게 되는 것이었다.
ChatGPT의 해결 방법 : setTimeout()
gpt는 setTimeout()을 사용할 것을 추천해주었다. 즉, blur 이벤트의 처리를 약간 지연시켜 click 이벤트가 먼저 발생하도록 만드는 것이다. setTimeout()을 이용해 blur 이벤트의 콜백 함수(드랍다운의 display를 none으로 설정)를 100ms정도 되는 짧은 시간 뒤에 실행되도록 지연시킨다. 이렇게 하면 click 이벤트가 먼저 발생하고, 그 후에 blur 이벤트가 발생하게 되어 드롭다운 메뉴 등을 정상적으로 선택할 수 있게 된다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Textarea Blur Event Example</title>
</head>
<body>
<textarea id="myTextarea" rows="4" cols="50">Click outside of this textarea to see the issue.</textarea>
<div id="dropdownMenu" style="display: none; border: 1px solid #000; padding: 10px; position: absolute;">
<p>Dropdown Item 1</p>
<p>Dropdown Item 2</p>
<p>Dropdown Item 3</p>
</div>
<script>
const textarea = document.getElementById('myTextarea');
const dropdownMenu = document.getElementById('dropdownMenu');
textarea.addEventListener('focus', () => {
// Textarea가 포커스를 얻으면 드롭다운 메뉴를 보여줍니다.
dropdownMenu.style.display = 'block';
});
textarea.addEventListener('blur', () => {
// setTimeout을 사용하여 blur 이벤트의 처리를 지연시킵니다.
setTimeout(() => {
dropdownMenu.style.display = 'none';
}, 100);
});
dropdownMenu.addEventListener('click', (event) => {
// 드롭다운 메뉴가 클릭되었을 때의 동작을 정의합니다.
alert('Dropdown item clicked!');
});
</script>
</body>
</html>
실제 해결 방법 : mousedown 이벤트로 blur 이벤트 리스너 제거
우선 기능 개발이 급해 gpt의 추천대로 setTimeout()을 사용했지만, 급한 불을 끄고 난 뒤에 일정 시간을 지연시키는 방법이 밀리세컨을 직접 저렇게 숫자로 코드에 넣는 것이 하드코딩스럽기도 하고 괜히 마음에 걸렸다. 나중에 수정한 방법은 mousedown 이벤트를 활용한 방법이다.
mousedown 이벤트는 사용자가 마우스 버튼을 누를 때 발생하므로, click 이벤트보다 먼저 발생한다. 이 이벤트를 활용해 사용자가 클릭하는 순간, blur 이벤트 리스너를 제거하여 클릭이 먼저 처리되도록 해결하였다. (나중에 gpt에게 다시 물어보니 mouseup 이벤트에서 다시 blur 이벤트 리스너를 추가하여 클릭이 완료된 후에 blur 이벤트가 발생할 수 있도록 원상복구 하란다. 출근하면 바꿔놔야겠다.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Textarea Blur Event Example</title>
</head>
<body>
<textarea id="myTextarea" rows="4" cols="50">Click outside of this textarea to see the issue.</textarea>
<div id="dropdownMenu" style="display: none; border: 1px solid #000; padding: 10px; position: absolute;">
<p>Dropdown Item 1</p>
<p>Dropdown Item 2</p>
<p>Dropdown Item 3</p>
</div>
<script>
const textarea = document.getElementById('myTextarea');
const dropdownMenu = document.getElementById('dropdownMenu');
function hideDropdown() {
dropdownMenu.style.display = 'none';
}
// blur 이벤트 리스너를 추가하는 함수
function addBlurListener() {
textarea.addEventListener('blur', hideDropdown);
}
// blur 이벤트 리스너를 제거하는 함수
function removeBlurListener() {
textarea.removeEventListener('blur', hideDropdown);
}
textarea.addEventListener('focus', () => {
// Textarea가 포커스를 얻으면 드롭다운 메뉴를 보여줍니다.
dropdownMenu.style.display = 'block';
});
textarea.addEventListener('mousedown', () => {
// mousedown 시점에 blur 이벤트 리스너를 제거합니다.
removeBlurListener();
});
document.addEventListener('mouseup', () => {
// mouseup 시점에 blur 이벤트 리스너를 다시 추가합니다.
addBlurListener();
});
dropdownMenu.addEventListener('click', (event) => {
// 드롭다운 메뉴가 클릭되었을 때의 동작을 정의합니다.
alert('Dropdown item clicked!');
});
// 초기에는 blur 이벤트 리스너가 활성화된 상태로 시작합니다.
addBlurListener();
</script>
</body>
</html>
결론
사실 두 방식 모두 잘 작동했기에 뭐가 더 나은 방법이라 말하긴 어렵다. 내부 구현이 어떻든 유저가 체감하는 퍼포먼스만 좋으면 되니까...ㅋㅋㅋ 그래도 역시 지연시킬 시간을 얼마를 주는게 좋을지 고민하는 것보단 동적으로 이벤트 리스너를 제거하고 다시 추가해주는게 좀 더 내스타일 해결방법이란 생각이 든다.
'🔨 개발 > 🖥️ 웹개발' 카테고리의 다른 글
자바스크립트에서 큐(Queue) 사용하기 (3) | 2024.09.27 |
---|---|
로컬 스토리지(LocalStorage)란 무엇인가?, 어떻게 사용하는가? (1) | 2024.09.19 |
CORS(Cross-Origin Resource Sharing)란 무엇인가? (1) | 2024.09.06 |
JWT 로그인 인증 방식: Stateful vs Stateless (1) | 2024.09.06 |
메뉴 드롭다운 구현하기 (1) | 2024.09.03 |