프론트에서 메뉴 컴포넌트를 만들 때 필수적으로 들어가야 하는 로직은 키보드를 이용한 메뉴 컴포넌트 탐색 로직이다.
메뉴 컴포넌트가 너무 길어서 안에 스크롤이 생기게 되었을 때 키보드 탐색 방향에 맞게 메뉴 아이템이 포커스 되어야한다.
예를 들어 분기처리를 해보자면,
그런데 내 원래 로직은 상단으로만 고정되었음,,, 그래서 어떤 모양이었나면
![]() | ![]() |
---|
그룹에서는 스크롤이 작동안하고, 일반 메뉴리스트에서는 무조건 상단 고정이되고 있었음.. 사실 그룹에서 스크롤 작동 안하는건 그룹의 하위에 있는 child를 구하지 않아서 그런거였고 나머지는 여기 부분이 문제였음
//* 해당하는 자식에 맞게 scroll
const scrollToChild = (child: Element) => {
if (!(child instanceof HTMLLIElement)) {
return
}
const { offsetTop } = child
ref.current?.scrollTo(0, offsetTop)
}
매개변수로 받는 child가 다음에 포커스 할 요소인데 그냥 그걸 맨 위로 가게 스크롤 짠거..
이 부분을 뜯어 고쳐보자!
먼저 메뉴 스크롤 코드에서 ref로 정보를 뽑아내는 주체는 크게 2개이다.
컨테이너(메뉴)에서 clientHeight, scrollTop, scrollHeight
메뉴 아이템에서 clientHeight, offsetTop
clientHeight: 화면상 보이는 요소의 높이
scrollHeight: 스크롤로 가려진 부분까지 합친 요소의 전체 높이
scrollTop: 화면상 보이는 시작점이 맨 위에서 얼마큼 떨어져 있는지
offsetTop: 각 요소가 부모요소의 시작점으로부터 얼마큼 떨어져 있는지
ref가 걸려있는 곳은 메뉴컨테이너고
child는 탐색 로직에서 구한 다음에 포커스 될 요소이다.
위 정보들을 가지고 위에서 구한 1~5까지의 분기처리를 진행해야한다.
먼저 화면을 벗어나는 것은 신경쓰지 않고 탐색 방향만을 구한다.
현재 child의 offsetTop + clientHeight가 ref의 scrollTop + clientHeight보다 작다.
현재 child의 offsetTop + clientHeight가 ref의 scrollTop + clientHeight보다 크다.
위 두가지 경우에서 분기 쳐야하는 것은 3개이다.
이걸 코드에 녹여보자.
//* 이동방향에 따라 스크롤 위치 로직 (아래로가면 아래 고정, 위로가면 위로 고정)
const scrollToChild = (child: Element) => {
if (!(child instanceof HTMLLIElement)) {
return
}
const { offsetTop, clientHeight } = child
const menuHeight = ref.current?.clientHeight
const scrollTop = ref.current?.scrollTop
// 메뉴 ref가 제대로 잡히지 않았다는 것
if (menuHeight === undefined || scrollTop === undefined) return
// 키보드 캄색이 화면에서 벗어나지 않았을 때 얼리리턴
if (scrollTop < offsetTop && offsetTop + clientHeight < scrollTop + menuHeight) return
if (menuHeight && scrollTop !== undefined && scrollTop + menuHeight < offsetTop + clientHeight) {
//* 아래로 갈때 하단 픽스
const scrollToPosition = offsetTop + clientHeight - menuHeight
ref.current?.scrollTo(0, scrollToPosition + paddingHeight)
} else if (menuHeight && scrollTop && scrollTop + menuHeight > offsetTop + clientHeight) {
//* 위로갈 때 상단 픽스
ref.current?.scrollTo(0, offsetTop)
}
}
![]() | ![]() |
---|
깰~꼼!