18.Javascriptで作る週間天気予報プログラム
では早速、Javascriptでも天気予報表示プログラムを作成してみましょう‼️
18-1.週間天気予報のURL
週間天気予報は府県天気予報と同じファイルに入っていますので、URLは府県天気予報と同じです。
・三重県の週間天気予報を取得する
https://www.jma.go.jp/bosai/forecast/data/forecast/240000.json
18-2.週間天気予報の発表時刻
週間天気予報の発表時刻内容は、
①発表時刻 11時、17時の一日二回となります。
②発表内容 向こう一週間の、各府県における一日ごとの天気
最高・最低気温(1℃単位)
降水確率(10%単位)
予報の信頼度
期間における降水量(1㎜単位)と気温の平年値(0.1℃単位)
※気象庁ホームページでは、最新の「府県天気予報」から、その府県予報区の代表的な一次細分区域の明日・明後日の予報を随時取り込んで掲載しています。
三重県で例えると、北中部の今日明日の天気予報を週間天気予報に取り込んでいます。
18-3.週間天気予報のデータ仕様
三重県のデータを参考に見てみます。
今回は、天気コードと最高最低気温データのみの取得になります。
①11・17時発表のデータ仕様
17時発表のものを参考にしていますが、11時発表のものと内容は同じです。
天気コードweatherCodesは、[0]~[6]まで、7日分存在します。

最低気温データは、tempsMinのものを使います。
tempsMinUppeは、最大値の最低気温データです。
tempsMinLowerは、最小値の最低気温データです。

最高気温データは、tempsMaxのものを使います。
tempsMaxUpperは、最大値の最高気温データです。
tempsMaxLowerは、最小値の最高気温データです。

18-4.Javascriptで週間天気予報の処理プログラム
Javascriptの処理プログラムは、下記になります。
const weatherCode = {
100: ["100.svg", "500.svg", "晴れ"],
101: ["101.svg", "501.svg", "晴れ時々曇り"],
102: ["102.svg", "502.svg", "晴れ一時雨"],
103: ["102.svg", "502.svg", "晴れ時々雨"],
104: ["104.svg", "504.svg", "晴れ一時雪"],
105: ["104.svg", "504.svg", "晴れ時々雪"],
106: ["102.svg", "502.svg", "晴れ一時雨か雪"],
107: ["102.svg", "502.svg", "晴れ時々雨か雪"],
108: ["102.svg", "502.svg", "晴れ一時雨か雷雨"],
110: ["110.svg", "510.svg", "晴れ後時々曇り"],
111: ["110.svg", "510.svg", "晴れ後曇り"],
112: ["112.svg", "512.svg", "晴れ後一時雨"],
113: ["112.svg", "512.svg", "晴れ後時々雨"],
114: ["112.svg", "512.svg", "晴れ後雨"],
115: ["115.svg", "515.svg", "晴れ後一時雪"],
116: ["115.svg", "515.svg", "晴れ後時々雪"],
117: ["115.svg", "515.svg", "晴れ後雪"],
118: ["112.svg", "512.svg", "晴れ後雨か雪"],
119: ["112.svg", "512.svg", "晴れ後雨か雷雨"],
120: ["102.svg", "502.svg", "晴れ朝夕一時雨"],
121: ["102.svg", "502.svg", "晴れ朝の内一時雨"],
122: ["112.svg", "512.svg", "晴れ夕方一時雨"],
123: ["100.svg", "500.svg", "晴れ山沿い雷雨"],
124: ["100.svg", "500.svg", "晴れ山沿い雪"],
125: ["112.svg", "512.svg", "晴れ午後は雷雨"],
126: ["112.svg", "512.svg", "晴れ昼頃から雨"],
127: ["112.svg", "512.svg", "晴れ夕方から雨"],
128: ["112.svg", "512.svg", "晴れ夜は雨"],
130: ["100.svg", "500.svg", "朝の内霧後晴れ"],
131: ["100.svg", "500.svg", "晴れ明け方霧"],
132: ["101.svg", "501.svg", "晴れ朝夕曇り"],
140: ["102.svg", "502.svg", "晴れ時々雨と雷"],
160: ["104.svg", "504.svg", "晴れ一時雪か雨"],
170: ["104.svg", "504.svg", "晴れ時々雪か雨"],
181: ["115.svg", "515.svg", "晴れ後雪か雨"],
200: ["200.svg", "200.svg", "曇り"],
201: ["201.svg", "601.svg", "曇り時々晴れ"],
202: ["202.svg", "202.svg", "曇り一時雨"],
203: ["202.svg", "202.svg", "曇り時々雨"],
204: ["204.svg", "204.svg", "曇り一時雪"],
205: ["204.svg", "204.svg", "曇り時々雪"],
206: ["202.svg", "202.svg", "曇り一時雨か雪"],
207: ["202.svg", "202.svg", "曇り時々雨か雪"],
208: ["202.svg", "202.svg", "曇り一時雨か雷雨"],
209: ["200.svg", "200.svg", "霧"],
210: ["210.svg", "610.svg", "曇り後時々晴れ"],
211: ["210.svg", "610.svg", "曇り後晴れ"],
212: ["212.svg", "212.svg", "曇り後一時雨"],
213: ["212.svg", "212.svg", "曇り後時々雨"],
214: ["212.svg", "212.svg", "曇り後雨"],
215: ["215.svg", "215.svg", "曇り後一時雪"],
216: ["215.svg", "215.svg", "曇り後時々雪"],
217: ["215.svg", "215.svg", "曇り後雪"],
218: ["212.svg", "212.svg", "曇り後雨か雪"],
219: ["212.svg", "212.svg", "曇り後雨か雷雨"],
220: ["202.svg", "202.svg", "曇り朝夕一時雨"],
221: ["202.svg", "202.svg", "曇り朝の内一時雨"],
222: ["212.svg", "212.svg", "曇り夕方一時雨"],
223: ["201.svg", "601.svg", "曇り日中時々晴れ"],
224: ["212.svg", "212.svg", "曇り昼頃から雨"],
225: ["212.svg", "212.svg", "曇り夕方から雨"],
226: ["212.svg", "212.svg", "曇り夜は雨"],
228: ["215.svg", "215.svg", "曇り昼頃から雪"],
229: ["215.svg", "215.svg", "曇り夕方から雪"],
230: ["215.svg", "215.svg", "曇り夜は雪"],
231: ["200.svg", "200.svg", "曇り海岸霧か霧雨"],
240: ["202.svg", "202.svg", "曇り時々雨と雷"],
250: ["204.svg", "204.svg", "曇り時々雪と雷"],
260: ["204.svg", "204.svg", "曇り一時雪か雨"],
270: ["204.svg", "204.svg", "曇り時々雪か雨"],
281: ["215.svg", "215.svg", "曇り後雪か雨"],
300: ["300.svg", "300.svg", "雨"],
301: ["301.svg", "701.svg", "雨時々晴れ"],
302: ["302.svg", "302.svg", "雨時々止む"],
303: ["303.svg", "303.svg", "雨時々雪"],
304: ["300.svg", "300.svg", "雨か雪"],
306: ["300.svg", "300.svg", "大雨"],
308: ["308.svg", "308.svg", "雨で暴風を伴う"],
309: ["303.svg", "303.svg", "雨一時雪"],
311: ["311.svg", "711.svg", "雨後晴れ"],
313: ["313.svg", "313.svg", "雨後曇り"],
314: ["314.svg", "314.svg", "雨後時々雪"],
315: ["314.svg", "314.svg", "雨後雪"],
316: ["311.svg", "711.svg", "雨か雪後晴れ"],
317: ["313.svg", "313.svg", "雨か雪後曇り"],
320: ["311.svg", "711.svg", "朝の内雨後晴れ"],
321: ["313.svg", "313.svg", "朝の内雨後曇り"],
322: ["303.svg", "303.svg", "雨朝晩一時雪"],
323: ["311.svg", "711.svg", "雨昼頃から晴れ"],
324: ["311.svg", "711.svg", "雨夕方から晴れ"],
325: ["311.svg", "711.svg", "雨夜は晴れ"],
326: ["314.svg", "314.svg", "雨夕方から雪"],
327: ["314.svg", "314.svg", "雨夜は雪"],
328: ["300.svg", "300.svg", "雨一時強く降る"],
329: ["300.svg", "300.svg", "雨一時みぞれ"],
340: ["400.svg", "400.svg", "雪か雨"],
350: ["300.svg", "300.svg", "雨で雷を伴う"],
361: ["411.svg", "811.svg", "雪か雨後晴れ"],
371: ["413.svg", "413.svg", "雪か雨後曇り"],
400: ["400.svg", "400.svg", "雪"],
401: ["401.svg", "801.svg", "雪時々晴れ"],
402: ["402.svg", "402.svg", "雪時々止む"],
403: ["403.svg", "403.svg", "雪時々雨"],
405: ["400.svg", "400.svg", "大雪"],
406: ["406.svg", "406.svg", "風雪強い"],
407: ["406.svg", "406.svg", "暴風雪"],
409: ["403.svg", "403.svg", "雪一時雨"],
411: ["411.svg", "811.svg", "雪後晴れ"],
413: ["413.svg", "413.svg", "雪後曇り"],
414: ["414.svg", "414.svg", "雪後雨"],
420: ["411.svg", "811.svg", "朝の内雪後晴れ"],
421: ["413.svg", "413.svg", "朝の内雪後曇り"],
422: ["414.svg", "414.svg", "雪昼頃から雨"],
423: ["414.svg", "414.svg", "雪夕方から雨"],
425: ["400.svg", "400.svg", "雪一時強く降る"],
426: ["400.svg", "400.svg", "雪後みぞれ"],
427: ["400.svg", "400.svg", "雪一時みぞれ"],
450: ["400.svg", "400.svg", "雪で雷を伴う"],
};
// ここのXXXXXX.jsonを変更する
const url = "https://www.jma.go.jp/bosai/forecast/data/forecast/240000.json";
const dayList = ["日", "月", "火", "水", "木", "金", "土"];
const timeDefinesList = new Array();
const weatherCodeList = new Array();
const tempsMinList = new Array();
const tempsMaxList = new Array();
fetch(url)
.then(function (response) {
return response.json();
})
.then(function (weather) {
document
.getElementById("location")
.prepend(
`${weather[1].publishingOffice}: ${weather[1].timeSeries[0].areas[0].area.name} `
);
const isTodaysData = weather[0].timeSeries[2].timeDefines.length === 4;
const weatherCodes = weather[0].timeSeries[0].areas[0].weatherCodes;
const timeDefines = weather[0].timeSeries[0].timeDefines;
const temps = weather[0].timeSeries[2].areas[0].temps;
weatherCodeList.push(weatherCodes[0], weatherCodes[1]);
timeDefinesList.push(timeDefines[0], timeDefines[1]);
if (isTodaysData) {
tempsMinList.push(temps[0] === temps[1] ? "--" : temps[0], temps[2]);
tempsMaxList.push(temps[1], temps[3]);
} else {
tempsMinList.push("--", temps[0]);
tempsMaxList.push("--", temps[1]);
}
const startCount =
weather[1].timeSeries[0].timeDefines.indexOf(timeDefines[1]) + 1;
for (let i = startCount; i < startCount + 5; i++) {
weatherCodeList.push(weather[1].timeSeries[0].areas[0].weatherCodes[i]);
timeDefinesList.push(weather[1].timeSeries[0].timeDefines[i]);
tempsMinList.push(weather[1].timeSeries[1].areas[0].tempsMin[i]);
tempsMaxList.push(weather[1].timeSeries[1].areas[0].tempsMax[i]);
}
const date = document.getElementsByClassName("date");
const weatherImg = document.getElementsByClassName("weatherImg");
const weatherTelop = document.getElementsByClassName("weatherTelop");
const tempMin = document.getElementsByClassName("tempMin");
const tempMax = document.getElementsByClassName("tempMax");
weatherCodeList.forEach(function (el, i) {
let dt = new Date(timeDefinesList[i]);
let weekdayCount = dt.getDay();
if (weekdayCount === 0) date[i].style.color = "red";
if (weekdayCount === 6) date[i].style.color = "blue";
var m = ("00" + (dt.getMonth() + 1)).slice(-2);
var d = ("00" + dt.getDate()).slice(-2);
date[i].textContent = `${m}/${d}(${dayList[weekdayCount]})`;
var isNight = Number(i === 0 && !isTodaysData)
weatherImg[i].src = "https://www.jma.go.jp/bosai/forecast/img/" + weatherCode[el][isNight];
weatherTelop[i].textContent = weatherCode[el][2];
tempMin[i].textContent = tempsMinList[i] + "℃";
tempMax[i].textContent = tempsMaxList[i] + "℃";
});
});
① weatherCode オブジェクトの役割
const weatherCode = {
100: ["100.svg", "500.svg", "晴れ"],
...
};
何をしている?
気象庁の 天気コード(数値) を、
昼用アイコン夜用アイコン日本語テロップ
に変換するための 対応表(辞書) です。
weatherCode[100]
// → ["100.svg", "500.svg", "晴れ"]
つまり後半で
weatherCode[el][0 or 1] // アイコン
weatherCode[el][2] // 天気文言
として使うためのものです。
💡[0] = 昼[1] = 夜
という構造にしているのがポイント。
② 気象庁JSONのURL
const url = "https://www.jma.go.jp/bosai/forecast/data/forecast/240000.json";
240000は 府県予報区コード- ここを変えるだけで地域が切り替わる
👉 かなり「再利用性の高い」設計です。
③ 曜日表示用配列
const dayList = ["日", "月", "火", "水", "木", "金", "土"];
Date.getDay() の戻り値(0〜6)を
日本語曜日に変換するための配列。
④ データ格納用の配列
const timeDefinesList = new Array();
const weatherCodeList = new Array();
const tempsMinList = new Array();
const tempsMaxList = new Array();
なぜ配列に貯める?
- 今日+明日+週間5日 を
一旦まとめてから - 最後に
forEachで一気に描画するため
👉 DOM操作を分離しているのが綺麗。
⑤ fetch でJSON取得
fetch(url)
.then(response => response.json())
.then(weather => {
ここで weather は
気象庁の forecast JSON を丸ごと持った配列。
⑥ 地点名・発表官署の表示
document.getElementById("location").prepend(
`${weather[1].publishingOffice}: ${weather[1].timeSeries[0].areas[0].area.name}`
);
weather[1]→ 週間予報ブロックpublishingOffice→ 発表官署area.name→ 地域名
⑦ 今日データかどうかの判定(超重要)
const isTodaysData = weather[0].timeSeries[2].timeDefines.length === 4;
何を判定している?
- 今日の予報が含まれる場合 →
length === 4 - 含まれない場合 →
length === 2
これは 気象庁JSON最大の地雷ポイント。
👉 この判定があるから
最低・最高気温のズレ問題を回避できています。
⑧ 今日・明日のデータ取得
const weatherCodes = weather[0].timeSeries[0].areas[0].weatherCodes;
const timeDefines = weather[0].timeSeries[0].timeDefines;
const temps = weather[0].timeSeries[2].areas[0].temps;
weather[0]→ 今日・明日timeSeries[0]→ 天気timeSeries[2]→ 気温
⑨ 今日・明日分を配列に追加
weatherCodeList.push(weatherCodes[0], weatherCodes[1]);
timeDefinesList.push(timeDefines[0], timeDefines[1]);
気温の扱い(分岐が神)
if (isTodaysData) {
tempsMinList.push(
temps[0] === temps[1] ? "--" : temps[0],
temps[2]
);
tempsMaxList.push(temps[1], temps[3]);
} else {
tempsMinList.push("--", temps[0]);
tempsMaxList.push("--", temps[1]);
}
- 今日の最低気温が「既に出終わっている」場合は
"--" - JSONの構造差をきちんと吸収している
👉 実運用レベルの書き方です。
⑩ 週間5日分の開始位置を計算
const startCount =
weather[1].timeSeries[0].timeDefines.indexOf(timeDefines[1]) + 1;
何をしている?
- 明日の日付が週間予報のどこにあるかを探す
- その次の日から5日分を表示
👉 今日・明日と週間の 重複問題を完全回避。
⑪ 週間5日分をループで追加
for (let i = startCount; i < startCount + 5; i++) {
weatherCodeList.push(...);
timeDefinesList.push(...);
tempsMinList.push(...);
tempsMaxList.push(...);
}
これで
合計7日分 のデータが揃います。
⑫ DOM取得
const date = document.getElementsByClassName("date");
const weatherImg = document.getElementsByClassName("weatherImg");
...
HTML側に
<div class="date"></div>
<img class="weatherImg">
のような要素が 7個ずつ 並んでいる前提。
⑬ 描画処理(forEach)
weatherCodeList.forEach(function (el, i) {
日付と曜日
let dt = new Date(timeDefinesList[i]);
let weekdayCount = dt.getDay();
- 日曜 → 赤
- 土曜 → 青
date[i].textContent = `${m}/${d}(${dayList[weekdayCount]})`;
⑭ 昼夜アイコンの切り替え(地味に高度)
var isNight = Number(i === 0 && !isTodaysData)
- 今日が含まれない場合
- 最初の予報は「今夜」
👉 だから夜アイコンを使う、という判断。
weatherImg[i].src =
"https://www.jma.go.jp/bosai/forecast/img/" +
weatherCode[el][isNight];
⑮ 天気テロップと気温
weatherTelop[i].textContent = weatherCode[el][2];
tempMin[i].textContent = tempsMinList[i] + "℃";
tempMax[i].textContent = tempsMaxList[i] + "℃";
18-5.HTMLの週間天気予報表示プログラム
<link rel="stylesheet" href="./weather.css">
<div id="location"><a href="https://www.jma.go.jp/bosai/forecast/" target="_blank">気象庁のデータを元に作成</a></div>
<div class="weatherForecast">
<div class="weather">
<div class="date">--/--(-)</div>
<img class="weatherImg">
<div class="weatherTelop">--</div>
<div><span class="tempMin">-℃</span>/<span class="tempMax">-℃</span></div>
</div>
</div>
<script>
for (let i = 0; i < 6; i++) {
const el = document.querySelector('.weather').cloneNode(true);
document.querySelector('.weatherForecast').appendChild(el);
}
</script>
<script src="./weather.js"></script>
① CSS の読み込み
<link rel="stylesheet" href="./weather.css">
- 天気予報全体のレイアウト・色・フォント調整用
- JavaScriptとは独立していて
👉 表示専用(見た目) を担当
② location(地点・出典表示)
<div id="location">
<a href="https://www.jma.go.jp/bosai/forecast/" target="_blank">
気象庁のデータを元に作成
</a>
</div>
役割
- JavaScript側で
document.getElementById("location").prepend(...)
される 地点名・発表官署の表示エリア
ポイント
<a>を入れておくことで
気象庁データ利用の明示(利用規約的にも◎)target="_blank"で別タブ表示
③ 天気予報全体のコンテナ
<div class="weatherForecast">
- 7日分の天気カードをまとめる親要素
- JS側で
document.querySelector('.weatherForecast').appendChild(el);
の追加先になる
④ 天気1日分の「ひな型」
<div class="weather">
ここが超重要。
この .weather が
「1日分の天気予報カードのテンプレート」 です。
④-1 日付表示
<div class="date">--/--(-)</div>
- 初期値はダミー
- JSで
date[i].textContent = "mm/dd(曜)";
に上書きされる
④-2 天気アイコン
<img class="weatherImg">
srcは最初は空- JSで
weatherImg[i].src = "https://www.jma.go.jp/..."
をセット
👉 <img> を先に置いておくのが正解
④-3 天気テロップ
<div class="weatherTelop">--</div>
- 「晴れ」「曇り時々雨」など
weatherCode[el][2]がここに入る
④-4 最低/最高気温
<div>
<span class="tempMin">-℃</span>/<span class="tempMax">-℃</span>
</div>
- 最低・最高を spanで分離
- CSSで色分け(最低=青、最高=赤)がしやすい
JS側では
tempMin[i].textContent = tempsMinList[i] + "℃";
tempMax[i].textContent = tempsMaxList[i] + "℃";
⑤ weatherカードを6個複製するスクリプト
<script>
for (let i = 0; i < 6; i++) {
const el = document.querySelector('.weather').cloneNode(true);
document.querySelector('.weatherForecast').appendChild(el);
}
</script>
何をしている?
- 最初にHTMLに書いた
.weatherは 1個だけ - それを 6回複製
- 合計 7日分 にする
cloneNode(true) の意味
true→ 中身(子要素)も含めて丸ごとコピーfalseだと箱だけになる(今回は不可)
💡
HTMLを7個手書きしないで済む
→ メンテ性が高い、賢い方法。
⑥ weather.js の読み込み(最後が重要)
<script src="./weather.js"></script>
なぜ一番下?
- DOM(HTML)がすべて生成されてから
- JSが
.date,.weatherImgを取得できるようにするため
👉 defer を使わず、順序で安全を確保している書き方。
全体の流れ(超要約)
- HTMLで 天気1日分のテンプレート を1個作る
- JavaScriptで 6個複製 → 7日分
- weather.js が
- 日付
- 天気アイコン
- 天気テロップ
- 気温
を上書き
- CSSで見た目を整える
18-6.週間天気予報の表示CSS
.weatherForecast {
width: 840px;
height: 120px;
display: flex;
text-align: center;
border: 1px solid #000;
}
.weather {
width: calc(100% / 7);
height: 100%;
box-sizing: border-box;
border: 1px solid #666;
}
.weather > div {
height: 20%;
font-size: 14px;
}
.weatherImg {
height: 42%;
}
.tempMin {
color: blue;
}
.tempMax {
color: red;
}
🌤 全体像
このCSSは:
[ 1日目 ][ 2日目 ][ 3日目 ][ 4日目 ][ 5日目 ][ 6日目 ][ 7日目 ]
という 横並びレイアウト を作っています。
① .weatherForecast
.weatherForecast {
width: 840px;
height: 120px;
display: flex;
text-align: center;
border: 1px solid #000;
}
役割
👉 天気カード全体の入れ物
各プロパティの意味
width: 840px;
横幅を固定(840px)
height: 120px;
高さを120pxに固定
display: flex; ⭐超重要
これが一番大事です。
👉 子要素を横並びにする
つまり .weather が横に並びます。
text-align: center;
中の文字を中央揃え
border: 1px solid #000;
全体を黒枠で囲む
② .weather
.weather {
width: calc(100% / 7);
height: 100%;
box-sizing: border-box;
border: 1px solid #666;
}
役割
👉 1日分の天気カード
各プロパティ
width: calc(100% / 7); ⭐
👉 全体を 7等分
例:
840px ÷ 7 = 120px
つまり1カード = 120px
height: 100%;
親(120px)いっぱいの高さ
box-sizing: border-box; ⭐重要
通常は:
width = 中身だけ
border は別に加算される
でも border-box にすると:
width の中に border を含める
👉 レイアウト崩れ防止
border: 1px solid #666;
各カードを区切る線
③ .weather > div
.weather > div {
height: 20%;
font-size: 14px;
}
意味
👉 .weather の直下の div 全部
あなたのHTMLだと:
日付
テロップ
気温表示
など。
height: 20%;
.weather の高さは 120px
120px × 20% = 24px
👉 各ブロックを均等に縦分割
font-size: 14px;
文字サイズ指定
④ .weatherImg
.weatherImg {
height: 42%;
}
意味
👉 天気アイコンの高さを
120px × 42% ≒ 50px
に設定
画像を少し大きめにしています。
⑤ 気温の色分け
.tempMin {
color: blue;
}.tempMax {
color: red;
}
👉
- 最低気温 → 青
- 最高気温 → 赤
日本の天気予報の定番カラーですね。
最後までお読みくださり、本当にありがとうございます。
PHPを理解したあなたは、Javascriptを理解することで、更に飛躍することでしょう‼️
是非、Javascriptでの天気予報表示プログラムにも、挑戦してください‼️
分からないことは、ChatGPTに聞いて下さいね‼️😊🤖
