내가 보려고 만든 개발 공부 일지

자바스크립트 onfocus 와 onblur 그리고 relatedTarget 본문

Javascript

자바스크립트 onfocus 와 onblur 그리고 relatedTarget

kwangsunny 2022. 2. 5. 23:11

HTML 요소 포커싱

HTLM 요소의 포커싱 관련 이벤트로 onfocus 와 onblur 가 있다.

이 두 이벤트는 서로 짝꿍이다.

 

이름 그대로 HTML 요소가 포커싱 되었을땐 onfocus 이벤트가 발생하고,

반대로 포커싱 해제 되었을땐 onblur 이벤트가 발생하게된다.

 

여기서 '포커싱' 이란 HTML 요소가 마우스 클릭 혹은 Tab키로 선택이 가능한 것을 의미한다.

모든 HTML 요소가 다 포커싱 가능한것은 아니다.

기본적으로 포커싱 가능한 HTML 요소로는 a, button, details, input, select, textarea 가 있고 이 요소들을

대화형 요소 라고 부른다.

 

위 6개 요소들을 제외한 HTML 요소들은 비대화형 요소 라고 부르며 (div, span, ul ... 등 같은 나머지 태그)

이 비대화형 요소는 포커싱이 되지 않는다.

즉 onfocus / onblur 이벤트가 발생하지 않는다는 뜻이다.

 

물론 강제로 비대화형 요소에 포커싱을 줄 수도 있는데, 바로 tabindex 라는 속성을 사용하면 된다.  

 

 

 

포커싱 기능 부여 - tabindex 

우리가 브라우저 화면에서 Tab키를 누르면 순서대로 화면내 요소들이 순서대로 포커싱 되면서

넘어가는 모습을 볼 수 있는데, 이 순서를 부여하는 속성이 바로 tabindex다.

tabindex='숫자값' 형태로 사용할 수 있고, 숫자값이 작은값 -> 큰값 의 순서로 HTML 요소가 포커싱된다.

 

숫자값으로 음수를 줄 수도 있는데, (보통 -1) 음수를 주게되면 키입력으로 포커싱은 안되지만 여전히 마우스 클릭이나

focus() 메소드로는 포커싱이 가능하다. 

음수값을 주는 이유는 화면에서 보이는것과 상관없이 기능상 포커싱이 필요한 경우가 있기 때문이다.

<div tabindex='1'>A</div>
<div tabindex='3'>B</div>
<div tabindex='2'>C</div>

위 예시처럼 하면 Tab키 입력 시 A -> C -> B 순으로 div 태크가 포커싱 된다.

 

그리고 tabindex 로 포커싱이 가능하게 되었다는 것은 곧 onfocus / onblur 이벤트도 발생한다는 뜻이다.

원래라면 위 예시의 div 태그들은 마우스 클릭이나 Tab키로 포커싱을 줄 수 없고 당연히 onfocus / onblur 이벤트도

발생하지 않겠지만 tabindex 값을 줌으로 인해 이게 가능해진 것이다.  

 

참고로 tabindex는 사용자 접근성과 관련있는 부분이므로 남용하지말고 꼭 필요한 경우에만 사용해야된다. 

 

여기까지 포커싱이 뭔지 간단히 짚어보았고 이제 relatedTarget 이 무엇인지 알아보자.

 

 

 

relatedTarget 이란

onblur 이벤트가 발생했을때 인자로 받는 event 객체 안을 보면, relatedTarget 이라는 녀석이 함께 담겨져있다.

MDN 사이트에는 '이벤트의 보조 대상을 식별한다' 라고 설명이 나와있다.

 

<input name='id' onblur='onblur()'/>
<input name='pw'/>

<script>
    function onblur(e){
    	console.log(e.currentTarget); // A
    	console.log(e.relatedTarget); // B
    }
</script>

위 예제를 보면 id, pw 를 입력하는 input이 두 개가 있다.

만약 id input을 클릭한 후 pw input을 클릭하면, A에는 id input이 담겨 있을 것이고,

B에는 pw input이 담겨 있을 것이다.

 

반면에 id input이 클릭된 상태에서 pw input이 아닌 빈공간 아무곳을 클릭하게 되면

B에 null이 출력될 것이다.

 

즉, 포커싱된 상태인 어떤 HTML 요소가 사용자의 마우스 클릭 등에 의해 포커싱 해제될때

그 다음으로 포커싱될 HTML 요소가 relatedTarget에 담겨지게 되는 것이다.

 

실제 활용 예제

그러면 relatedTarget은 어떤 상황에 활용할 수 있을까? 아래 활용 예시를 살펴보자.

import React, { useRef, useState } from 'react';
/** @jsxImportSource @emotion/react */
import {css} from '@emotion/react';

const TEST_LIST = [
    'apple',
    'codeing',
    'test',
    'front-end',
    'React',
    'CSS',
    'HTML',
    'Javascript',
    'Typescript',
    'Apollo'
]

function AutoComplete(){
    const [filteredList, setFilteredList] = useState([]);
    const [input, setInput] = useState('');
    const inputRef = useRef(null);

    const onChange = (e)=>{
        const value = e.currentTarget.value;
        filterText(value);
        setInput(value);
    }

    const filterText = (value)=>{
        const searchText = value ? value.toLowerCase() : null;
        const filtered = TEST_LIST.filter(str => str.toLowerCase().indexOf(searchText) > -1);
        setFilteredList(filtered);
    }

    const onBlur = (e)=>{
        const next = e.relatedTarget;
        if(next instanceof HTMLLIElement){
            setInput(next.textContent);
            inputRef.current.focus();
        }else{
            setInput('');
            filterText('');
        }
    }
    
    const style = css`
        // 생략
    `;

    return(
        <div css={style}>
            <input 
                ref={inputRef}
                onChange={onChange}
                onBlur={onBlur}
                value={input}/>
            <ul className='text-box'>
                {filteredList.map((txt, i) => (
                    <li key={i} tabIndex={-1}>{txt}</li>
                ))}
            </ul>
        </div>
    );
}

export default AutoComplete;

위 예시는 필자가 React로 만들어본 입력 자동완성 컴포넌트이다. (css는 주제와 상관 없으므로 생략)

실제 작동 모습은 아래와 같다.

예제 실행 모습

 

실행 모습을 보면, 입력된 텍스트에 따라 필터링된 리스트가 하단에 보이게 되는데

입력 후 빈공간을 클릭하면 필터링 리스트가 사라지지만 리스트값을 클릭하면 리스트는 사라지지 않고 유지된다.

 

onblur 함수에서 relatedTarget 값이 li 태그인지 아닌지 확인하고 li 태그라면 클릭한 필터값으로 input 값을 세팅해주고

li 태그가 아니라면 필터링값을 빈값으로 세팅해줘서 필터링 리스트를 보이지 않게 처리해주고 있다.

 

위에서 빈공간을 클릭했을때 필터링 리스트가 사라지는 이유는 이때 relatedTarget에 null 이 들어있기 때문이다.

참고로 li 태그는 비대화형 요소이기 때문에 tabindex 값을 줘서 포커싱이 가능하도록 만들어줬다.

그래야 리스트를 클릭했을때 li 태그가 포커싱이 되면서 relatedTarget에 담겨지기 때문이다.

 

이렇게 relatedTarget은 HTML 요소의 포커싱에 따라 어떤 동작을 구분지어 수행해야 할때

활용할 수 있다.

 

 

 

 

 

잘못된 내용이 있다면 댓글에 남겨주세요 :)

 

 

Comments