
<div class="search-wrap">
<div class="search">
<div class="shadow"></div>
<div class="textfield">
<input type="text" placeholder="apple.com 검색">
<div class="search-icon"></div>
<div class="search-closer"></div>
</div>
<div class="autocompletes">
<h3>빠른 링크</h3>
<ul>
<li><a href="javascript:void(0)">Apple Store Online에서 쇼핑하기</a></li>
<li><a href="javascript:void(0)">Apple Visoin Pro</a></li>
<li><a href="javascript:void(0)">Airpods</a></li>
<li><a href="javascript:void(0)">Apple Intelligence</a></li>
<li><a href="javascript:void(0)">Apple Trade In</a></li>
</ul>
</div>
</div>
</div>
header .search-wrap {
position: absolute;
top: 0;
left: 0;
width: 100%;
visibility: hidden;
opacity: 0;
transition: 0.4s;
}
header .search {
max-width: 680px;
margin: 0 auto;
position: relative;
}
header .search .shadow {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
}
header .search .textfield {
position: relative;
}
header .search input {
width: 100%;
height: 44px;
padding: 0 40px;
border: none;
outline: none;
box-sizing: border-box;
background-color: transparent;
font-size: 17px;
color: #FFF;
}
header .search .search-icon {
width: 40px;
height: 44px;
background-image: url("../images/header_search.svg");
background-repeat: no-repeat;
background-position: center;
position: absolute;
top: 0;
left: 0;
opacity: 0.4;
}
header .search .search-closer {
width: 40px;
height: 44px;
background-image: url("../images/header_close.svg");
background-repeat: no-repeat;
background-position: center;
position: absolute;
top: 0;
right: 0;
opacity: 0.4;
cursor: pointer;
}
header .search .search-closer:hover {
opacity: 1;
}
header .search .autocompletes {
width: 100%;
padding: 26px 40px 20px;
border-radius: 0 0 18px 18px;
box-sizing: border-box;
background-color: #FFF;
position: absolute;
top: 44px;
left: 0;
}
header .search .autocompletes h3 {
font-size: 12px;
color: #6E6E6E;
margin-bottom: 12px;
}
header .search .autocompletes ul li a {
display: block;
margin: 0 -14px;
padding: 10px 0 10px 30px;
font-size: 14px;
cursor: pointer;
}
header .search .autocompletes ul li a:hover {
background-color: #F5F5F5;
font-weight: 500;
}
header .search input,
header .search .search-icon,
header .search .autocompletes h3,
header .search .autocompletes li {
transition: 0.6s;
transform: translate(100px, 0);
}
header.searching ul.menu > li {
transform: scale(0.7);
opacity: 0;
}
header.searching .search-wrap {
visibility: visible;
opacity: 1;
transition-delay: 0.2s;
}
header.searching .search input,
header.searching .search .search-icon,
header.searching .search .autocompletes h3,
header.searching .search .autocompletes li {
transform: translate(0, 0);
transition-delay: 0.2s;
}
const headerEl = document.querySelector('header')
const headerMenuEls = [...headerEl.querySelectorAll('ul.menu > li')]
const searchWrapEl = headerEl.querySelector('.search-wrap')
const searchStartEl = headerEl.querySelector('.search-starter')
const searchCloserEl = searchWrapEl.querySelector('.search-closer')
const searchSchadowEl = searchWrapEl.querySelector('.shadow')
const searchInputRl = searchWrapEl.querySelector('input')
const searchDelayEls = [...searchWrapEl.querySelectorAll('li')]
searchStartEl.addEventListener('click', showSearch)
searchCloserEl.addEventListener('click', hideSearch)
searchSchadowEl.addEventListener('click', hideSearch)
function showSearch() {
headerEl.classList.add('searching')
document.documentElement.classList.add('fixed')
}
function hideSearch() {
headerEl.classList.remove('searching')
document.documentElement.classList.remove('fixed')
}
html.fixed {
position: fixed;
overflow-y: scroll;
width: 100%;
}
function showSearch() {
headerEl.classList.add('searching')
document.documentElement.classList.add('fixed')
}
function hideSearch() {
headerEl.classList.remove('searching')
document.documentElement.classList.remove('fixed')
}
위 코드에서는 showSearch() 함수를 통해 검색바가 보이면 html에 fixed 클래스를 추가하고 hideSearch() 함수를 통해 검색바가 없어지면 html에 fixed 클래스를 제거한다.
이때 html에 fixed 클래스가 추가되었다면 css에서 position: fixed; 에 의해 html은 현재 위치에 고정되게 되고 overflow-y: scroll에 의해 y축(높이)에는 스크롤이 등장하게된다. 하지만 html은 현재 위치에 고정되어 있지 때문에 스크롤을 할 수 없는 상태가 된다.

검색 버튼을 터치하였을 경우 메뉴 버튼이 우측에서부터 사라지고 검색 바가 상단에서부터 출력되고 있다.
header .search input,
header .search .search-icon,
header .search .autocompletes h3,
header .search .autocompletes li {
transition: 0.6s;
transform: translate(100px, 0);
}
위 코드를 통해 검색 바는 기본적으로 x축 기준으로 100px만큼 우측으로 이동해있는 상태이다.
function showSearch() {
headerEl.classList.add('searching')
document.documentElement.classList.add('fixed')
headerMenuEls.reverse().forEach(function(el, index) {
el.style.transitionDelay = index * 0.4 / headerMenuEls.length + 's'
})
searchDelayEls.forEach(function (el, index) {
el.style.transitionDelay = index * 0.4 / searchDelayEls.length + 's'
})
setTimeout(function(){
searchInputRl.focus()
},600)
}
만약 검색 버튼을 누르면 showSearch() 함수가 실행되고 css의 header의 클래스에 searching이 추가된다.
header.searching ul.menu > li {
transform: scale(0.7);
opacity: 0;
}
header.searching .search-wrap {
visibility: visible;
opacity: 1;
transition-delay: 0.2s;
}
header.searching .search input,
header.searching .search .search-icon,
header.searching .search .autocompletes h3,
header.searching .search .autocompletes li {
transform: translate(0, 0);
transition-delay: 0.2s;
}
header에 searching이 추가되면서 검색창에 스타일이 변화가 생기게 되는데, opacity: 1; 보이지 않던 검색 바가 보이게 되고 transform: translate(0, 0);를 통해 x축으로 이동해 있던 검색 바가 원위치로 이동하게 된다.
function showSearch() {
headerMenuEls.reverse().forEach(function(el, index) {
el.style.transitionDelay = index * 0.4 / headerMenuEls.length + 's'
})
searchDelayEls.forEach(function (el, index) {
el.style.transitionDelay = index * 0.4 / searchDelayEls.length + 's'
})
}
function hideSearch() {
headerMenuEls.reverse().forEach(function(el, index) {
el.style.transitionDelay = index * 0.4 / headerMenuEls.length + 's'
})
searchDelayEls.reverse().forEach(function (el, index) {
el.style.transitionDelay = index * 0.4 / searchDelayEls.length + 's'
})
searchDelayEls.reverse()
}
transitionDelay 함수는 CSS 전환이 시작되기 전까지의 지연 시간을 나타낸다.
페이지에서는 검색창 등장 시 기존 메뉴가 한개씩 사라지고 검색바의 검색어가 하나씩 등장하는 효과가 보여지고 있다. 이 경우에는 배열을 이용할 필요가 있다.
const searchDelayEls = [...searchWrapEl.querySelectorAll('li')]
searchWrapEl 안의 모든 li요소를 사용하는데 전개 연산자 [...]를 사용하여 해당 요소들을 배열로 사용할 수 있다.
searchDelayEls.reverse().forEach를 사용하여 배열의 각 요소에 transitionDelay를 지정할 수 있다.
el.style.transitionDelay = index * 0.4 / searchDelayEls.length + 's' 함수를 통해 각 배열 별로 계산을 하여 검색창이 1개씩 등장하는 느낌을 줄 수 있게된다.
ex) 해당 페이지의 searchDelayEls.length 값은 12이다.
첫번째 배열의 경우 index = 0이다. 그렇기에 첫번째 배열은 0 0.4 / 12 +'s' 이기에 0s이다.
두번째 배역의경우 index = 1이다. 그렇기에 첫번째 배열은 1 0.4 / 12 +'s' 이기에 0.0333s이다.
searchDelayEls.reverse().forEach 에서 reverse() 사용 이유?검색 버튼을 터치하였을 경우 메뉴 버튼이 우측에서부터 사라지고 검색 바가 상단에서부터 출력되고 있다.
만약 reverse() 사용하지 않았다면 검색 버튼 터치 시 메뉴 버튼은 우측부터가 아닌 좌측에서부터 사라지게 될 것이다.
마찬가지로 검색 바가 사라졌을 때 검색 바가 하단에서부터 사라지고 메뉴 버튼은 좌측에서부터 등장하고 있다.
검색 바가 없어졌을 때의 함수에 reverse()를 사용함으로써 검색 바가 사라졌을 경우 상단이 아닌 하단부터 사라지고 메뉴버튼이 좌측에서부터 등장하도록 하고 있다.

검색 버튼을 터치했을 경우 자동으로 검색 바에 포커싱이 되도록 구성할 것이다.
const headerEl = document.querySelector('header')
const searchWrapEl = headerEl.querySelector('.search-wrap')
const searchInputRl = searchWrapEl.querySelector('input')
function showSearch() {
headerEl.classList.add('searching')
document.documentElement.classList.add('fixed')
headerMenuEls.reverse().forEach(function(el, index) {
el.style.transitionDelay = index * 0.4 / headerMenuEls.length + 's'
})
searchDelayEls.forEach(function (el, index) {
el.style.transitionDelay = index * 0.4 / searchDelayEls.length + 's'
})
setTimeout(function(){
searchInputRl.focus()
},600)
}
function hideSearch() {
headerEl.classList.remove('searching')
document.documentElement.classList.remove('fixed')
headerMenuEls.reverse().forEach(function(el, index) {
el.style.transitionDelay = index * 0.4 / headerMenuEls.length + 's'
})
searchDelayEls.reverse().forEach(function (el, index) {
el.style.transitionDelay = index * 0.4 / searchDelayEls.length + 's'
})
searchDelayEls.reverse()
setTimeout(function(){
searchInputRl.value = ''
},600)
}
위 코드에서 searchInputRl.focus()을 통해 해당 창에 포커싱하도록 할 수 있다.
하지만 위 코드에서는 버튼을 터치하였을 경우 0.4s 뒤에 검색창이 출력되기에 포커싱이 되지 않는 오류가 생긴다.
이 경우에는 setTimeoutset 함수를 사용하면된다.
setTimeoutset 함수는 setTimeoutset(함수(), 시간(ms)) 형식으로 사용하는데 특정 시간 뒤에 함수() 영역이 실행된다고 할 수 있다.
setTimeout(function(){
searchInputRl.focus()
},600)
위 코드에서는 0.6초 뒤에 searchInputRl.focus() 함수가 실행되어 검색 창에 포커싱이 되도록 할 수 있다.
또한 검색 창을 끄고 닫으면 이전에 검색했던 단어가 남아있는 오류가 생길 수 있다.
이럴경우 검색창을 닫는 함수 뒤에 searchInputRl.value = ''을 추가하여 입력한 값을 초기화 해줄 수 있다.