MENU

CSVからHTMLテーブルを生成するツール

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>CSV → HTMLテーブル変換</title>

<style>
:root {
  --c-accent: #6699ff;
}

body {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  margin: 0;
  padding: 20px;
  background-color: #f8f9fa;
  line-height: 1.6;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  overflow: hidden;
}

.header {
  background: linear-gradient(135deg, var(--c-accent), #4d7fff);
  color: white;
  padding: 24px;
  text-align: center;
}

.header h1 {
  margin: 0;
  font-size: 28px;
  font-weight: 600;
}

.header p {
  margin: 8px 0 0;
  opacity: 0.9;
}

.content {
  padding: 24px;
}

.section {
  margin-bottom: 32px;
}

.section-title {
  font-size: 20px;
  font-weight: 600;
  margin-bottom: 16px;
  color: #333;
  border-left: 4px solid var(--c-accent);
  padding-left: 12px;
}

/* 入力エリア */
.input-area {
  background: #f8f9fa;
  border-radius: 8px;
  padding: 16px;
  margin-bottom: 16px;
}

#csvInput {
  width: 100%;
  height: 150px;
  border: 2px solid #ddd;
  border-radius: 6px;
  padding: 12px;
  font-family: 'Courier New', monospace;
  font-size: 14px;
  resize: vertical;
  transition: border-color 0.3s;
}

#csvInput:focus {
  outline: none;
  border-color: var(--c-accent);
}

/* オプション */
.options {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.option-group {
  display: flex;
  align-items: center;
  gap: 8px;
}

.option-group label {
  font-weight: 500;
  color: #555;
}

.option-group input, .option-group select {
  padding: 6px 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

/* ボタン */
.btn-group {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}

.btn {
  padding: 12px 24px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.3s;
  text-decoration: none;
  display: inline-block;
  text-align: center;
}

.btn-primary {
  background: var(--c-accent);
  color: white;
}

.btn-primary:hover {
  background: #4d7fff;
  transform: translateY(-1px);
}

.btn-secondary {
  background: #6c757d;
  color: white;
}

.btn-secondary:hover {
  background: #545b62;
  transform: translateY(-1px);
}

.btn-success {
  background: #28a745;
  color: white;
}

.btn-success:hover {
  background: #218838;
  transform: translateY(-1px);
}

/* プレビューエリア */
.preview-area {
  border: 2px solid #ddd;
  border-radius: 8px;
  padding: 16px;
  background: white;
  min-height: 200px;
  overflow-x: auto;
}

/* テーブルスタイル */
.generated-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0;
  font-size: 14px;
}

.generated-table th,
.generated-table td {
  padding: 12px;
  text-align: left;
  border: 1px solid #ddd;
}

.generated-table th {
  background-color: #f8f9fa;
  font-weight: 600;
  color: #495057;
}

.generated-table tr:nth-child(even) {
  background-color: #f8f9fa;
}

.generated-table tr:hover {
  background-color: #e3f2fd;
}

/* コード表示エリア */
.code-area {
  background: #2d3748;
  color: #e2e8f0;
  padding: 16px;
  border-radius: 6px;
  font-family: 'Courier New', monospace;
  font-size: 13px;
  overflow-x: auto;
  white-space: pre;
  max-height: 300px;
  overflow-y: auto;
}

/* トースト */
#toast {
  position: fixed;
  top: 20px;
  right: 20px;
  background: #28a745;
  color: white;
  padding: 12px 20px;
  border-radius: 6px;
  font-size: 14px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
  z-index: 1000;
}

#toast.show {
  opacity: 1;
}

/* サンプル */
.sample {
  background: #e8f4f8;
  border-left: 4px solid #17a2b8;
  padding: 16px;
  border-radius: 0 6px 6px 0;
  margin: 16px 0;
}

.sample-title {
  font-weight: 600;
  color: #0c5460;
  margin-bottom: 8px;
}

.sample code {
  background: white;
  padding: 2px 6px;
  border-radius: 3px;
  font-family: 'Courier New', monospace;
  color: #d73502;
}
</style>
</head>
<body>

<div class="container">
  <div class="header">
    <h1>CSV → HTMLテーブル変換ツール</h1>
    <p>ExcelやGoogleスプレッドシートからコピーしたデータを美しいHTMLテーブルに変換</p>
  </div>

  <div class="content">
    <!-- 入力セクション -->
    <div class="section">
      <h2 class="section-title">1. CSVデータを入力</h2>
      
      <div class="sample">
        <div class="sample-title">使用例:</div>
        Excelで表を選択 → Ctrl+C でコピー → 下のテキストエリアに Ctrl+V で貼り付け
        <br><code>名前,年齢,職業<br>田中太郎,25,エンジニア<br>佐藤花子,30,デザイナー</code>
      </div>

      <div class="input-area">
        <textarea id="csvInput" placeholder="ここにCSVデータを貼り付けてください...&#10;例:&#10;名前,年齢,職業&#10;田中太郎,25,エンジニア&#10;佐藤花子,30,デザイナー"></textarea>
      </div>

      <div class="options">
        <div class="option-group">
          <input type="checkbox" id="hasHeader" checked>
          <label for="hasHeader">1行目をヘッダーとして扱う</label>
        </div>
        <div class="option-group">
          <label for="delimiter">区切り文字:</label>
          <select id="delimiter">
            <option value="," selected>カンマ (,)</option>
            <option value="\t">タブ</option>
            <option value=";">セミコロン (;)</option>
            <option value="|">パイプ (|)</option>
          </select>
        </div>
        <div class="option-group">
          <label for="tableClass">テーブルクラス:</label>
          <input type="text" id="tableClass" placeholder="例: table table-striped" value="generated-table">
        </div>
      </div>

      <div class="btn-group">
        <button class="btn btn-primary" onclick="generateTable()">テーブル生成</button>
        <button class="btn btn-secondary" onclick="clearInput()">クリア</button>
        <button class="btn btn-secondary" onclick="loadSample()">サンプルデータ読込</button>
      </div>
    </div>

    <!-- プレビューセクション -->
    <div class="section">
      <h2 class="section-title">2. プレビュー</h2>
      <div class="preview-area" id="previewArea">
        <p style="color: #666; text-align: center; margin: 60px 0;">上記でデータを入力して「テーブル生成」ボタンを押してください</p>
      </div>
    </div>

    <!-- HTMLコードセクション -->
    <div class="section">
      <h2 class="section-title">3. HTMLコード</h2>
      <div class="btn-group">
        <button class="btn btn-success" onclick="copyHtmlCode()" id="copyBtn">HTMLコードをコピー</button>
        <button class="btn btn-secondary" onclick="exportHtml()">HTMLファイルとして保存</button>
      </div>
      <div class="code-area" id="htmlCode">
HTMLコードがここに表示されます
      </div>
    </div>
  </div>
</div>

<div id="toast">コピーしました!</div>

<script>
function parseCSV(csvText, delimiter = ',') {
  const lines = csvText.trim().split('\n');
  const result = [];
  
  for (let line of lines) {
    if (line.trim() === '') continue;
    
    const row = [];
    let current = '';
    let inQuotes = false;
    
    for (let i = 0; i < line.length; i++) {
      const char = line[i];
      const nextChar = line[i + 1];
      
      if (char === '"') {
        if (inQuotes && nextChar === '"') {
          current += '"';
          i++; // Skip next quote
        } else {
          inQuotes = !inQuotes;
        }
      } else if (char === delimiter && !inQuotes) {
        row.push(current.trim());
        current = '';
      } else {
        current += char;
      }
    }
    row.push(current.trim());
    result.push(row);
  }
  
  return result;
}

function generateTable() {
  const csvInput = document.getElementById('csvInput').value;
  const hasHeader = document.getElementById('hasHeader').checked;
  const delimiter = document.getElementById('delimiter').value === '\\t' ? '\t' : document.getElementById('delimiter').value;
  const tableClass = document.getElementById('tableClass').value;
  
  if (!csvInput.trim()) {
    showToast('CSVデータを入力してください', 'error');
    return;
  }
  
  try {
    const rows = parseCSV(csvInput, delimiter);
    
    if (rows.length === 0) {
      showToast('有効なCSVデータが見つかりません', 'error');
      return;
    }
    
    // HTMLテーブルを生成
    let tableHtml = `<table class="${tableClass}">`;
    
    if (hasHeader && rows.length > 0) {
      tableHtml += '\n  <thead>\n    <tr>';
      for (let cell of rows[0]) {
        tableHtml += `\n      <th>${escapeHtml(cell)}</th>`;
      }
      tableHtml += '\n    </tr>\n  </thead>';
      
      if (rows.length > 1) {
        tableHtml += '\n  <tbody>';
        for (let i = 1; i < rows.length; i++) {
          tableHtml += '\n    <tr>';
          for (let cell of rows[i]) {
            tableHtml += `\n      <td>${escapeHtml(cell)}</td>`;
          }
          tableHtml += '\n    </tr>';
        }
        tableHtml += '\n  </tbody>';
      }
    } else {
      tableHtml += '\n  <tbody>';
      for (let row of rows) {
        tableHtml += '\n    <tr>';
        for (let cell of row) {
          tableHtml += `\n      <td>${escapeHtml(cell)}</td>`;
        }
        tableHtml += '\n    </tr>';
      }
      tableHtml += '\n  </tbody>';
    }
    
    tableHtml += '\n</table>';
    
    // プレビュー表示
    document.getElementById('previewArea').innerHTML = tableHtml;
    
    // HTMLコード表示
    document.getElementById('htmlCode').textContent = tableHtml;
    
    showToast('テーブルを生成しました');
    
  } catch (error) {
    showToast('CSVの解析でエラーが発生しました', 'error');
    console.error(error);
  }
}

function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

function clearInput() {
  document.getElementById('csvInput').value = '';
  document.getElementById('previewArea').innerHTML = '<p style="color: #666; text-align: center; margin: 60px 0;">上記でデータを入力して「テーブル生成」ボタンを押してください</p>';
  document.getElementById('htmlCode').textContent = 'HTMLコードがここに表示されます';
}

function loadSample() {
  const sampleData = `名前,年齢,職業,住所
田中太郎,25,エンジニア,東京都
佐藤花子,30,デザイナー,大阪府
山田次郎,28,営業,愛知県
鈴木一郎,35,マネージャー,福岡県
高橋美咲,27,マーケター,神奈川県`;
  
  document.getElementById('csvInput').value = sampleData;
  generateTable();
}

function copyHtmlCode() {
  const htmlCode = document.getElementById('htmlCode').textContent;
  
  if (htmlCode === 'HTMLコードがここに表示されます') {
    showToast('まずテーブルを生成してください', 'error');
    return;
  }
  
  navigator.clipboard.writeText(htmlCode).then(() => {
    showToast('HTMLコードをコピーしました');
  }).catch(() => {
    // フォールバック
    const textArea = document.createElement('textarea');
    textArea.value = htmlCode;
    document.body.appendChild(textArea);
    textArea.select();
    document.execCommand('copy');
    document.body.removeChild(textArea);
    showToast('HTMLコードをコピーしました');
  });
}

function exportHtml() {
  const htmlCode = document.getElementById('htmlCode').textContent;
  
  if (htmlCode === 'HTMLコードがここに表示されます') {
    showToast('まずテーブルを生成してください', 'error');
    return;
  }
  
  const fullHtml = `<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Generated Table</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    .generated-table { width: 100%; border-collapse: collapse; margin: 0; }
    .generated-table th, .generated-table td { padding: 12px; text-align: left; border: 1px solid #ddd; }
    .generated-table th { background-color: #f8f9fa; font-weight: 600; }
    .generated-table tr:nth-child(even) { background-color: #f8f9fa; }
    .generated-table tr:hover { background-color: #e3f2fd; }
  </style>
</head>
<body>
${htmlCode}
</body>
</html>`;
  
  const blob = new Blob([fullHtml], { type: 'text/html' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'generated-table.html';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
  
  showToast('HTMLファイルを保存しました');
}

function showToast(message, type = 'success') {
  const toast = document.getElementById('toast');
  toast.textContent = message;
  toast.className = type === 'error' ? 'show error' : 'show';
  toast.style.backgroundColor = type === 'error' ? '#dc3545' : '#28a745';
  
  setTimeout(() => {
    toast.classList.remove('show');
  }, 3000);
}

// 初期設定
document.addEventListener('DOMContentLoaded', function() {
  // Ctrl+Enter でテーブル生成
  document.getElementById('csvInput').addEventListener('keydown', function(e) {
    if (e.ctrlKey && e.key === 'Enter') {
      generateTable();
    }
  });
});
</script>

</body>
</html>
よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

目次