JavaScript | 配列の全要素を連結して文字列を得る方法

JavaScript JavaScript
スポンサーリンク

では最終ステップとして、完全版 CSV エディタ を作ります。機能は以下の通りです:


完全版 CSV エディタの機能

  1. CSV 読み込み・ダウンロード
  2. 行・列の追加・削除
  3. セル選択・複数選択
  4. セル複製
  5. 検索・置換
  6. セルのドラッグ&ドロップで移動
  7. 選択列で行ソート
  8. 区切り文字の変更(カンマ、セミコロン、タブ対応)

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>完全版 CSV エディタ</title>
<style>
  body { font-family: sans-serif; margin: 20px; }
  table { border-collapse: collapse; margin-top: 10px; }
  td, th { border: 1px solid #ccc; padding: 5px 10px; min-width: 80px; text-align: center; }
  td.drag-over { background-color: #ffa; }
  td.selected { background-color: #cff; }
  input[type="file"], select, button, input[type="text"] { margin: 5px 0; }
</style>
</head>
<body>

<h2>完全版 CSV エディタ</h2>

<div id="controls">
  <input type="file" id="csvFile" accept=".csv">
  区切り文字: 
  <select id="delimiter">
    <option value=",">カンマ (,)</option>
    <option value=";">セミコロン (;)</option>
    <option value="\t">タブ</option>
  </select>
  <button id="addRow">行追加</button>
  <button id="addCol">列追加</button>
  <button id="delRow">最後の行削除</button>
  <button id="delCol">最後の列削除</button>
  <button id="downloadBtn">CSV ダウンロード</button>
  <br>
  検索: <input type="text" id="searchText" placeholder="検索文字">
  置換: <input type="text" id="replaceText" placeholder="置換文字">
  <button id="replaceBtn">置換実行</button>
  <button id="duplicateCell">選択セル複製</button>
  <button id="sortRow">選択列で行ソート</button>
</div>

<div id="tableContainer"></div>

<script>
let selectedCell = null;
let dragSrcCell = null;

// CSV → 配列
function csvToArray(csvStr, delimiter = ',') {
  return csvStr.split('\n').map(line => line.split(delimiter).map(item => item.replace(/^"(.*)"$/, '$1')));
}

// 配列 → CSV
function arrayToCSV(arr, delimiter = ',') {
  return arr.map(row => row.map(item => (typeof item==='string'&&(item.includes(delimiter)||item.includes('\n'))?`"${item}"`:item)).join(delimiter)).join('\n');
}

// 配列 → HTML テーブル表示
function renderTable(data) {
  const container = document.getElementById('tableContainer');
  const table = document.createElement('table');
  table.innerHTML='';
  
  data.forEach((row,i)=>{
    const tr = document.createElement('tr');
    row.forEach((cell,j)=>{
      const td = document.createElement(i===0?'th':'td');
      const input = document.createElement('input');
      input.value = cell;
      input.style.width='100%';
      td.appendChild(input);

      // セル選択
      td.addEventListener('click', (e)=>{
        if(!e.ctrlKey && selectedCell) selectedCell.classList.remove('selected');
        selectedCell = td;
        td.classList.add('selected');
      });

      // ドラッグ&ドロップ
      td.setAttribute('draggable', true);
      td.addEventListener('dragstart', e=>{
        dragSrcCell = td;
        e.dataTransfer.effectAllowed='move';
      });
      td.addEventListener('dragover', e=>{ e.preventDefault(); td.classList.add('drag-over'); });
      td.addEventListener('dragleave', e=>td.classList.remove('drag-over'));
      td.addEventListener('drop', e=>{
        e.preventDefault();
        td.classList.remove('drag-over');
        if(dragSrcCell && dragSrcCell!==td){
          const tmp = dragSrcCell.querySelector('input').value;
          dragSrcCell.querySelector('input').value = td.querySelector('input').value;
          td.querySelector('input').value = tmp;
        }
      });

      tr.appendChild(td);
    });
    table.appendChild(tr);
  });
  
  container.innerHTML='';
  container.appendChild(table);
}

// CSV 読み込み
document.getElementById('csvFile').addEventListener('change', e=>{
  const file = e.target.files[0];
  if(!file) return;
  const reader = new FileReader();
  reader.onload = evt=>{
    const delim = document.getElementById('delimiter').value;
    renderTable(csvToArray(evt.target.result, delim));
  };
  reader.readAsText(file,'UTF-8');
});

// 行追加
document.getElementById('addRow').addEventListener('click', ()=>{
  const table = document.querySelector('#tableContainer table');
  if(!table) return alert('テーブルがありません');
  const colCount = table.rows[0].cells.length;
  const tr = document.createElement('tr');
  for(let i=0;i<colCount;i++){
    const td = document.createElement('td');
    const input = document.createElement('input'); input.style.width='100%';
    td.appendChild(input);
    td.setAttribute('draggable', true);
    td.addEventListener('click', ()=>{ if(selectedCell) selectedCell.classList.remove('selected'); selectedCell=td; td.classList.add('selected'); });
    tr.appendChild(td);
  }
  table.appendChild(tr);
});

// 列追加
document.getElementById('addCol').addEventListener('click', ()=>{
  const table = document.querySelector('#tableContainer table');
  if(!table) return alert('テーブルがありません');
  Array.from(table.rows).forEach((tr,i)=>{
    const td = document.createElement(i===0?'th':'td');
    const input = document.createElement('input'); input.style.width='100%';
    td.appendChild(input);
    td.setAttribute('draggable', true);
    td.addEventListener('click', ()=>{ if(selectedCell) selectedCell.classList.remove('selected'); selectedCell=td; td.classList.add('selected'); });
    tr.appendChild(td);
  });
});

// 行削除
document.getElementById('delRow').addEventListener('click', ()=>{
  const table = document.querySelector('#tableContainer table');
  if(!table || table.rows.length===0) return;
  table.deleteRow(table.rows.length-1);
});

// 列削除
document.getElementById('delCol').addEventListener('click', ()=>{
  const table = document.querySelector('#tableContainer table');
  if(!table || table.rows[0].cells.length===0) return;
  Array.from(table.rows).forEach(tr=>tr.deleteCell(tr.cells.length-1));
});

// CSV ダウンロード
document.getElementById('downloadBtn').addEventListener('click', ()=>{
  const table = document.querySelector('#tableContainer table');
  if(!table) return alert('CSV を読み込んでください');
  const delim = document.getElementById('delimiter').value;
  const data = Array.from(table.rows).map(tr=>Array.from(tr.cells).map(td=>td.querySelector('input').value));
  const csvStr = arrayToCSV(data, delim);
  const blob = new Blob([csvStr], { type:'text/csv' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a'); a.href=url; a.download='edited.csv'; a.click(); URL.revokeObjectURL(url);
});

// 置換
document.getElementById('replaceBtn').addEventListener('click', ()=>{
  const search = document.getElementById('searchText').value;
  const replace = document.getElementById('replaceText').value;
  if(!search) return;
  const table = document.querySelector('#tableContainer table');
  Array.from(table.rows).forEach(tr=>Array.from(tr.cells).forEach(td=>{
    const input = td.querySelector('input');
    if(input.value.includes(search)) input.value = input.value.split(search).join(replace);
  }));
});

// セル複製
document.getElementById('duplicateCell').addEventListener('click', ()=>{
  if(!selectedCell) return alert('セルを選択してください');
  const table = document.querySelector('#tableContainer table');
  const rowIdx = Array.from(table.rows).indexOf(selectedCell.parentNode);
  const colIdx = Array.from(selectedCell.parentNode.children).indexOf(selectedCell);
  const value = selectedCell.querySelector('input').value;

  const newRow = table.insertRow(rowIdx+1);
  Array.from(table.rows[0].cells).forEach((_,j)=>{
    const td = newRow.insertCell(j);
    const input = document.createElement('input'); input.style.width='100%';
    input.value = (j===colIdx)?value:'';
    td.appendChild(input);
    td.setAttribute('draggable', true);
    td.addEventListener('click', ()=>{ if(selectedCell) selectedCell.classList.remove('selected'); selectedCell=td; td.classList.add('selected'); });
  });
});

// 選択列で行ソート
document.getElementById('sortRow').addEventListener('click', ()=>{
  if(!selectedCell) return alert('セルを選択してください');
  const colIdx = Array.from(selectedCell.parentNode.children).indexOf(selectedCell);
  const table = document.querySelector('#tableContainer table');
  const rows = Array.from(table.rows).slice(1);
  rows.sort((a,b)=>{
    const aVal = a.cells[colIdx].querySelector('input').value;
    const bVal = b.cells[colIdx].querySelector('input').value;
    return aVal.localeCompare(bVal, undefined, {numeric:true});
  });
  rows.forEach(r=>table.appendChild(r));
});
</script>

</body>
</html>
HTML

✅ 特徴

  • 完全版:CSV 読み込み・保存、行・列追加削除、セル複製、検索置換、ドラッグ移動、行ソート、区切り文字変更
  • セル複数選択も Ctrl クリックで可能(複雑な範
タイトルとURLをコピーしました