みんなで書き込める掲示板を作ってみよう!

JavaScript ではじめる Web アプリ開発

今日のゴール

書いた文字が、他の人の画面にも出る

  • 名前とメッセージを書いて、投稿ボタンを押す
  • 投稿が一覧に並ぶ
  • 「更新する」を押すと、他の人が書いた投稿も出てくる
  • ブラウザを閉じても投稿は消えない

Webページは 3つの技術 でできている

技術 役割
HTML 構造
CSS 見た目
JavaScript 動き

HTML と CSS は用意済みです。今日書くのは JavaScript だけ。

そして今日は、そこにサーバーが加わります。

準備

  1. ブラウザで StackBlitz のテンプレートを開く
    https://stackblitz.com/edit/board-beginner
  2. 左上の Fork を押す(URL が自分専用のものに変わります)
  3. script.js を開く
  4. 1〜2行目の APIROOM を、伝えられた値に書き換える

左にファイル、右にプレビューが並びます。今日書くのは script.js だけです。

テンプレートは「入力欄とボタンが並んだ、まだ何も動かない状態」から始まります。

今日の進め方

  1. 書いた文字を画面に出す
  2. サーバーってなに?
  3. みんなの投稿を読み込む
  4. 自分の投稿をサーバーに送る
  5. 仕上げ

前半で「自分の画面の中だけで動く掲示板」を作り、後半でそれをサーバーにつなぎ替えます。

スライドの見かた

右上に「記述」バッジ → 手を動かしてコードを書くスライド

ハイライトあり — ハイライトの部分だけを書く(足す・書き換える)。残りはそのまま

function addPost() {
  const name = document.getElementById('name-input').value;
  const text = document.getElementById('text-input').value;
}

ハイライトなし — コードをそのまま写す

document.getElementById('post-btn').addEventListener('click', addPost);

画面は 5つの部品 でできている

名前① 名前の入力欄
メッセージ② メッセージの入力欄
投稿する③ 投稿ボタン
みんなの投稿更新する④ 更新ボタン
(まだ何もありません)⑤ 投稿の一覧

この5つを JavaScript から動かしていきます。

HTML(index.html)との対応

JavaScript から部品を呼ぶために、HTML には id が付いています。

id 部品
name-input ① 名前の入力欄
text-input ② メッセージの入力欄
post-btn ③ 投稿ボタン
reload-btn ④ 更新ボタン
posts ⑤ 投稿の一覧(<ul>

id = 部品につけた名札。JavaScript はこの名札を頼りに部品を探します。

Chapter 1

書いた文字を画面に出そう

この章のゴール

自分の画面の中だけで動く掲示板を作る

  • 投稿ボタンを押す → 書いた文字が一覧に並ぶ
  • 何件でも増えていく

まだサーバーは使いません。ページを再読み込みすると消えます。

そこを Chapter 2 以降で解決していきます。

1-1. 関数 — 処理に名前をつける

まとめておいて、あとから名前で呼び出す

function sayHello() {
  alert('こんにちは');
}

sayHello();   // ← ここではじめて中身が実行される

function 名前() { ... } で作り、名前() で呼び出します。

作っただけでは何も起きません。呼ばれたときにはじめて中身が動きます。

では、誰が呼ぶのか。今日はボタンが押されたときに呼ばせます。

1-1. 部品を探して、押されたときに動かす

ボタンに「押されたらこの関数を呼んで」と頼んでおく

document.getElementById('名札') = 名札で HTML の部品を探す

部品.addEventListener('きっかけ', 関数) = きっかけが起きたら関数を動かす

<button id="ok-btn">OK</button>
const button =
  document.getElementById('ok-btn');

button.addEventListener('click', sayHello);
きっかけ いつ起きるか
'click' クリックされたとき
'input' 入力欄の文字が変わったとき
'change' 選択や入力が確定したとき

書いた時点では、まだ何も動きません。実際にボタンが押された瞬間に sayHello が呼ばれます。

1-1. ボタンを押したら反応させよう

まず「押したら何か起きる」を確かめる

書く場所script.js のいちばん下

function addPost() {
  alert('投稿ボタンが押されました!');
}

document.getElementById('post-btn').addEventListener('click', addPost);

成功 — 「投稿する」を押すとメッセージが出る

1-2. 入力された文字を使うには

決めうちのメッセージではなく、書かれた文字を出したい

部品.value = 入力欄に書かれている文字。部品そのものではなく「中身」

バッククォート ` の文字列 = ${ } の中に変数を差し込める

const name = document.getElementById('name-input').value;   // 'たろう'

`${name} さん`   // → 'たろう さん'
'name さん'      // → 'name さん'(差し込まれない)

差し込みが要らないときは、ふつうの ' で書きます。

1-2. 入力欄の文字を取り出そう

決めうちのメッセージではなく、入力された文字を使う

書く場所addPost の中を丸ごと置き換え

function addPost() {
  const name = document.getElementById('name-input').value;
  const text = document.getElementById('text-input').value;

  alert(`${name} さん: ${text}`);
}

成功 — 名前とメッセージを入力して押すと、その中身が出る

1-3. 投稿を画面に並べる

<li> を作って <ul id="posts"> に入れる

投稿1件が <li> 1行になります。

<ul id="posts">
  <li>たろう: はじめまして!</li>   <!-- これを JavaScript で作る -->
</ul>
やること 書き方
<li> を作る document.createElement('li')
② 文字を入れる item.textContent = '...'
<ul> に入れる list.appendChild(item)

作っただけでは画面に出ません。③ で入れ先を指定して、はじめて表示されます。

1-3. 投稿を画面に出そう

alert をやめて、一覧に並べる

書く場所addPost の中を丸ごと置き換え

function addPost() {
  const name = document.getElementById('name-input').value;
  const text = document.getElementById('text-input').value;

  const item = document.createElement('li');
  item.textContent = `${name}: ${text}`;
  document.getElementById('posts').appendChild(item);
}

成功 — 投稿するたびに、下の一覧に行が増えていく

動作チェック

2つとも当てはまれば Chapter 1 は完了です

  1. 名前とメッセージを入れて「投稿する」を押すと、一覧に1行増える
  2. もう一度投稿すると、2行になる(前の行が消えない)

プレビューを更新してみてください

投稿が全部消えます。ここが次の章の出発点です。

Chapter 2

サーバーってなに?

いまの掲示板

投稿は「自分のブラウザの中」にしかない

  • ページを再読み込みすると消える
  • 他の人のブラウザには出ない

posts という配列は、開いているページの中にだけあります。ページを閉じれば一緒に消えます。

この2つは、投稿を自分のブラウザの外に置けば解決します。ページを閉じても残っていて、他の人からも読める場所が要ります。

サーバー = 頼まれたら答えるプログラム

あなたの
ブラウザ
サーバー動かしっぱなし
ほかの人の
ブラウザ
  • 「投稿を全部ください」と頼まれたら、返す
  • 「この投稿を保存して」と頼まれたら、保存する

ブラウザは開いている間しか動きません。サーバーはいつ頼まれても答えられるように、動かしっぱなしにしてあります。あなたがページを閉じている間も、他の人からの依頼に答えています。

投稿そのものは データベース に入る

サーバーは受け取った投稿をデータベースに預ける

データベース = データを保存しておくための専用のソフト

ブラウザ投稿を送る
サーバー受け取る
データベース保存する

投稿が消えないのは、最後にここへ届いているからです。サーバーを入れ替えても、データベースの中身は残ります。

今日のサーバーは 用意済み です

書くのはブラウザ側だけ

サーバーとデータベースはこちらで動かしてあります。今日は、動いているサーバーに話しかけるところまでをやります。

サーバーのコードも公開しています。中身が気になる人は、資料のリポジトリの 2026/board/backend/ を見てください。

  • 200 行ほどの JavaScript(TypeScript)です
  • 「投稿を返す」「投稿を保存する」の2つしか書いてありません

サーバーとのやりとりに使う 2つのメソッド

メソッド = サーバーへの頼み方の種類

やること 呼び方
置いてあるデータを取ってくる GET
新しいデータを送る POST

今日の掲示板では、この2つを使います。

  • 投稿の一覧を読み込む → GET
  • 自分の投稿を書き込む → POST

やりとりの中身は JSON という形

JSON = データを文字列で表すための、共通の書き方です。

[
  { "name": "たろう", "text": "はじめまして" },
  { "name": "はなこ", "text": "こんにちは" }
]

JavaScript の配列とオブジェクトによく似ています。

やりとりできるのは文字列だけです。JavaScript の配列やオブジェクトをそのまま送れないので、いったんこの形にしてやりとりします。

fetch — サーバーに話しかける命令

const res = await fetch('https://.../posts?room=sample');
const posts = await res.json();
やっていること
fetch(...) この住所(URL)に GET でとりにいく
res 返ってきた返事そのもの
res.json() 返事の中身を JavaScript の配列やオブジェクトとして取り出す

await待つ印

サーバーとのやりとりには時間がかかる

サーバーは別の場所にあります。頼んでから返事が届くまでの時間は、ページの中だけで済む処理とは桁が違います。「返事が届くまで待つ」と書かないと、届く前に次の行へ進んでしまいます。

ルールは2つ

  1. fetchres.json() には await を付ける
  2. await を使う関数には async を付ける
async function showPosts() {
  const res = await fetch('...');
}

await を忘れるとどうなるか

const posts = res.json();          // await なし
console.log(posts);
Promise { <pending> }

中身の代わりに「まだ準備中です」という札が返ってきます。

この表示を見たら、await の付け忘れを疑ってください。今日いちばん出やすいエラーです。

Chapter 3

みんなの投稿を読みこもう

3-1. 一覧を並べるのに使う書き方

サーバーからは、投稿が配列で返ってきます

[
  { name: 'たろう', text: 'はじめまして' },
  { name: 'はなこ', text: 'こんにちは' },
]

配列 = データを順番に並べたリスト

オブジェクト { name: ..., text: ... } = 名前つきのデータのまとまり

for...of = リストの中身を1つずつ取り出して繰り返す

list.textContent = '' = 中身を空にする。表示のたびに全部消してから並べ直す

3-1. showPosts() を作ろう

サーバーから投稿を取ってきて、一覧に並べる

書く場所script.jsaddPost より前

async function showPosts() {
  const res = await fetch(`${API}/posts?room=${ROOM}`);
  const posts = await res.json();

  const list = document.getElementById('posts');
  list.textContent = '';

  for (const post of posts) {
    const item = document.createElement('li');
    item.textContent = `${post.name}: ${post.text}`;
    list.appendChild(item);
  }
}

まだ呼んでいないので、画面は変わりません。

3-2. 読み込むきっかけを作ろう

読み込むきっかけを2つ作る

書く場所script.js のいちばん下

document.getElementById('reload-btn').addEventListener('click', showPosts);

showPosts();

成功 — ページを開いた時点で、すでに誰かの投稿が並んでいる

いちばん下の showPosts() は、ページを開いた瞬間に1回だけ実行するためのものです。同じ関数を2か所から呼べるのが、処理に名前をつけておく利点です。

動作チェック

2つとも当てはまれば Chapter 3 は完了です

  1. ページを開くと、自分が投稿したものではない投稿が並んでいる
  2. 「投稿する」を押すと画面に1行増えるが、「更新する」を押すと消える

2番はなぜ消えるのか

いまの「投稿する」は画面に足しているだけで、サーバーには何も送っていません。だから読み直すと消えます。次の章で送る側を作ります。

他の人が書いた文字を、そのまま出していいのか

いま画面に並んでいるのは、他の人が書いた文字です

showPosts では textContent を使っています。似たものに innerHTML があり、違いは書かれた文字を HTML として解釈するかどうかです。

書き方 <b>あ</b> と投稿されたら
textContent <b>あ</b> と、そのまま表示される
innerHTML と太字になる(HTML として実行される)

innerHTML だと、他人の書いた文字に自分のページを勝手に書き換えられます。文字を表示するつもりが、命令を実行させてしまいます。

掲示板やコメント欄では textContent を使います。

Chapter 4

自分の投稿をサーバーに送ろう

送るときは、取るときより少し長い

「どこへ」だけでなく「何を」「どうやって」を伝える

await fetch(URL, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: name, text: text }),
});
書くもの 伝えていること
method: 'POST' どうやって — 取りにいくのではなく、送る
headers どうやって — 中身は JSON です、という申告
body 何を — 送るデータそのもの

JSON.stringify — オブジェクトを文字列にする

サーバーとやりとりできるのは文字列だけです。オブジェクトのままでは送れません。

const data = { name: 'たろう', text: 'やっほー' };

JSON.stringify(data);
// → '{"name":"たろう","text":"やっほー"}'

送るとき(JSON.stringify)と、受け取るとき(res.json())で、ちょうど逆のことをしています。

4-1. addPost() を書き換えよう

画面に直接足すのをやめて、サーバーに送る

書く場所addPost を丸ごと置き換え(前のものは消す)

async function addPost() {
  const name = document.getElementById('name-input').value;
  const text = document.getElementById('text-input').value;

  await fetch(`${API}/posts?room=${ROOM}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: name, text: text }),
  });

  document.getElementById('text-input').value = '';
  showPosts();
}

成功 — 投稿すると一覧に自分の投稿が出て、メッセージ欄が空になる

動作チェック

掲示板の完成です

  1. 投稿すると、一覧に自分の投稿が出る
  2. プレビューを更新しても消えない
  3. しばらく待ってから「更新する」を押すと、他の参加者の投稿が増えている

3番が確認できたら、あなたの書いたコードがインターネットの向こう側とやりとりしているということです。全員が同じ掲示板を見ているので、自分の投稿も他の参加者の画面に出ています。

完成!

みんなで書き込める掲示板ができました

Chapter 5

仕上げをしよう

5-1. サーバーが返している時刻の形

{ "name": "たろう", "text": "やっほー", "createdAt": "2026-09-10T05:23:00.000Z" }

これは世界中どこでも同じ意味になるように決められた書き方で、人が読むには向いていません。

書き方 結果
new Date('2026-09-10T05:23:00.000Z') 日付として扱えるようにする
.toLocaleTimeString() 見ている人の地域の時刻に合わせる → 14:23:00

日付ごと出したいときは .toLocaleString() を使います。

5-1. 投稿の時刻を表示しよう

サーバーは投稿された時刻も返しています

書く場所showPostsfor の中。1行足して、1行書き換える

  for (const post of posts) {
    const item = document.createElement('li');
    const time = new Date(post.createdAt).toLocaleTimeString();
    item.textContent = `${post.name}: ${post.text} (${time})`;
    list.appendChild(item);
  }

成功 — 各投稿のうしろに 14:23:05 のような時刻が付く

5-2. 名前を変えて投稿してみよう

動くようになったら、遊んでみてください

  • 名前を変えて投稿する
  • 絵文字を入れてみる
  • 長い文章を投稿してみる(一定の長さで切られます)
  • 空のまま投稿してみる(送られません)

サーバー側には「1つの掲示板に置ける投稿は200件まで」という制限があります。古いものから順に消えていきます。

発展課題

余裕がある人はチャレンジ!

発展①:自動で更新されるようにする

「更新する」を押さなくても、新しい投稿が流れてくる

書く場所script.js のいちばん下

setInterval(showPosts, 10000);

setInterval(関数, ミリ秒) = 決まった間隔で、関数を何度も実行する

10000 ミリ秒 = 10秒ごとに showPosts() が呼ばれます。

成功 — 何も押さなくても、他の人の投稿が出てくる

発展①:この書き方の弱点

新しい投稿がなくても、10秒ごとに聞きにいっている

ブラウザ新着ある?
サーバーないよ

これをポーリングといいます。間隔を短くするほど無駄な通信が増えます。

逆に、サーバーの側から「来たよ」と教えてくるやり方もあります。

  • SSE — サーバーからの一方通行のお知らせ
  • WebSocket — つなぎっぱなしにして双方向にやりとりする

発展②:投稿を新しい順に並べる

いまは古い順に並んでいます

showPostsfor の前に、1行足すと逆順になります。

posts.reverse();

reverse() = 配列の並びをひっくり返す

SNS のタイムラインはたいてい新しい順です。どちらが読みやすいか、実際に切り替えて比べてみてください。

困ったときは

投稿しても何も起きない

  • ConsolePromise { <pending> } が出ていないか → await の付け忘れ
  • 関数の頭に async は付いているか

一覧が空のまま

  • 1〜2行目の APIROOM が、伝えられた値になっているか
  • showPosts()script.js のいちばん下に書いたか

何を試してもだめなとき

エラーの内容と、書いたコードをそのままチャットに貼ってください。

今日学んだこと

やったこと 使ったもの
部品を探して、中身を取り出す getElementById / .value
ボタンが押されたら動かす addEventListener
画面に部品を足す createElement / textContent / appendChild
サーバーからデータを取る fetch / res.json() / GET
サーバーにデータを送る fetch / JSON.stringify / POST
返事を待つ async / await

おつかれさまでした!

自分の書いたコードが、他の人の画面を動かしました