ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Type Ahead ⌨
    Web/JS30 2020. 9. 25. 19:28
    반응형

    이번에는 프론트엔드 코딩테스트로도 자주 등장하는 자동완성 기능에 대한 예제이다.

    검색창에다가 특정 단어를 입력하면, 해당 단어로 이루어진 도시나 주를 찾아서 화면에 보여주는 방식이다.

     

    우선, 데이터를 불러올 URL을 변수에 저장해둔다.

    그리고 불러온 데이터를 저장해 둘 배열을 만들어 둔다.

    const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';
    
    const cities = [];

    데이터를 fetch하는 데는 fetch API를 사용한다.

    사실 나는 이때까지 데이터를 불러오는 가장 좋은 방법이 axios인 줄 알았는데

    최근에 와서 브라우저에 새로 fetch라는 API가 내장되었다고 한다. 사용법이 거의 비슷하다.

    따로 import를 할 필요도 없으니, 앞으로는 이 fetch API를 애용해야겠다.

     

    fetch는 promise를 return한다.

    그래서, 데이터를 fetch하고나서 할 일은 당연히 .then() 안에다가 적어주면 된다.

    여기서 날라오는 response는 Response 객체인데, 우리는 이미 데이터가 JSON 형식의 데이터라는 것을 알고 있다.

    마침 Response 객체의 prototype을 뒤져보면, json()이라는 메소드가 보인다.

    이 메소드를 이용해서 Response 객체를 JSON 데이터로 변환해줘야 한다.

    fetch(endpoint)
      .then(res => res.json())
      .then(data => cities.push(...data)); 

    그러면 res.json()은 또 다른 promise를 return하므로, 다시 .then()을 사용해서 받아주면 된다.

    참고로, data는 배열의 형태로 저장되어있기 때문에, 해당 배열의 내용들을 spread로 펼쳐서

    미리 만들어뒀던 cities 배열에다가 집어넣어주면 된다.

    그 다음은 불러온 데이터에서 우리가 검색창에 입력한 단어를 포함하는 city명이나 state명을 찾아야 한다.

    이를 위해서는 정규표현식(Regular Expression)을 사용해야 하는데, 정규표현식에 변수를 어떻게 집어넣어야 할까?

    그냥 '/정규표현식/' 꼴을 사용해서는 변수를 집어넣을 수가 없고, 새로 정규표현식 객체를 만들어줘야 한다.

    사용법은 다음과 같다.

    const regex = new RegExp(wordToMatch, 'gi');

    wordToMatch는 우리가 검색창에 입력한 내용이다.

    두 번째 인자로 들어오는 문자열은 option인데, 'g'는 'global'을 의미하며, 대상 문자열 전체를 범위로 하겠다는 뜻이다.

    'i'는 'insensitive'를 의미하며, 대소문자 상관없이 일치하는 문자열을 찾겠다는 의미이다.

     

    city명이나 state명 안에 사용자가 검색한 단어가 포함되어있으면, 걔네들을 return해주는 함수를 만든다.

    function findMatches(wordToMatch, cities) {
      return cities.filter(place => {
        const regex = new RegExp(wordToMatch, 'gi');
        return place.city.match(regex) || place.state.match(regex);
      });
    }

    그 다음은, 검색창의 내용이 바뀔 때마다 findMatches 함수를 호출해서

    일치하는 이름을 찾아, 화면에 뿌려주는 함수를 작성할 차례이다.

    const searchInput = document.querySelector('.search');
    const suggestions = document.querySelector('.suggestions');
    
    function displayMatches() {
      const matchArray = findMatches(this.value, cities);
      const html = matchArray.map(place => {
        const regex = new RegExp(this.value, 'gi');
        const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
        const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);
        return `
          <li>
            <span class="name">${cityName}, ${stateName}</span>
            <span class="population">${Number(place.population).toLocaleString()}</span>
          </li>
        `;
      }).join(''); 
    
      suggestions.innerHTML = html;
    }

    일단 input의 value를 이용해서 findMatches 함수를 호출해주고, 결과값 배열을 변수에다가 담는다.

    그다음, 화면에 뿌려주기 위해 html 태그로 마크업을 해야 하는데, 이 때 input.value와 일치하는 부분에는

    노란색으로 강조 표시를 해서 어떤 부분이 일치하는지를 눈에 띄게 만들어준다.

    (hl 클래스가 바로 강조되는 부분을 감싸는 클래스)

     

    map 메소드는 문자열로 이루어진 배열을 return하기 때문에, join('')을 통해 하나의 문자열로 만들어줬다.

     

    그 다음, 인구 수도 표시를 해줄건데, 천 단위마다 콤마를 찍기 위해서

    인구수 데이터를 숫자로 만들어 준 후, toLocaleString() 함수를 호출해줬다.

    숫자에다가 toLocaleString()메소드를 호출해주면 천 단위마다 콤마를 찍어준다.

    참고로, stackoverflow에 정규표현식을 이용한 방법도 있으니 선호하는 방법을 사용하면 된다.

    // stackoverflow에서 구해온, 숫자에 ','를 찍어주는 변환 함수.
    // toLocaleString()과 동일한 효과.
    function numberWithCommas(x) {
      return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    }

    그리고 마지막으로 검색창에다가 이벤트 리스너를 붙여주면 되는데,

    검색창의 값이 바뀔 때마다 함수를 호출하기 위해 'change' 이벤트에다가 붙여주면,

    키보드를 치는 도중에는 함수가 호출되지 않으며 값을 다 입력하고 엔터를 쳐야만 함수가 호출된다.

    키보드로 값을 입력하는 도중에도 실시간으로 함수가 호출되어 화면에 결과를 표시하기 위해서는

    keyup 이벤트도 함께 붙여줘야 한다는 것을 잊지 말자.

    searchInput.addEventListener('change', displayMatches);
    searchInput.addEventListener('keyup', displayMatches);

     

    반응형

    'Web > JS30' 카테고리의 다른 글

    Fun with HTML5 Canvas 🎨  (0) 2020.11.01
    Array Cardio Day 2 🎈  (0) 2020.10.30
    Flex Panel Gallery 🎫  (0) 2020.09.21
    Array Cardio Day 1 🎈  (0) 2020.09.21
    Update CSS variables with JS 🎨  (0) 2020.09.20

    댓글

Designed by Tistory.