인스타그램의 search input을 구현하는 작업이었다. 값을 입력할때마다 그 값을 포함하는 배열을 새로 만들어 리스트로 보여주는 형식이다. 아직 서버에서 데이터를 가져오는 방법은 모르기 때문에, 아이디와 닉네임, 사진이 담긴 배열을 자바스크립트에서 선언하여 작업했다.
추가 구현 사항을 진행하기 전 실제 인스타그램 search input을 분석해보았다.
가상의 계정 데이터 배열은 다음과 같이 구성하였다. array 안에 개개인의 user 정보값이 object로 묶인 형태이다.
// 계정 데이터 배열
const userArray = [
{id: "todayis_wendy",
nickname: "Wendy",
picture: "https://#"},
{id: "wecode_bootcamp",
nickname: ">wecode | 위코드",
picture: "https://#"},
{id: "thisisyourhyung",
nickname: "JIHYUNG LEE",
picture: "https://#"},
...
]
.filter()
메소드는 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환한다. 어떤 요소도 테스트를 통과하지 못하면 빈 배열을 반환한다. 배열의 모든 요소를 looping 한다는 점에서 .forEach()
, .map()
메소드와 유사하다고 볼 수 있다.
function isBigEnough(value) {
return value >= 10;
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// filtered 는 [12, 130, 44]
function matchSearch(value) {
const searchID = searchInput.value;
return value.indexOf(searchID) != -1;
}
value 인자(계정 데이터 배열의 id value)를 받아서 searchInput의 value가 포함되어 있는지 boolean 형태로 리턴해주는 함수이다.
function showFilteredID(id) {
resultBox.style.display = "flex";
const filteredUser = document.createElement('li');
filteredUser.innerHTML = `
<img class="img-profile" src=${id.picture} alt=${id.id}님의 프로필 사진">
<div class="profile-text">
<span class="userID point-span">${id.id}</span>
<span class="sub-span">${id.nickname}</span>
</div>`
searchList.appendChild(filteredUser);
}
위의 인스타그램 분석에서 나왔듯이 결과 리스트 컨테이너는 search input에 값이 있을때만 등장한다. 이를 위해 결과 리스트가 담길 div
컨테이너에 display
속성을 부여하여, display: none
으로 보이지 않았던 컨테이너가 나타나게 만들었다. li
element를 생성, 그 안에 받아온 배열의 value
값을 innerHTML
로 넣어주고, searchList
라는 ul
태그 안에 appendChild
해준다.
searchInput.addEventListener('keyup', function() {
searchList.innerHTML = '';
resultBox.style.display = "none";
if (searchInput.value) {
const filteredID = userArray.filter( x => matchSearch(x.id))
if (filteredID) {
filteredID.forEach(function(e) {
showFilteredID(e)
})
}
}
})
드디어 array.filter()
가 등장한다! searchInput에 keyup 이벤트 발생시, 인풋값이 있는 경우에만 matchSearch()
함수로 필터링 된 새로운 배열인 filteredID
가 생성된다. 이렇게 필터링 된 배열은 (배열이 있는 경우에만) showFilteredID()
함수를 거쳐 ul 리스트 안에 들어갈 수 있는 형태로 재구성되는 것이다.
searchInput.addEventListener('focusout', function() {
resultBox.style.display = "none";
})
인풋에서 포커스아웃 되는 순간 resultBox에 display: none;
이 적용된다.
새로 배운 filter 메소드를 사용해볼 수 있어 재미있는 작업이었다. 아이디 검색 기능을 구현하는 자체보다는 인스타그램과 동일한 UI를 만드는게 더 어려웠던 것 같다. 개발자 도구 element 탭으로 인스타그램을 계속 분석했는데 css의 z-index
속성을 새로 알게 되었다.
x축, y축, z축 할때 그 z라고 생각하면 쉽다. 화면은 평평하지만 그 안에서 개체들이 쌓이는 순서를 정해준다고 생각하면 된다. position
이 static
외의 다른 값인 요소에 모두 지정할 수 있으며, 값이 클수록 위로 쌓여 올라간다. 정수만 사용이 가능하다.
디폴트값은 auto이며 이 경우 새로운 쌓인 맥락을 생성하지 않고 부모 요소와 동일한 쌓임 맥락에서의 위치를 갖는다. 자식 요소의 z-index
값은 자기 외의 바깥요소와 비교하지 않는다.
결과 컨테이너 박스의 모양 (말풍선 같은?) 형태를 만들기 위해 z-index
속성을 사용했다.
.ui-little-diamond {
width: 14px;
height: 14px;
padding: 1px;
position: absolute;
margin: auto;
left: 0;
right: -20px;
top: 12px;
transform: rotate(45deg);
background: #fff;
content: " ";
box-shadow: 0 0 5px 1px rgba(0, 0, 0, .1);
z-index: 1;
}
.upper {
box-shadow: none;
z-index: 3;
}
.input-result-container {
width: 243px;
max-height: 365px;
position: absolute;
top: 18px;
right: -30px;
background: #fff;
border: 1px solid #DBDBDB;
border-radius: 3px;
box-shadow: 0 0 5px rgba(0,0,0,.1);
z-index: 2;
}