12日目のゴールとテーマ
12日目のテーマは「外部データを“取ってきて使う”体験をする」です。
ここまでで、ブラウザの中だけで完結するアプリ(名簿・タスク)を作ってきました。
今日は一歩外に出て、
外部サービスからデータを取ってくる
JSON というデータ形式を読む
非同期処理(すぐには終わらない処理)と少し仲良くなる
これを、小さな「ランダム名言アプリ」を作りながら体験していきます。
今日作る「ランダム名言アプリ」のイメージ
どんな動きをするアプリかを先に言葉で決める
まずは仕様を言葉で決めます。
画面に「名言を取得する」ボタンがある。
ボタンを押すと、外部のAPI(名言サービス)からデータを取ってくる。
取得した名言と作者を画面に表示する。
通信中は「読み込み中…」と表示しておく。
エラーが起きたら「取得に失敗しました」と表示する。
ここで大事なのは、「外部からデータを取ってくる」という一点です。
名言じゃなくても、天気でもニュースでもいいのですが、
今日はシンプルに「テキスト+作者」の形で進めます。
API と JSON をざっくりイメージで理解する
「データの自動販売機」と「データの箱」
難しい言葉を一気に覚える必要はありません。
イメージだけ持っておきましょう。
API というのは、「データの自動販売機」のようなものです。
決められたURLに「ください」とアクセスすると、
決められた形でデータを返してくれます。
その「決められた形」が JSON です。
JSON は、JavaScript のオブジェクトや配列にとてもよく似た「データの箱」の書き方です。
例えば、こんな感じです。
{
"text": "人生とは自転車のようなものだ。倒れないようにするには走り続けなければならない。",
"author": "アルベルト・アインシュタイン"
}
これは、「text というキーに名言の本文」「author というキーに作者名」が入っている箱です。
JavaScript では、これをオブジェクトとして扱えます。
HTML の土台を用意する
ボタンと表示エリアだけのシンプルな画面
index.html を次のように用意します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>12日目:ランダム名言アプリ</title>
</head>
<body>
<h1>ランダム名言アプリ</h1>
<div>
<button id="fetch-quote-button">名言を取得する</button>
</div>
<h2>結果</h2>
<div id="status-area">
ボタンを押して名言を取得してください。
</div>
<div id="quote-area"></div>
<div id="author-area"></div>
<script src="main.js"></script>
</body>
</html>
HTMLここで押さえておきたいのは、三つの表示エリアです。
status-area
今の状態(待機中・読み込み中・成功・失敗)を表示する場所。
quote-area
名言の本文を表示する場所。
author-area
作者名を表示する場所。
JavaScript からこれらをつかんで、状態に応じて中身を書き換えていきます。
fetch という「取りに行く関数」を知る
「URL にアクセスして、結果を Promise で返す」
ブラウザには、fetch という関数が用意されています。
これは、「指定したURLにアクセスして、結果を返してくれる関数」です。
ざっくり書くと、こういう形です。
fetch("https://example.com/api/quote")
.then(function (response) {
// ここでレスポンスを処理する
})
.catch(function (error) {
// ここでエラーを処理する
});
JavaScriptここで初めて出てくるのが then と catch です。
これは「非同期処理」のための書き方です。
非同期というのは、「すぐには終わらない処理」のことです。
ネットワーク通信は時間がかかるので、
「終わったらこの関数を呼んでね」という形で書きます。
fetch(...) は、「すぐに結果そのもの」ではなく、「そのうち結果が入る約束(Promise)」を返します。then は「結果が返ってきたときに呼ばれる関数」、catch は「エラーが起きたときに呼ばれる関数」です。
ここは一気に完璧に理解しなくて大丈夫です。
「ネットワークは時間がかかるから、終わったら then の中が動く」とだけ押さえておきましょう。
main.js の準備と要素の取得
まずは DOM をつかんで、ボタンにイベントを付ける
main.js を作り、次のように書きます。
let fetchQuoteButtonElement = document.getElementById("fetch-quote-button");
let statusAreaElement = document.getElementById("status-area");
let quoteAreaElement = document.getElementById("quote-area");
let authorAreaElement = document.getElementById("author-area");
JavaScript次に、「名言を取得する」ボタンにイベントを付けます。
fetchQuoteButtonElement.addEventListener("click", function () {
fetchRandomQuote();
});
JavaScriptここで fetchRandomQuote という関数は、これから作る「名言を取りに行く処理」の本体です。
イベントリスナーは、「ボタンが押されたら、その関数を呼ぶ」だけにしておきます。
名言を取得する関数の骨組みを作る
状態表示をしながら、fetch を呼ぶ
fetchRandomQuote 関数を次のように書きます。
function fetchRandomQuote() {
statusAreaElement.textContent = "読み込み中…";
quoteAreaElement.textContent = "";
authorAreaElement.textContent = "";
let url = "https://example.com/api/random-quote";
fetch(url)
.then(function (response) {
if (!response.ok) {
throw new Error("HTTP エラー: " + response.status);
}
return response.json();
})
.then(function (data) {
showQuote(data);
})
.catch(function (error) {
console.error("名言の取得に失敗しました:", error);
statusAreaElement.textContent = "取得に失敗しました。時間をおいて再度お試しください。";
});
}
JavaScriptここでの重要ポイントを丁寧に見ていきます。
最初に、status-area に「読み込み中…」と表示しています。
同時に、quote-area と author-area を空にしています。
これで、「今は通信中なんだな」とユーザーに伝わります。
url には、本来は実在するAPIのURLを入れます(ここでは例として書いています)。
fetch(url) で通信を開始し、
最初の then で response(レスポンス)を受け取ります。
response.ok は、「HTTPステータスが 200番台かどうか」を表します。
もし ok でなければ、throw new Error(...) でエラーを投げて、
後ろの catch に処理を飛ばします。
response.json() は、「レスポンスの中身を JSON として読み込んで、JavaScript のオブジェクトに変換する」処理です。
これも非同期なので、return response.json(); と書いて、
次の then に「変換後の data」を渡しています。
二つ目の then では、data を受け取って showQuote(data) を呼んでいます。
ここで「画面に表示する処理」を分離しているのがポイントです。
catch では、通信エラーや HTTP エラーが起きたときに呼ばれます。
console.error で詳細を出しつつ、画面には「取得に失敗しました」と表示しています。
取得したデータを画面に表示する
JSON の中身をオブジェクトとして扱う
次に、showQuote 関数を書きます。
function showQuote(data) {
console.log("取得したデータ:", data);
let text = data.text;
let author = data.author;
if (!text) {
statusAreaElement.textContent = "名言の本文が取得できませんでした。";
quoteAreaElement.textContent = "";
authorAreaElement.textContent = "";
return;
}
statusAreaElement.textContent = "取得に成功しました。";
quoteAreaElement.textContent = "「" + text + "」";
if (author) {
authorAreaElement.textContent = "― " + author;
} else {
authorAreaElement.textContent = "― 作者不明";
}
}
JavaScriptここでの重要ポイントを深掘りします。
data は、JSON をパースした結果のオブジェクトです。
例えば、{ text: "...", author: "..." } のような形を想定しています。
let text = data.text; で本文を取り出し、let author = data.author; で作者名を取り出しています。
もし text が空(null や undefined)なら、
「名言の本文が取得できませんでした」と表示して return しています。
これは、「API 側のデータがおかしい」場合に備えた防御です。
正常な場合は、status-area に「取得に成功しました。」と表示し、
quote-area に「「本文」」という形で表示しています。
作者名がある場合は「― 作者名」、
ない場合は「― 作者不明」としています。
ここでも、「null や undefined の可能性を考える」クセを意識しています。
非同期処理の流れを言葉で追ってみる
「ボタンを押してから、画面が更新されるまで」
ここまでの流れを、コードではなく“物語”として追ってみます。
ユーザーが「名言を取得する」ボタンを押す。
クリックイベントが発火し、fetchRandomQuote() が呼ばれる。
status-area に「読み込み中…」と表示される。
fetch が URL にアクセスしに行く(ここで少し時間がかかる)。
レスポンスが返ってきたら、最初の then が呼ばれる。
HTTP ステータスをチェックし、問題なければ response.json() を呼ぶ。
JSON のパースが終わったら、二つ目の then が呼ばれ、showQuote(data) が実行される。
showQuote が、quote-area と author-area にテキストを表示する。
同時に、status-area に「取得に成功しました。」と表示される。
もし途中でエラーが起きたら、catch が呼ばれ、「取得に失敗しました。」と表示される。
この流れを一度自分の言葉で説明できると、
「非同期処理って、ただ“時間差で then が呼ばれるだけなんだな”」という感覚がつかめてきます。
async / await という書き方も少しだけ覗いてみる
「then をつなげる」代わりに「一見ふつうの順番で書ける」
今は then を使った書き方をしましたが、
同じことを async / await という構文で書くこともできます。
完全に理解するのはもう少し先でいいですが、
「こういう書き方もあるんだな」とだけ見ておきましょう。
async function fetchRandomQuote() {
statusAreaElement.textContent = "読み込み中…";
quoteAreaElement.textContent = "";
authorAreaElement.textContent = "";
let url = "https://example.com/api/random-quote";
try {
let response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP エラー: " + response.status);
}
let data = await response.json();
showQuote(data);
} catch (error) {
console.error("名言の取得に失敗しました:", error);
statusAreaElement.textContent = "取得に失敗しました。時間をおいて再度お試しください。";
}
}
JavaScriptポイントだけ言うと、
関数の前に async を付けると、その中で await が使えるawait fetch(url) は、「fetch が終わるまで待ってから、結果を response に入れる」try { ... } catch (error) { ... } で、エラー処理をまとめて書ける
という感じです。
見た目としては、「上から順番に処理が進んでいる」ように見えるので、then より読みやすいと感じる人も多いです。
12日目で一番大事な感覚
「ブラウザの外の世界とつながった」
今日あなたが体験したのは、かなり大きな一歩です。
今までは、「自分で用意した配列や localStorage の中身」だけを相手にしていました。
今日は、「外部サービスからデータをもらって、それを画面に反映する」という流れを通しました。
これは、現実のWebアプリのほとんどがやっていることです。
天気アプリなら、天気APIからデータを取ってくる。
ニュースアプリなら、ニュースAPIから記事一覧を取ってくる。
チャットアプリなら、サーバーからメッセージを取ってくる。
その入口に、もう立っています。
12日目のまとめ
今日のキーポイントを短く整理すると、こうなります。
API は「決められたURLにアクセスすると、決められた形でデータを返してくれる仕組み」。
JSON は「JavaScript のオブジェクトに似た“データの箱”の書き方」。
fetch は「URL にアクセスして、結果を Promise(そのうち結果が入る約束)として返す関数」。
then / catch で、「成功したとき」「失敗したとき」の処理を分けて書ける。
非同期処理は、「時間がかかるから、終わったらこの関数を呼んでね」という世界。
もし余裕があれば、
今日の名言アプリを少しアレンジしてみてください。
例えば、
ボタンを押した回数を数えて、「何回目の名言か」を表示する
前回の名言を localStorage に保存しておき、「前回の名言」も表示する
「日本語だけ」「英語だけ」など、フィルタ用のボタンを増やす(API側が対応していれば)
など、小さな工夫でいいです。
「外からデータをもらって、自分のアプリの中で料理する」
この感覚が育ってくると、作れるものの世界が一気に広がります。
