๊ธ€ ์ž‘์„ฑ์ž: ํƒ์‹œ ์šด์ „์‚ฌ
๋ฐ˜์‘ํ˜•

๐Ÿคณ Day6 - JavaScript๋กœ ์ •๊ทœํ‘œํ˜„์‹์„ ์ด์šฉํ•œ ๊ฒ€์ƒ‰์ฐฝ ๊ตฌํ˜„ํ•˜๊ธฐ

JavaScript 30์˜ ์—ฌ์„ฏ ๋ฒˆ์งธ ํ”„๋กœ์ ํŠธ๋Š” ์ •๊ทœํ‘œํ˜„์‹์„ ์ด์šฉํ•˜์—ฌ ๊ฒ€์ƒ‰์ฐฝ์„ ๊ตฌํ˜„ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.

๐Ÿ˜ƒ HTML ์ฝ”๋“œ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Type Ahead ๐Ÿ‘€</title>
    <link rel="stylesheet" href="main.css" />
  </head>
  <body>
    <form class="search-form">
      <input type="text" class="search" placeholder="City or State" />
      <ul class="suggestions">
        <li>Filter for a city</li>
        <li>or a state</li>
      </ul>
    </form>
    <script src="main.js"></script>
  </body>
</html>
 
cs


๐Ÿ˜ƒ CSS ์ฝ”๋“œ

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
html {
  box-sizing: border-box;
  background: #ffc600;
  font-family: "helvetica neue";
  font-size: 20px;
  font-weight: 200;
}
*,
*:before,
*:after {
  box-sizing: inherit;
}
input {
  width: 100%;
  padding: 20px;
}
.search-form {
  max-width: 400px;
  margin: 50px auto;
}
input.search {
  margin: 0;
  text-align: center;
  outline: 0;
  border: 10px solid #f7f7f7;
  width: 120%;
  left: -10%;
  position: relative;
  top: 10px;
  z-index: 2;
  border-radius: 5px;
  font-size: 40px;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.19);
}
.suggestions {
  margin: 0;
  padding: 0;
  position: relative;
  /*perspective: 20px;*/
}
.suggestions li {
  background: white;
  list-style: none;
  border-bottom: 1px solid #d8d8d8;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.14);
  margin: 0;
  padding: 20px;
  transition: background 0.2s;
  display: flex;
  justify-content: space-between;
  text-transform: capitalize;
}
.suggestions li:nth-child(even) {
  transform: perspective(100px) rotateX(3deg) translateY(2px) scale(1.001);
  background: linear-gradient(to bottom, #ffffff 0%, #efefef 100%);
}
.suggestions li:nth-child(odd) {
  transform: perspective(100px) rotateX(-3deg) translateY(3px);
  background: linear-gradient(to top, #ffffff 0%, #efefef 100%);
}
span.population {
  font-size: 15px;
}
.hl {
  background: #ffc600;
}
 
cs

CSS๋Š” ์› ์ฝ”๋“œ๊ฐ€ ์ด๋ป์„œ ๊ทธ๋Œ€๋กœ ๋‚˜๋’€์Šต๋‹ˆ๋‹ค. transform๊ณผ linear-gradient๋ฅผ ์ด์šฉํ•˜์—ฌ ์ž…์ฒด๊ฐ์„ ์‚ด๋ ธ์Šต๋‹ˆ๋‹ค.

๐Ÿ˜ƒ JavaScript ์ฝ”๋“œ

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const endpoint =
  "https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json";
 
const cities = [];
 
fetch(endpoint)
  .then(blob => blob.json())
  .then(data => cities.push(...data));
 
findMatches = (wordToMatch, cities) =>
  cities.filter(place => {
    // ์ด ๊ณณ์—์„œ ๊ฒ€์ƒ‰์–ด์™€ ๋งค์น˜ ๋˜๋Š” ์ง€๋ฅผ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
    const regex = new RegExp(wordToMatch, "gi");
    return place.city.match(regex) || place.state.match(regex);
  });
 
numberWithCommas = x => {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
 
displayMatches = e => {
  const word = e.target.value;
  const matchArray = findMatches(word, cities);
  const html = matchArray
    .map(place => {
      const regex = new RegExp(word, "gi");
      const cityName = place.city.replace(
        regex,
        `<span class="hl">${word}</span>`
      );
      const stateName = place.state.replace(
        regex,
        `<span class="hl">${word}</span>`
      );
      return `
        <li>
            <span class="name">${cityName}, ${stateName}</span>
            <span class="population">${numberWithCommas(
              place.population
            )}</span>
        </li>
    `;
    })
    .join("");
  suggestions.innerHTML = html;
};
 
const searchInput = document.querySelector(".search");
const suggestions = document.querySelector(".suggestions");
 
searchInput.addEventListener("change", displayMatches);
searchInput.addEventListener("keyup", displayMatches);
 
cs

์šฐ์„  ๊ธฐ์กด์— ์žˆ๋Š” api์˜ endpoint๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ cities์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.


๊ทธ ๋’ค์— findMatches๋ผ๋Š” cities ๋ฐฐ์—ด์—์„œ ํŠน์ • ๋‹จ์–ด๋ฅผ ์ฐพ๋Š” ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ new RegExp(wordToMatch, "gi")๋ฅผ ์ด์šฉํ•ด ์ „์ฒด์—์„œ wordToMatch์™€ ๋น„๊ตํ•˜์—ฌ ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„์—†์ด ๊ฐ™์€ ๋‹จ์–ด๊ฐ€ ์žˆ๋Š” ๊ณณ์„ ์ฐพ๋Š” ์ •๊ทœ ํ‘œํ˜„์‹์„ ๋งŒ๋“ค๊ณ , cities์˜ ๊ฐ place๋ณ„ city์™€ state์— ๋งค์น˜๋˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋Š” ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.


๋‹ค์Œ์€ ๊ฒ€์ƒ‰์ฐฝ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. search ํด๋ž˜์Šค์— ํ•ด๋‹นํ•˜๋Š” input์„ searchInput์ด๋ผ ํ•˜๊ณ  change์™€ keyup์˜ ์ด๋ฒคํŠธ์— ์‹ค์ œ ๋ถˆ๋Ÿฌ์˜ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” displayMatches๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.


displayMatches๋Š” ์šฐ์„  ์•ž์—์„œ ๊ตฌํ˜„ํ•œ findMatches๋ฅผ ์ด์šฉํ•ด ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ๊ฒ€์ƒ‰์–ด๊ฐ€ ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด์˜ ์–ด๋Š ๋ถ€๋ถ„๊ณผ ๊ฐ™์€ ์ง€ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด replace๋ฅผ ์ด์šฉํ•˜์—ฌ ๋งค์น˜๋˜๋Š” ๋‹จ์–ด๋ฅผ ํ•˜์ด๋ผ์ดํŠธ ํƒœ๊ทธ๋กœ ๊ฐ์‹ผ ๋‹จ์–ด๋กœ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ํ•˜์ด๋ผ์ดํŠธ ๋œ ๋‹จ์–ด๋“ค์„ ํฌํ•จํ•œ ์ „์ฒด ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด์— ํ•ด๋‹นํ•˜๋Š” html ํƒœ๊ทธ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ค๋‹ˆ๋‹ค.


๋งํฌ


๋ฐ˜์‘ํ˜•