// jsx
...
<Backdrop onClick={onLeaveFocusLogin}>
<Modal ref={loginBox}>
<Cover>
...
</Cover>
</Modal>
</Backdrop>
위와 같이 구분할 엘리먼트에 ref
를 걸고 바깥쪽에 해당하는 박스에 onClick
을 바인딩한다. 이후 아래와 같이 boolean
으로 타겟이 구분엘리먼트 안쪽인지 바깥쪽인지 구분해서 바깥쪽이면 원하는 엘리먼트 컨트롤을 종료하면 된다.
// jsx
const onLeaveFocusLogin = useCallback((e) => {
if (!loginBox.current) return
if (!loginBox.current.contains(e.target)) {
onCloseLogin()
}
}, [])
2번글의 원래 목적은 input 엘리먼트의 focus/blur의 대한 목적이 아닌 컨트롤내부와 외부를 구분해서 외부를 클릭시 컨트롤을 종료하는 방법을 작성하기 위해 썼는 것이다. 현재 더 쉬운 방식으로 내부와 외부를 구분하는 방법을 알게됬음. 그래서 원래 목적이었던 내외부 구분하는 것을 위해서라면 2번방식이 아닌 위에 1번 방식을 사용하는 것이 맞다. 2번은 그냥 onBlur가 어떤 순서로 이벤트가 진행되는지까지만 확인하는 것으로 이해하도록 하자.
Focus
관련해서 일반적으로 js에서는 focusin
, focusout
이벤트가 있고 별도로 blur
관련이벤트가 있다. 하지만 react
에서는 onFocus
, onBlur
두가지만 있다. 따라서 포커스가 떠남을 표현하고자 할 때는 onBlur
를 사용해야한다. 참고로 onBlur를 사용할 때는 tabIndex
속성을 같이 사용해줘야 onBlur
이벤트가 트리거되다.
단
onBlur
이벤트는onClick
이벤트와 같이 사용하게 될 시 순서가 꼬일수있다.
아래 이미지는 콤보박스이다. 콤보박스의 헤더부분을 클릭하면 아래 리스트가 열리고 다시 헤더를 클릭하면 리스트가 닫힌다.
onBlur
이벤트로 처리할 수 있다. 하지만 만약 리스트아이템의 핸들러를 onClick
으로 받는다면 리스트아이템의 핸들러가 동작하기전에 onBlur
에 의해 리스트가 닫힌다.
이벤트 진행순서
onBlur
->onClick
따라서 onClick
을 onMouseDown
으로 변경하면 해결된다.
이벤트 진행순서
onMouseDown
->onBlur
->onClick
<Container>
<DisplayItem
tabIndex={0}
onClick={onShowListItems}
onBlur={onLeaveFocusComboBox}
>
<span>{selectedItem.displayName}</span>
</DisplayItem>
<>
{isShowList && (
<DisplayList>
{itemsSource.map((item) => (
<Item
onMouseDown={onSelectedItem(item)}
isSelected={getIsSelected(item)}
>
{item.displayName}
</Item>
))}
</DisplayList>
)}
</>
</Container>
덕분에 1시간동안 고민하던 문제를 해결 할 수 있었습니다, 감사합니다 👍🏻