JavaScriptで本の検索Webアプリを作ってみましょう。
これまで学習してきた内容を十分に発揮することができ、実際に制作する過程の中で新しく疑問を持つきっかけにもなると思います。
今回の学習内容
本の検索Webアプリの概要
検索ワードで書籍を検索して結果一覧を表示し、本の詳細を確認することができるシンプルなWebアプリです。
一からデザインするのではなく、他のWebサービスに似せるように「◯◯風」なアプリを作成してみましょう。
本の検索結果を取得するために Google Books APIs を使用します。
また、他のWebサービス(例えばAmazonやApple、Netflix、Facebookなど)に似せて作ることで「模写」を行うことができます。この模写するメリットは、多くのユーザーを集めることに成功している既存のサービスがどのようなレイアウトで制作しているのか、コードを書きながら学習できるところです。
見本となる「Netflix風の本の検索Webアプリ」は こちら で確認することができます。このソースコードは GitHub で確認することができます。
トップページ
例を見てみましょう。
トップページは、ユーザーが検索したい「本のタイトル」を入力する機能があります。入力したクエリは次の「検索結果画面」に渡す必要があります。
- 画面のヘッダーです。ヘッダー内には「アプリのタイトル」と「外部リンク」を含んでいます。
【CSS】Flexbox(フレックスボックス) - アプリのタイトルです。
font-family
やcolor
を設定してデザインしてみましょう。
【CSS】マテリアルデザイン風ボタン - 外部リンクです。
【HTML】頻繁に使用するタグ - アプリのタイトルです。背景画像の中にFlexboxを使用して中央寄せしています。
【CSS】背景画像を作成 - アプリの概要です。この例は、デモのためスクールの概要を記載しています。
- 検索フォームです。ここに本のタイトルを入力します。
- サブミットボタンです。検索フォームに入力したクエリを「検索結果ページ」にURLパラメータとして渡します。
【CSS】マテリアルデザイン風ボタン
⑥の検索フォームは、form
タグでinput
タグを囲みます。
重要なのは input
タグのname
属性でここに設定した値がURLパラメータの名前になります。
例: name="query"
の場合は、?query=検索クエリ
となります。
また、form
タグのイベントハンドラに_onsubmit
を登録することで、検索クエリが未入力の状態での画面遷移を防止しています。
<form action="./results.html" onsubmit="_onsubmit(event);">
<input type="text" name="q" placeholder="タイトル" tabindex="0">
<button class="btn contained" tabindex="1">本を検索</button>
</form>
検索結果ページ
- トップ画面と同じ機能を持つ検索フォームです。
- どのキーワードで検索を行ったのかを表示しています。
- 検索結果の一覧を Google Books APIs から取得し、DOM操作で画面に表示しています。
【JS】Web API
【JS】配列と繰り返し処理
【JS】DOMの操作
また、気になる本を見つけたら「詳細画面」に飛べるように、それぞれのカードにリンクを設定しています。
パラメータを取得する
現在のURLから特定のパラメータを取得する場合に、以下の関数を使用します。
検索入力フォームの name
属性に設定している値をこの関数に渡すことで検索クエリの値を取得できます。
/**
* URLのパラメータから引数で指定されたパラメータ名の値を返却する.
* @param name {string}
* @return {string}
*/
function getParamater(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp('[\\?&]' + name + '=([^]*)'),
results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
本を検索する
Google Books APIsを使用して本を検索します。
URLのパラメータ q
に検索クエリを設定してリクエストします。
例では、リクエスト先のURLのパラメータに検索クエリに加えてその他のフィールドを複数設定していますが、よくわからない場合は、以下のように書き換えることもできます。
https://www.googleapis.com/books/v1/volumes?q=ドラえもん
/**
* 引数のクエリで本のリストを検索する.
*
* @param query {string}
*/
function searchBooksBy(query) {
if (!query) {
return []; // queryがないため検索を行わない.
}
// // https://developers.google.com/books/docs/v1/getting_started?hl=ja#rest-in-the-books-api
const by = 'relevance';
const fields = 'fields=items(id,volumeInfo/*,accessInfo(embeddable,country,viewability))';
fetch(`https://www.googleapis.com/books/v1/volumes?q=${query}&orderBy=${by}&${fields}&download=epub&maxResults=20`)
.then(res => res.json())
.then(data => _itemsChanged(data.items))
.catch((e) => console.error(e, `Search books fetch error. query is ${query}`));
}
詳細画面のリンクについて
レスポンスで受け取った結果はの items
には一覧に表示する書籍情報が配列で設定されています。
それぞれの本の id
の値を使用することでより詳細な情報をリクエストすることができるため、この id
を詳細画面に渡す必要があります。
この方法は、「トップ画面」から「検索結果画面」にパラメータを渡したのと同じ方法でリンク先のURLにパラメータを設定します。
レスポンスは以下の様になっています。(長いので上部分のみです)
この例では、id
は 8Oo7zQEACAAJ
となります。
{
"kind": "books#volumes",
"totalItems": 2184,
"items": [
{
"kind": "books#volume",
"id": "8Oo7zQEACAAJ",
"etag": "29A3owlhTy0",
"selfLink": "https://www.googleapis.com/books/v1/volumes/8Oo7zQEACAAJ",
"volumeInfo": {
"title": "ドラえもん",
"subtitle": "星野源 : 「映画ドラえもんのび太の宝島」主題歌 : 金管5 (6) 重奏",
"publishedDate": "2018",
"industryIdentifiers": []
}
}
]
}
詳細画面
- 「検索結果画面」から渡された
id
で本の詳細を Google Books APIs から取得し、画面に表示します。 - レスポンスにある「購買ページ」のリンクを設定しています。
本の詳細を取得する
本の詳細を取得するためには、本のid
が必要です。この id
は「検索結果画面」から渡されたものを使用します。
/**
* 引数のidで本の詳細を検索する.
*
* @param id {string}
*/
function searchBookBy(id) {
if (!id) {
return {}; // idがないため検索を行わない.
}
fetch(`https://www.googleapis.com/books/v1/volumes/${id}`)
.then(res => res.json())
.then(data => _itemChanged(data))
.catch((e) => console.error(e, `Search book fetch error. id is ${id}`));
}
詳細取得についての詳細なリファレンス
https://developers.google.com/books/docs/v1/reference/volumes/get
購買リンクの取得
本の詳細情報の中には書籍の販売情報が設定されています。
[data].saleInfo.buyLink
に設定されているURLが購買ページのリンクです。