こんにちは。KOUKIです。
本記事は、Udemyの「50 Projects In 50 Days – HTML, CSS & JavaScript」で学習したことを載せています。
<目次>
実装するもの
今回は、フィルタリングアプリを実装します。
RANDOM USER GENERATORというAPIがあって、そこからユーザーデータを取得します。そして、それをリスト化し、任意の文字列でフィルタリングするアプリケーションです。
demoは「こちら」で確認できます。
環境構築
簡単な環境構築をお願いします。
必要なファイルは、以下の通りです。
1 2 3 4 5 6 |
$ tree . ├── index.html ├── script.js └── style.css |
CSS版
ページ(HTML)の作成
最初にページを作成しましょう。
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 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="style.css" /> <title>Live User Filter</title> </head> <body> <div class="container"> <header class="header"> <h4 class="title">Live User Filter</h4> <small class="subtitle">Search by name and/or location</small> <input type="text" id="filter" placeholder="Search"> </header> <ul id="result" class="user-list"> <li> <h3>Loading...</h3> </li> </ul> </div> <script src="script.js"></script> </body> </html> |
このHTMLをブラウザ上で表示すると以下のようになります。

スタイル(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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); * { box-sizing: border-box; } body { background-color: #f8f9fd; font-family: 'Roboto', sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; overflow: hidden; margin: 0; } .container { border-radius: 5px; box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2); overflow: hidden; width: 300px; } .title { margin: 0; } .subtitle { display: inline-block; margin: 5px 0 20px; opacity: 0.8; } .header { background-color: #3e57db; color: #fff; padding: 30px 20px; } .header input { background-color: rgba(0, 0, 0, 0.3); border: 0; border-radius: 50px; color: #fff; font-size: 14px; padding: 10px 15px; width: 100%; } .header input:focus { outline: none; } .user-list { background-color: #fff; list-style-type: none; margin: 0; padding: 0; max-height: 400px; overflow-y: auto; } .user-list li { display: flex; padding: 20px; } .user-list img { border-radius: 50%; object-fit: cover; height: 50px; width: 50px; } .user-list .user-info { margin-left: 10px; } .user-list .user-info h4 { margin: 0 0 10px; } .user-list .user-info p { font-size: 12px; } .user-list li:not(:last-of-type) { border-bottom: 1px solid #eee; } .user-list li.hide { display: none; } |
ここまで実装すると以下のようになります。

JavaScriptの実装
前述の通り、RANDOM USER GENERATORからユーザーデータを取得します。
「ページを開いた時にAPIを叩く->ユーザーデータ取得->リスト化->フィルタリング」の流れになります。
要素を取得する
最初に、画面操作に必要な要素を取得します。
1 2 3 |
// 要素の取得 const result = document.getElementById('result') const filter = document.getElementById('filter') |
ユーザーデータの取得
ユーザーデータを取得しましょう。
1 2 3 4 5 6 7 8 9 10 |
// 取得データ格納場所 const listItems = [] async function getData() { // 50件のユーザーデータを取得 const res = await fetch('https://randomuser.me/api?results=50') // 取得したデータをJSONに変更 const { results } = await res.json() } |
「https://randomuser.me/api?results=50」は、APIのリクエスト先です。50件のデータを取得できるようにしています。
async/awaitは非同期関数です。通常は、他のコードと別に実行されるようになりますが、awaitでAPIからのレスポンスが返却されるまで待機させてます。
ユーザーリストの作成
次は、取得したデータを元にユーザーリストを作成しましょう。
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 |
async function getData() { // 50件のユーザーデータを取得 // 取得したデータをJSONに変更 // リストのクリア result.innerHTML = '' // 取得データごとに処理 results.forEach(user => { // li要素を作成 const li = document.createElement('li') // リストアイテムとして追加 listItems.push(li) // コンテンツ作成 li.innerHTML = ` <img src="${user.picture.large}" alt="${user.name.first}"> <div class="user-info"> <h4>${user.name.first} ${user.name.last}</h4> <p>${user.location.city}, ${user.location.country}</p> </div> ` // resultの子要素として追加 result.appendChild(li) }) } |
これはもう定番の処理ですね。createElementメソッドでリストを作成して、appendChildメソッドで要素を追加しています。
「user.name.first」などは、JavaScriptのテンプレートリテラルを使って、APIから取得したデータを埋め込んでいます。
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 |
// 取得データ { "gender": "male", "name": { "title": "Mr", "first": "Hudson", "last": "Hughes" }, "location": { "street": { "number": 9153, "name": "Green Lane West" }, "city": "Greymouth", "state": "Tasman", "country": "New Zealand", "postcode": 69099, "coordinates": { "latitude": "59.0673", "longitude": "-46.5832" }, "timezone": { "offset": "+9:30", "description": "Adelaide, Darwin" } }, "email": "hudson.hughes@example.com", "login": { "uuid": "74ee53ca-7463-41d4-b81c-e4b2d4918f2d", "username": "bigrabbit919", "password": "latina", "salt": "Btznf3sn", "md5": "c5d64ec43efafdad45dc2060af3962c0", "sha1": "4c457df3b1a99076daa74064f1770cd7b40212b2", "sha256": "953d00f1cde289f662903746743246c608e5cb907f067b57b56ed3231b313b12" }, "dob": { "date": "1960-09-14T13:05:09.700Z", "age": 61 }, "registered": { "date": "2015-04-05T11:23:40.626Z", "age": 6 }, "phone": "(825)-059-1838", "cell": "(386)-342-9561", "id": { "name": "", "value": null }, "picture": { "large": "https://randomuser.me/api/portraits/men/13.jpg", "medium": "https://randomuser.me/api/portraits/med/men/13.jpg", "thumbnail": "https://randomuser.me/api/portraits/thumb/men/13.jpg" }, "nat": "NZ" } |
getData関数をページが読み込まれた時に実行するようにしておきましょう。
1 2 3 4 |
getData() async function getData() { .... } |
フィルタリング機能
次に、フィルタリング機能を作成しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// フィルタリング機能 function filterData(searchTerm) { // listItemsのデータ分だけループ listItems.forEach(item => { // searchTermの文字が含まれているかチェック if(item.innerText.toLowerCase().includes(searchTerm.toLowerCase())) { // hideクラスを除去 item.classList.remove('hide') } else { // hideクラスを付与 item.classList.add('hide') } }) } |
フィルタリングと言っても実際にデータを削除しているわけではなく、hideクラス(CSSのスタイル)で要素の表示・非表示を切り替えています。
フィルタリングするデータもAPIから再取得せず、取得したデータの中で処理しています。
自己学習レベルならこの実装でも問題ないと思いますが、実務の場合はフィルタリングをする度にAPIからデータを取得しなおしてください。そうでないと、API側でデータの変更があった時に、その変更を画面に反映できません。
inputイベントの登録
最後にinputイベントを登録します。これは、検索窓から文字を打ち込んだ時に発火するイベントです。
1 2 |
// inputイベントの登録 filter.addEventListener('input', (e) => filterData(e.target.value)) |
これで、完成です。
おわりに
フィルタリング処理は、実務でも実装する機会が結構あります。データがめちゃくちゃ多くなった時に便利なんですよ。
あとは、オートコンプリートやページネーションを覚えれば、完璧かもですね^^
それでは、また!
JavaScriptまとめ
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 54 55 56 57 58 59 |
// 要素の取得 const result = document.getElementById('result') const filter = document.getElementById('filter') // 取得データ格納場所 const listItems = [] getData() // inputイベントの登録 filter.addEventListener('input', (e) => filterData(e.target.value)) async function getData() { // 50件のユーザーデータを取得 const res = await fetch('https://randomuser.me/api?results=50') // 取得したデータをJSONに変更 const { results } = await res.json() // リストのクリア result.innerHTML = '' // 取得データごとに処理 results.forEach(user => { // li要素を作成 const li = document.createElement('li') // リストアイテムとして追加 listItems.push(li) // コンテンツ作成 li.innerHTML = ` <img src="${user.picture.large}" alt="${user.name.first}"> <div class="user-info"> <h4>${user.name.first} ${user.name.last}</h4> <p>${user.location.city}, ${user.location.country}</p> </div> ` // resultの子要素として追加 result.appendChild(li) }) } // フィルタリング機能 function filterData(searchTerm) { // listItemsのデータ分だけループ listItems.forEach(item => { // searchTermの文字が含まれているかチェック if(item.innerText.toLowerCase().includes(searchTerm.toLowerCase())) { // hideクラスを除去 item.classList.remove('hide') } else { // hideクラスを付与 item.classList.add('hide') } }) } |
コメントを残す
コメントを投稿するにはログインしてください。