Sticky Nav와 같이 웹 사이트를 제작할 때 자주 사용할 수 있을 것 같은 기능이다. 마우스 hover
→ 서브메뉴들의 크기에 따라 동적으로 드롭다운 된다.
로직
const를 통한 선언
함수 만들기 + 이벤트리스너 (mouseenter
, mouseleave
)
마우스 이벤트에 반응 할 CSS 작성
classlist.add
와 remove
getBoundingClientRect()
를 활용한 엘리먼트 위치 및 크기 값 활용
const 선언 -불편한 바보 드롭다운이 되지 않기 위해선 a가 아닌, li태그 에 이벤트를 걸어줘야 한다.
-dropdownBackground 가 따라다녀야 함
-nav태그 의 위치값이 추후에 필요하므로 nav태그 또한 선언 필요
1 2 3 const triggers = document .querySelectorAll('.cool > li' );const background = document .querySelector('.dropdownBackground' );const nav = document .querySelector('.top' );
함수 & eventListener
💡 마우스 hover와 같은 기능이자 해당 이벤트가 엘리먼트에만 적용되는 mouseenter
, mouseleave
1 2 3 4 5 6 7 8 9 10 function handleEnter ( ) { console .log('Entered!' ); } function handleLeave ( ) { console .log('Leaved!' ); } triggers.forEach((li ) => li.addEventListener('mouseenter' , handleEnter)); triggers.forEach((li ) => li.addEventListener('mouseleave' , handleLeave));
CSS 작성하기 1 2 3 4 5 6 7 8 9 10 11 .trigger-enter .dropdown { display : block; } .trigger-enter-active .dropdown { opacity : 1 ; } .dropdownBackground .open { opacity : 1 ; }
classList.add & remove 1 2 3 4 5 6 7 8 9 10 function handleEnter ( ) { this .classList.add('trigger-enter' ); this .classList.add('trigger-enter-active' ); background.classList.add('open' ); } function handleLeave ( ) { this .classList.remove('trigger-enter' , 'trigger-enter-active' ); background.classList.remove('open' ); }
‼️ 하지만… 이렇게 코딩할 경우, transition 효과가 적용되지 않는다.
💡 display : block
에는 transition
이 먹히지 않기 때문에, block
이 된 후 opacity
에 변화를 줘야 ⇒ transition
이 적절하게 적용된다.
1 2 3 4 5 6 7 8 9 10 11 function handleEnter ( ) { this .classList.add('trigger-enter' ) setTimeout (()=> this .classList.add('trigger-enter' ) && this .classList.add('trigger-enter-active' ), 150 )')) background.classList.add(' open') }; function handleLeave(){ this.classList.remove(' trigger-enter', ' trigger-enter-active') background.classList.remove(' open') };
getBoundingClientRect()의 활용
💡 getBoundingClientRect()
= 엘리먼트의 넓이, 뷰포트를 기준으로 한 위치값(x,y) 등을 알 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function handleEnter ( ) { this .classList.add('trigger-enter' ); setTimeout ( () => this .classList.contains('trigger-enter' ) && this .classList.add('trigger-enter-active' ), 100 ); background.classList.add('open' ); const dropdown = this .querySelector('.dropdown' ); const dropdownCoords = dropdown.getBoundingClientRect(); const navCoords = nav.getBoundingClientRect(); background.style.setProperty('width' , `${dropdownCoords.width} px` ); background.style.setProperty('height' , `${dropdownCoords.height} px` ); background.style.setProperty( 'transform' , `translate(${dropdownCoords.left - navCoords.left} px, ${ dropdownCoords.top - navCoords.top } px)` ); } function handleLeave ( ) { this .classList.remove('trigger-enter' , 'trigger-enter-active' ); background.classList.remove('open' ); }
최종 완성 코드 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 const triggers = document .querySelectorAll('.cool > li' );const background = document .querySelector('.dropdownBackground' );const nav = document .querySelector('.top' );function handleEnter ( ) { this .classList.add('trigger-enter' ); setTimeout ( () => this .classList.contains('trigger-enter' ) && this .classList.add('trigger-enter-active' ), 100 ); background.classList.add('open' ); const dropdown = this .querySelector('.dropdown' ); const dropdownCoords = dropdown.getBoundingClientRect(); const navCoords = nav.getBoundingClientRect(); background.style.setProperty('width' , `${dropdownCoords.width} px` ); background.style.setProperty('height' , `${dropdownCoords.height} px` ); background.style.setProperty( 'transform' , `translate(${dropdownCoords.left - navCoords.left} px, ${ dropdownCoords.top - navCoords.top } px)` ); } function handleLeave ( ) { this .classList.remove('trigger-enter' , 'trigger-enter-active' ); background.classList.remove('open' ); } triggers.forEach((li ) => li.addEventListener('mouseenter' , handleEnter)); triggers.forEach((li ) => li.addEventListener('mouseleave' , handleLeave));