Excelファイル(.xlsx)読み込み・テーブル表示
このコンポーネントについて
Excelファイル(.xlsx)をアップロード前に確認したい場面では、ブラウザ内でシートの内容をプレビューできると安全です。このページでは SheetJS を使い、選択またはドラッグ&ドロップした .xlsx ファイルをサーバーへ送らずに読み込み、HTML tableで表示します。複数のワークシートを含むブックでは、シート名を選んで表示内容を切り替えられます。数式やマクロは実行せず、Excelファイルに保存されている表示値のみを扱います。
- ブラウザ内での .xlsx 読み込み —
file.arrayBuffer()と SheetJS のXLSX.read()で解析。ファイル内容はサーバーへ送信しません - シート選択 — 読み込んだブックのシート名から選択肢を作り、任意のシートへすぐに切り替えられます
- テーブルプレビュー — 先頭行をヘッダー、2行目以降をデータ行として表示。横・縦スクロールと固定ヘッダーに対応します
- 表示件数の上限 — 10MBまでのファイルを受け付け、各シートは先頭100行までを表示して負荷を抑えます
- 安全な文字列表示 — ファイル名・シート名・セル内容はすべて
textContentで表示し、innerHTMLに渡しません
実装のポイント・注意点
ブラウザ版のSheetJSは、ファイル全体をメモリに読み込んでから解析します。そのためページングを付けても読込時のメモリ使用量は減りません。このサンプルでは解析前の10MB制限と、描画を先頭100行に絞ることで、ブラウザが重くなることを防ぎます。
数式はブラウザで再計算せず、ファイルに保存済みの表示値だけを使います。VBAマクロも実行しません。CSVを読み込みたい場合は、専用のCSV読み込み・テーブル表示を、CSVの検証・取込画面が必要な場合はCSVインポート画面を使ってください。
デモ
このシートには表示できるデータがありません。
サンプルソース
3つのファイルを同じフォルダに保存し、index.html をブラウザで開くと動作確認できます。
ファイル名:index.html / style.css / script.js
— SheetJSはCDNから読み込むため、インターネット接続が必要です。保存時の文字コードは UTF-8 を指定してください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Excelファイル読み込み</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="xlsx-viewer">
<label class="xlsx-viewer__area" id="xlsx-upload-area" for="xlsx-input">
<span class="xlsx-viewer__text">クリックまたはドラッグ&ドロップでExcelファイルを選択</span>
<span class="xlsx-viewer__sub">.xlsx ファイル・10MBまで(サーバー送信なし)</span>
<input type="file" id="xlsx-input" class="xlsx-viewer__input" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
</label>
<p class="xlsx-viewer__status" id="xlsx-status" role="status" aria-live="polite"></p>
<p class="xlsx-viewer__error" id="xlsx-error" role="alert" hidden></p>
<div class="xlsx-viewer__result" id="xlsx-result" hidden>
<div class="xlsx-viewer__toolbar">
<p class="xlsx-viewer__file-name" id="xlsx-file-name"></p>
<label class="xlsx-viewer__sheet-label" for="xlsx-sheet-select">
表示するシート
<select id="xlsx-sheet-select" class="xlsx-viewer__sheet-select"></select>
</label>
</div>
<p class="xlsx-viewer__count" id="xlsx-row-count"></p>
<p class="xlsx-viewer__empty" id="xlsx-empty" hidden>このシートには表示できるデータがありません。</p>
<div class="xlsx-viewer__scroll" id="xlsx-scroll">
<table class="xlsx-viewer__table">
<thead id="xlsx-thead"></thead>
<tbody id="xlsx-tbody"></tbody>
</table>
</div>
</div>
<button class="xlsx-viewer__reset" id="xlsx-reset" type="button" hidden>リセット</button>
</div>
<script src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"></script>
<script src="./script.js"></script>
</body>
</html>
* { box-sizing: border-box; }
body { margin: 0; padding: 24px; color: #1a2332; background: #f7f9fc; font-family: sans-serif; }
.xlsx-viewer { width: min(100%, 480px); margin: 0 auto; }
.xlsx-viewer__area { display: flex; flex-direction: column; align-items: center; gap: 8px; min-height: 280px; padding: 32px 20px; color: #2b7fe8; background: #fff; border: 2px dashed #a9c8ef; border-radius: 12px; cursor: pointer; text-align: center; }
.xlsx-viewer__area:hover, .xlsx-viewer__area.is-dragover { background: #eef6ff; border-color: #2b7fe8; }
.xlsx-viewer__area.is-loading { opacity: .65; pointer-events: none; }
.xlsx-viewer__text { color: #1a2332; font-weight: 700; }
.xlsx-viewer__sub, .xlsx-viewer__status, .xlsx-viewer__count { color: #5a6a7a; font-size: 13px; }
.xlsx-viewer__input { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.xlsx-viewer__error { margin: 12px 0 0; padding: 12px; color: #b42318; background: #fef3f2; border: 1px solid #fecdca; border-radius: 8px; font-size: 14px; }
.xlsx-viewer__toolbar { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-top: 20px; flex-wrap: wrap; }
.xlsx-viewer__file-name { margin: 0; font-weight: 700; overflow-wrap: anywhere; }
.xlsx-viewer__sheet-label { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 700; }
.xlsx-viewer__sheet-select { max-width: 260px; padding: 7px 10px; border: 1px solid #b8c4d1; border-radius: 6px; background: #fff; font: inherit; }
.xlsx-viewer__scroll { max-height: 400px; overflow: auto; border: 1px solid #dce3eb; border-radius: 8px; }
.xlsx-viewer__table { width: max-content; min-width: 100%; border-collapse: collapse; background: #fff; font-size: 14px; }
.xlsx-viewer__table th, .xlsx-viewer__table td { max-width: 280px; padding: 10px 12px; border-bottom: 1px solid #e7edf3; overflow-wrap: anywhere; text-align: left; vertical-align: top; }
.xlsx-viewer__table th { position: sticky; top: 0; z-index: 1; color: #fff; background: #2b7fe8; }
.xlsx-viewer__table tbody tr:nth-child(even) { background: #f8fafc; }
.xlsx-viewer__empty { padding: 24px; color: #5a6a7a; background: #f8fafc; border: 1px solid #dce3eb; border-radius: 8px; text-align: center; }
.xlsx-viewer__reset { display: block; margin: 16px 0 0 auto; padding: 8px 16px; color: #5a6a7a; background: #fff; border: 1px solid #b8c4d1; border-radius: 6px; cursor: pointer; font: inherit; }
@media (max-width: 480px) { body { padding: 16px; } .xlsx-viewer__toolbar, .xlsx-viewer__sheet-label { align-items: stretch; flex-direction: column; } .xlsx-viewer__sheet-select { max-width: none; } }
(function () {
var MAX_FILE_SIZE = 10 * 1024 * 1024;
var MAX_PREVIEW_ROWS = 100;
var workbook = null;
var input = document.getElementById('xlsx-input');
var area = document.getElementById('xlsx-upload-area');
var status = document.getElementById('xlsx-status');
var error = document.getElementById('xlsx-error');
var result = document.getElementById('xlsx-result');
var fileName = document.getElementById('xlsx-file-name');
var sheetSelect = document.getElementById('xlsx-sheet-select');
var rowCount = document.getElementById('xlsx-row-count');
var empty = document.getElementById('xlsx-empty');
var scroll = document.getElementById('xlsx-scroll');
var thead = document.getElementById('xlsx-thead');
var tbody = document.getElementById('xlsx-tbody');
var reset = document.getElementById('xlsx-reset');
input.addEventListener('change', function () { if (input.files[0]) handleFile(input.files[0]); });
sheetSelect.addEventListener('change', function () { renderSheet(sheetSelect.value); });
reset.addEventListener('click', resetDemo);
['dragenter', 'dragover'].forEach(function (eventName) {
area.addEventListener(eventName, function (event) { event.preventDefault(); area.classList.add('is-dragover'); });
});
['dragleave', 'drop'].forEach(function (eventName) {
area.addEventListener(eventName, function (event) { event.preventDefault(); area.classList.remove('is-dragover'); });
});
area.addEventListener('drop', function (event) { var file = event.dataTransfer.files[0]; if (file) handleFile(file); });
async function handleFile(file) {
clearResult();
if (!/\.xlsx$/i.test(file.name)) return showError('.xlsx ファイルを選択してください。');
if (file.size > MAX_FILE_SIZE) return showError('10MB以下の .xlsx ファイルを選択してください。');
setLoading(true);
status.textContent = '読み込んでいます…';
try {
workbook = XLSX.read(await file.arrayBuffer(), { dense: true });
if (!workbook.SheetNames.length) throw new Error('sheet not found');
fileName.textContent = file.name + '(' + workbook.SheetNames.length + 'シート)';
workbook.SheetNames.forEach(function (name) { var option = document.createElement('option'); option.value = name; option.textContent = name; sheetSelect.appendChild(option); });
result.hidden = false;
reset.hidden = false;
status.textContent = '読み込みました。表示するシートを選べます。';
renderSheet(workbook.SheetNames[0]);
} catch (e) {
workbook = null;
showError('ファイルを読み込めませんでした。破損またはパスワード保護されていないか確認してください。');
} finally {
setLoading(false);
}
}
function renderSheet(sheetName) {
var rows = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { header: 1, defval: '', raw: false, blankrows: false });
thead.replaceChildren(); tbody.replaceChildren(); scroll.hidden = !rows.length; empty.hidden = Boolean(rows.length);
if (!rows.length) { rowCount.textContent = ''; return; }
var columnCount = rows.reduce(function (max, row) { return Math.max(max, row.length); }, 0);
var headerRow = document.createElement('tr');
for (var i = 0; i < columnCount; i += 1) { var th = document.createElement('th'); th.textContent = rows[0][i] == null ? '' : rows[0][i]; headerRow.appendChild(th); }
thead.appendChild(headerRow);
var dataRows = rows.slice(1); var fragment = document.createDocumentFragment();
dataRows.slice(0, MAX_PREVIEW_ROWS).forEach(function (row) { var tr = document.createElement('tr'); for (var i = 0; i < columnCount; i += 1) { var td = document.createElement('td'); td.textContent = row[i] == null ? '' : row[i]; tr.appendChild(td); } fragment.appendChild(tr); });
tbody.appendChild(fragment);
rowCount.textContent = dataRows.length > MAX_PREVIEW_ROWS ? dataRows.length + '件中 ' + MAX_PREVIEW_ROWS + '件を表示' : dataRows.length + '件';
}
function clearResult() { workbook = null; error.hidden = true; error.textContent = ''; result.hidden = true; reset.hidden = true; fileName.textContent = ''; rowCount.textContent = ''; sheetSelect.replaceChildren(); thead.replaceChildren(); tbody.replaceChildren(); }
function showError(message) { error.textContent = message; error.hidden = false; status.textContent = ''; }
function setLoading(isLoading) { input.disabled = isLoading; area.classList.toggle('is-loading', isLoading); area.setAttribute('aria-busy', String(isLoading)); }
function resetDemo() { input.value = ''; clearResult(); status.textContent = ''; setLoading(false); }
}());
AI用プロンプト
⚠️ 数式を再計算したり、VBAマクロを実行したりする用途には使わないでください。ブラウザ内のプレビュー専用です。
💡 既存の取込画面に組み込む場合は、プロンプト末尾に「読込後の検証・送信処理は既存の関数へつなげてください」と追記するとスムーズです。
# Excelファイル(.xlsx)読み込み・テーブル表示 作成依頼
## 概要
SheetJS Community Edition 0.20.3 を使い、.xlsx ファイルをブラウザ内だけで読み込み、シートを選んでテーブル表示するUIを実装してください。ファイルはサーバーへ送信しません。
## 要件
- .xlsx ファイルのみ受け付け、クリックとドラッグ&ドロップの両方に対応すること
- 10MBを超えるファイルは解析前にエラー表示すること
- `file.arrayBuffer()` で一度だけ読み込み、`XLSX.read(arrayBuffer, { dense: true })` で解析すること
- 先頭シートを表示し、シート名のselectで表示を切り替えられること
- シート切り替えではファイルを再読込・再解析しないこと
- 先頭行をヘッダー、2行目以降をデータ行として表示すること
- 各シートは先頭100データ行だけを表示し、101行以上は「N件中 100件を表示」と案内すること
- ページングは実装しないこと
- テーブルを横・縦スクロール対応にし、ヘッダー行をstickyで固定すること
- .xlsx 以外、10MB超、空シート、解析できないファイルには分かりやすいメッセージを表示すること
- 数式の評価やVBAマクロの実行は行わず、保存済みの表示値だけをプレビューすること
- ファイル名・シート名・セル内容は `textContent` で表示し、`innerHTML` に渡さないこと
- リセットでワークブック参照と表示内容をクリアすること
## 技術仕様
- HTML / CSS / バニラJavaScript
- SheetJS Community Edition 0.20.3 を `https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js` から読み込む
- レスポンシブ対応:必要
## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。