Webページにリアルタイムで更新される時計を実装したい、カウントダウンタイマーを作りたい――そんな場面で必ず登場するのが setInterval と Date オブジェクトの組み合わせです。
この記事では、1秒ごとに更新されるシンプルな時計の作り方から始まり、デジタル時計のCSSスタイリング、複数タイムゾーン対応、カウントダウンタイマー、タブが非アクティブなときに処理を止めるパフォーマンス最適化まで、実践的な実装パターンを体系的に解説します。
基本実装:1秒ごとに時刻を更新する
まずは最もシンプルな構成を見てみましょう。setInterval で関数を1秒ごとに呼び出し、Date オブジェクトから現在時刻を取得してDOMに書き出します。
<div id="clock"></div>
function updateClock() {
const now = new Date();
document.getElementById('clock').textContent = now.toLocaleTimeString('ja-JP');
}
updateClock(); // 初回即時実行(setInterval は1秒後から始まるため)
setInterval(updateClock, 1000);
setInterval は登録後1秒経ってから初めて呼ばれます。updateClock() を先に一度呼んでおかないと、表示が1秒間空白になります。setInterval の詳しい使い方は【JavaScript】setIntervalの使い方完全解説も参照してください。
日付と時刻を両方表示するパターン
年月日・曜日・時刻を個別に取得して組み合わせることで、自由なフォーマットで表示できます。
<div id="date-display"></div> <div id="time-display"></div>
const DAYS = ['日', '月', '火', '水', '木', '金', '土'];
function updateDateTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const week = DAYS[now.getDay()];
const hours = String(now.getHours()).padStart(2, '0');
const mins = String(now.getMinutes()).padStart(2, '0');
const secs = String(now.getSeconds()).padStart(2, '0');
document.getElementById('date-display').textContent =
`${year}年${month}月${day}日(${week})`;
document.getElementById('time-display').textContent =
`${hours}:${mins}:${secs}`;
}
updateDateTime();
setInterval(updateDateTime, 1000);
Date オブジェクトの各メソッドの詳細は【JavaScript】日付を取得する方法完全ガイドをご覧ください。
Intl.DateTimeFormat でタイムゾーン・多言語対応
グローバル対応が必要なサービスでは Intl.DateTimeFormat を使うと、タイムゾーン指定や多言語フォーマットが簡単に実現できます。
// 複数都市の時計を同時表示するユーティリティ
const CITY_CLOCKS = [
{ label: '東京', timeZone: 'Asia/Tokyo' },
{ label: 'ニューヨーク', timeZone: 'America/New_York' },
{ label: 'ロンドン', timeZone: 'Europe/London' },
];
function formatWithTimezone(date, timeZone, locale = 'ja-JP') {
return new Intl.DateTimeFormat(locale, {
timeZone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
}).format(date);
}
function updateCityClocks() {
const now = new Date();
CITY_CLOCKS.forEach(({ label, timeZone }) => {
const el = document.getElementById(`clock-${timeZone.replace('/', '-')}`);
if (el) el.textContent = `${label}: ${formatWithTimezone(now, timeZone)}`;
});
}
updateCityClocks();
setInterval(updateCityClocks, 1000);
Intl APIの詳しい使い方は【JavaScript】Intl APIを使った日付・数値の国際化フォーマットも参考にしてください。
デジタル時計のCSS スタイリング
見た目にこだわったデジタル時計を実装してみましょう。Google Fonts の等幅フォント + CSSだけで本格的なクロックUIが作れます。
<div class="digital-clock"> <span id="dc-date"></span> <span id="dc-time"></span> </div>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');
.digital-clock {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
background: #0f172a;
color: #38bdf8;
font-family: 'Share Tech Mono', monospace;
padding: 32px 48px;
border-radius: 12px;
box-shadow: 0 0 30px rgba(56, 189, 248, 0.3);
}
#dc-date {
font-size: 1.2rem;
opacity: 0.8;
}
#dc-time {
font-size: 3rem;
letter-spacing: 0.1em;
text-shadow: 0 0 10px rgba(56, 189, 248, 0.8);
}
const DAYS = ['日', '月', '火', '水', '木', '金', '土'];
function updateDigitalClock() {
const now = new Date();
const pad = (n) => String(n).padStart(2, '0');
const dateStr = `${now.getFullYear()}/${pad(now.getMonth()+1)}/${pad(now.getDate())}(${DAYS[now.getDay()]})`;
const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
document.getElementById('dc-date').textContent = dateStr;
document.getElementById('dc-time').textContent = timeStr;
}
updateDigitalClock();
setInterval(updateDigitalClock, 1000);
clearInterval で時計を停止・再開する
スタート/ストップボタンを付けた制御可能な時計の実装例です。setInterval の戻り値(タイマーID)を保持しておくことで、いつでも停止できます。
<div id="clock-display"></div> <button id="btn-toggle">停止</button>
let timerId = null;
function tick() {
document.getElementById('clock-display').textContent =
new Date().toLocaleTimeString('ja-JP');
}
function startClock() {
if (timerId !== null) return; // 多重起動を防止
tick();
timerId = setInterval(tick, 1000);
document.getElementById('btn-toggle').textContent = '停止';
}
function stopClock() {
clearInterval(timerId);
timerId = null;
document.getElementById('btn-toggle').textContent = 'スタート';
}
document.getElementById('btn-toggle').addEventListener('click', () => {
timerId !== null ? stopClock() : startClock();
});
startClock(); // 初期起動
カウントダウンタイマーの実装
期日まであと何日・何時間・何分・何秒かを表示するカウントダウンタイマーです。サイトのリリース告知やキャンペーン終了時刻の表示に活用できます。
<div id="countdown"> <span id="cd-days"></span>日 <span id="cd-hours"></span>時間 <span id="cd-mins"></span>分 <span id="cd-secs"></span>秒 </div>
// 目標日時を設定(例: 2026年12月31日 23:59:59 JST)
const TARGET = new Date('2026-12-31T23:59:59+09:00');
function updateCountdown() {
const now = new Date();
const diff = TARGET - now; // ミリ秒差
if (diff <= 0) {
clearInterval(countdownTimer);
document.getElementById('countdown').textContent = '終了しました';
return;
}
const totalSecs = Math.floor(diff / 1000);
const days = Math.floor(totalSecs / 86400);
const hours = Math.floor((totalSecs % 86400) / 3600);
const mins = Math.floor((totalSecs % 3600) / 60);
const secs = totalSecs % 60;
const pad = (n) => String(n).padStart(2, '0');
document.getElementById('cd-days').textContent = days;
document.getElementById('cd-hours').textContent = pad(hours);
document.getElementById('cd-mins').textContent = pad(mins);
document.getElementById('cd-secs').textContent = pad(secs);
}
updateCountdown();
const countdownTimer = setInterval(updateCountdown, 1000);
+09:00 のようにオフセットを明示するのが安全です。new Date("2026-12-31T23:59:59") のようにオフセットなしで書くと、実行環境のタイムゾーンによって解釈が変わる場合があります。requestAnimationFrame と setInterval の使い分け
時計の実装には setInterval が一般的ですが、アニメーションを伴う場合は requestAnimationFrame(rAF)も選択肢になります。それぞれの特徴を比較します。
| setInterval | requestAnimationFrame | |
|---|---|---|
| 更新タイミング | 指定間隔(例: 1000ms) | ブラウザの描画フレーム(通常60fps) |
| 用途 | 1秒単位の時計、カウントダウン | 滑らかなアニメーション付き時計 |
| タブ非表示時 | 間隔が不正確になる場合あり | 自動的に一時停止(省電力) |
| 停止方法 | clearInterval | cancelAnimationFrame |
秒単位の時計なら setInterval で十分ですが、アナログ時計の針をなめらかに動かしたい場合などは requestAnimationFrame が適しています。
let rafId;
function tickRaf() {
document.getElementById('clock').textContent =
new Date().toLocaleTimeString('ja-JP');
rafId = requestAnimationFrame(tickRaf);
}
rafId = requestAnimationFrame(tickRaf);
// 停止するとき
// cancelAnimationFrame(rafId);
パフォーマンス最適化:タブが非アクティブなときに停止する
時計が常に動き続けると、バックグラウンドタブでも無駄にCPUを使います。visibilitychange イベントを使えば、タブが非表示のときに自動停止・復帰させることができます。
let clockTimer = null;
function tick() {
document.getElementById('clock').textContent =
new Date().toLocaleTimeString('ja-JP');
}
function startClock() {
tick();
clockTimer = setInterval(tick, 1000);
}
function stopClock() {
clearInterval(clockTimer);
clockTimer = null;
}
// タブの表示状態が変わったら制御
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
stopClock(); // タブが非表示になったら停止
} else {
startClock(); // タブが表示されたら即座に再開
}
});
startClock();
requestAnimationFrame はタブが非表示になると自動的に一時停止するため、この処理は不要です。setInterval を使う場合のみ有効です。アクセシビリティ対応(aria-live)
スクリーンリーダーを使うユーザーに対して、時計の更新を適切に伝えるには aria-live 属性を設定します。ただし、毎秒読み上げられると非常にうるさくなるため、時刻表示は aria-live="off"(または属性なし)にするのが一般的です。
<!-- 時計は視覚的な補助のため、スクリーンリーダーからは除外 --> <div id="clock" aria-hidden="true"></div> <!-- カウントダウン終了など重要な通知のみ読み上げる場合 --> <div id="countdown-result" aria-live="polite" aria-atomic="true"></div>
完成形サンプル(コピペOK)
これまでの内容をまとめた、日付・時刻・曜日を表示するシンプルな完成サンプルです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>リアルタイム時計</title>
<style>
#clock-widget {
font-family: monospace;
text-align: center;
padding: 24px;
}
#clock-date { font-size: 1.2rem; color: #555; }
#clock-time { font-size: 3rem; font-weight: bold; color: #0ea5e9; }
</style>
</head>
<body>
<div id="clock-widget" aria-hidden="true">
<div id="clock-date"></div>
<div id="clock-time"></div>
</div>
<script>
const DAYS = ["日","月","火","水","木","金","土"];
function updateClock() {
const now = new Date();
const pad = (n) => String(n).padStart(2, "0");
document.getElementById("clock-date").textContent =
`${now.getFullYear()}年${pad(now.getMonth()+1)}月${pad(now.getDate())}日(${DAYS[now.getDay()]})`;
document.getElementById("clock-time").textContent =
`${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
}
updateClock();
setInterval(updateClock, 1000);
</script>
</body>
</html>
よくある問題とデバッグ
最初の1秒間だけ空白になる
setInterval は最初の呼び出しを1秒後に行います。setInterval の前に関数を一度直接呼び出すことで解決します。
// NG: 1秒間だけ空白になる setInterval(updateClock, 1000); // OK: 即時実行してから定期実行 updateClock(); setInterval(updateClock, 1000);
秒が飛んだり重複したりする
複数箇所で setInterval を呼んでいると、タイマーが重複して動いてしまいます。setInterval の戻り値を変数に保存し、再実行前に clearInterval してリセットするのが安全です。
let timerId = null;
function startClock() {
clearInterval(timerId); // 既存タイマーを必ずクリア
timerId = setInterval(updateClock, 1000);
}
setInterval の時刻がズレていく
setInterval(fn, 1000) は正確に1000msごとに実行されるわけではありません。タブの負荷や処理時間によって数ms〜数十ms単位でズレが蓄積します。長時間動かすアプリでズレが問題になる場合は、実行ごとに Date から正確な時刻を読み取るため実用上は問題ありません。時計の表示は常に new Date() から取得するので、ズレは表示には影響しません。
よくある質問
setInterval は指定間隔で繰り返し実行します。setTimeout は1回だけ実行します。時計のように繰り返す場合は setInterval、または setTimeout を再帰的に呼ぶ方法もあります。詳しくはsetIntervalの使い方完全解説をご覧ください。Intl.DateTimeFormat に timeZone オプションを指定します。例えば timeZone: "America/New_York" とするとニューヨーク時刻が表示されます。タイムゾーン識別子は IANA タイムゾーンデータベースの名称を使います。toLocaleTimeString("ja-JP", { hour12: true }) で「午前9:05:30」のような12時間表示になります。Intl.DateTimeFormat を使う場合は hour12: true を指定してください。transform: rotate() で針の角度を計算して回転させます。秒針なら now.getSeconds() * 6 度、分針なら (now.getMinutes() * 6) + (now.getSeconds() * 0.1) 度が基本計算式です。なめらかな動きには requestAnimationFrame を使うのがおすすめです。clearInterval(timerId) を呼んでください。タイマーが残ったままだとメモリリークの原因になります。React の場合は useEffect のクリーンアップ関数、Vue 3 の場合は onUnmounted で停止します。まとめ
JavaScriptでリアルタイム時計を実装する核心は 「setInterval で1秒ごとに new Date() を呼んでDOMを更新する」ことです。これを押さえれば、あとはフォーマットや見た目・機能を拡張していくだけです。
setIntervalの前に関数を一度呼んで初回空白を防ぐclearIntervalでタイマーIDを管理して多重起動を防ぐ- タイムゾーン対応には
Intl.DateTimeFormatのtimeZoneオプション - カウントダウンは目標日時との差分(ミリ秒)を計算して分解する
visibilitychangeイベントでバックグラウンドタブ時のCPU消費を節約
日付まわりのより深い操作(加算・減算・差分計算)については【JavaScript】日時の計算まとめも合わせてご覧ください。