| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>HTML Table to Word Converter</title> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| :root { |
| --primary: #6366f1; |
| --primary-dark: #4f46e5; |
| --bg: #f9fafb; |
| --surface: #ffffff; |
| --text: #111827; |
| --muted: #6b7280; |
| --border: #e5e7eb; |
| --error: #ef4444; |
| --success: #10b981; |
| --radius: 12px; |
| --shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); |
| } |
| |
| * { |
| box-sizing: border-box; |
| } |
| |
| body { |
| margin: 0; |
| font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; |
| background: var(--bg); |
| color: var(--text); |
| line-height: 1.6; |
| display: flex; |
| justify-content: center; |
| padding: 2rem 1rem; |
| } |
| |
| .app { |
| width: 100%; |
| max-width: 640px; |
| } |
| |
| header { |
| text-align: center; |
| margin-bottom: 2rem; |
| } |
| |
| h1 { |
| font-size: 2.25rem; |
| font-weight: 800; |
| margin: 0; |
| } |
| |
| .card { |
| background: var(--surface); |
| border-radius: var(--radius); |
| box-shadow: var(--shadow); |
| padding: 2rem; |
| } |
| |
| .upload-zone { |
| border: 2px dashed var(--border); |
| border-radius: var(--radius); |
| padding: 3rem 2rem; |
| text-align: center; |
| transition: border-color 0.3s, background-color 0.3s; |
| cursor: pointer; |
| } |
| |
| .upload-zone:hover { |
| border-color: var(--primary); |
| background-color: #f3f4ff; |
| } |
| |
| .upload-zone.dragover { |
| border-color: var(--primary); |
| background-color: #e0e7ff; |
| } |
| |
| .upload-icon { |
| font-size: 3rem; |
| color: var(--muted); |
| margin-bottom: 0.5rem; |
| } |
| |
| .upload-text { |
| font-size: 1.125rem; |
| font-weight: 600; |
| margin: 0; |
| } |
| |
| .upload-hint { |
| color: var(--muted); |
| margin-top: 0.5rem; |
| } |
| |
| .file-input { |
| display: none; |
| } |
| |
| .success { |
| color: var(--success); |
| font-weight: 600; |
| } |
| |
| .error { |
| color: var(--error); |
| font-weight: 600; |
| } |
| |
| .info { |
| color: var(--muted); |
| margin-top: 0.5rem; |
| } |
| |
| .controls { |
| margin-top: 2rem; |
| display: flex; |
| flex-direction: column; |
| gap: 1rem; |
| } |
| |
| label { |
| font-weight: 600; |
| margin-bottom: 0.5rem; |
| display: block; |
| } |
| |
| input[type="number"] { |
| width: 100%; |
| padding: 0.75rem; |
| border: 1px solid var(--border); |
| border-radius: 8px; |
| font-size: 1rem; |
| } |
| |
| input[type="number"]:focus { |
| border-color: var(--primary); |
| outline: none; |
| box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2); |
| } |
| |
| .preview { |
| overflow-x: auto; |
| margin-top: 2rem; |
| } |
| |
| table { |
| width: 100%; |
| border-collapse: collapse; |
| font-size: 0.875rem; |
| } |
| |
| th, td { |
| padding: 0.75rem; |
| border-bottom: 1px solid var(--border); |
| text-align: left; |
| } |
| |
| th { |
| background-color: #f3f4f6; |
| font-weight: 600; |
| } |
| |
| .btn { |
| background: var(--primary); |
| color: #fff; |
| border: none; |
| padding: 0.75rem 1.5rem; |
| border-radius: 8px; |
| font-size: 1rem; |
| font-weight: 600; |
| cursor: pointer; |
| transition: background-color 0.3s; |
| display: inline-flex; |
| align-items: center; |
| gap: 0.5rem; |
| } |
| |
| .btn:hover { |
| background: var(--primary-dark); |
| } |
| |
| .status { |
| margin-top: 1rem; |
| padding: 1rem; |
| border-radius: var(--radius); |
| } |
| |
| .status.success { |
| background: #ecfdf5; |
| color: var(--success); |
| } |
| |
| .status.error { |
| background: #fef2f2; |
| color: var(--error); |
| } |
| |
| .status.info { |
| background: #f3f4f6; |
| color: var(--muted); |
| } |
| |
| @media (max-width: 600px) { |
| h1 { |
| font-size: 1.75rem; |
| } |
| |
| .card { |
| padding: 1.5rem; |
| } |
| |
| .upload-zone { |
| padding: 2rem 1rem; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="app"> |
| <header> |
| <h1>HTML Table to Word Converter</h1> |
| </header> |
|
|
| <div class="card"> |
| <form id="uploadForm"> |
| <div class="upload-zone" id="dropZone"> |
| <i class="fas fa-cloud-upload-alt upload-icon"></i> |
| <p class="upload-text">Upload HTML File</p> |
| <p class="upload-hint">Drag & drop or click to select</p> |
| <input type="file" id="fileInput" class="file-input" accept=".html,.htm" /> |
| </div> |
|
|
| <div id="status" class="status" style="display: none;"></div> |
|
|
| <div id="controls" class="controls" style="display: none;"> |
| <div> |
| <label for="skipRows">Rows to skip (from top)</label> |
| <input type="number" id="skipRows" min="0" value="1" /> |
| </div> |
| <div> |
| <label for="numColumns">Number of columns to import</label> |
| <input type="number" id="numColumns" min="1" max="50" value="8" /> |
| </div> |
| </div> |
|
|
| <div id="preview" class="preview" style="display: none;"> |
| <h2>Extracted Table Preview</h2> |
| <table id="previewTable"></table> |
| </div> |
|
|
| <button type="button" id="downloadBtn" class="btn" style="display: none;"> |
| <i class="fas fa-download"></i>Download as Word (.docx) |
| </button> |
| </form> |
| </div> |
| </div> |
|
|
| <script> |
| const fileInput = document.getElementById('fileInput'); |
| const dropZone = document.getElementById('dropZone'); |
| const statusDiv = document.getElementById('status'); |
| const controlsDiv = document.getElementById('controls'); |
| const previewDiv = document.getElementById('preview'); |
| const previewTable = document.getElementById('previewTable'); |
| const downloadBtn = document.getElementById('downloadBtn'); |
| const skipRowsInput = document.getElementById('skipRows'); |
| const numColumnsInput = document.getElementById('numColumns'); |
| |
| let extractedTable = null; |
| |
| dropZone.addEventListener('click', () => fileInput.click()); |
| |
| dropZone.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| dropZone.classList.add('dragover'); |
| }); |
| |
| dropZone.addEventListener('dragleave', () => { |
| dropZone.classList.remove('dragover'); |
| }); |
| |
| dropZone.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| dropZone.classList.remove('dragover'); |
| const files = e.dataTransfer.files; |
| if (files.length) { |
| handleFile(files[0]); |
| } |
| }); |
| |
| fileInput.addEventListener('change', () => { |
| const file = fileInput.files[0]; |
| if (file) { |
| handleFile(file); |
| } |
| }); |
| |
| function handleFile(file) { |
| const reader = new FileReader(); |
| reader.onload = (e) => { |
| try { |
| const html = e.target.result; |
| const tables = extractAllTables(html); |
| const validTables = tables.filter(t => t && t.length && t[0].length >= 5); |
| if (!validTables.length) { |
| showStatus("No valid tables found in the uploaded HTML file.", 'error'); |
| return; |
| } |
| |
| const largest = validTables.reduce((max, cur) => (cur.length > max.length ? cur : max), validTables[0]); |
| extractedTable = largest; |
| |
| const maxCols = largest[0].length; |
| const maxRows = largest.length; |
| |
| showStatus(`Found ${validTables.length} table(s). Using the largest one with ${maxRows} rows and ${maxCols} columns.`, 'success'); |
| |
| skipRowsInput.max = Math.max(maxRows - 1, 0); |
| skipRowsInput.value = maxRows <= 1 ? 0 : 1; |
| |
| numColumnsInput.max = maxCols; |
| numColumnsInput.value = Math.min(8, maxCols); |
| |
| controlsDiv.style.display = 'block'; |
| previewTable.innerHTML = ''; |
| updatePreview(); |
| } catch (err) { |
| showStatus("An error occurred while processing the file.", 'error'); |
| console.error(err); |
| } |
| }; |
| reader.readAsText(file); |
| } |
| |
| function extractAllTables(html) { |
| const parser = new DOMParser(); |
| const doc = parser.parseFromString(html, 'text/html'); |
| const tables = doc.querySelectorAll('table'); |
| return Array.from(tables).map(table => { |
| const rows = Array.from(table.querySelectorAll('tr')); |
| return rows.map(row => { |
| const cells = Array.from(row.querySelectorAll('td, th')); |
| return cells.map(cell => cell.textContent.trim()); |
| }); |
| }); |
| } |
| |
| function updatePreview() { |
| if (!extractedTable) return; |
| const skip = parseInt(skipRowsInput.value, 10); |
| const cols = parseInt(numColumnsInput.value, 10); |
| const sliced = extractedTable.slice(skip).map(row => row.slice(0, cols)); |
| |
| previewTable.innerHTML = ''; |
| if (sliced.length > 0) { |
| const thead = document.createElement('thead'); |
| const tr = document.createElement('tr'); |
| sliced[0].forEach(cell => { |
| const th = document.createElement('th'); |
| th.textContent = cell; |
| tr.appendChild(th); |
| }); |
| thead.appendChild(tr); |
| previewTable.appendChild(thead); |
| |
| const tbody = document.createElement('tbody'); |
| for (let i = 1; i < sliced.length; i++) { |
| if (sliced[i].every(c => !c)) continue; |
| const tr = document.createElement('tr'); |
| sliced[i].forEach(cell => { |
| const td = document.createElement('td'); |
| td.textContent = cell; |
| tr.appendChild(td); |
| }); |
| tbody.appendChild(tr); |
| } |
| previewTable.appendChild(tbody); |
| } |
| |
| previewDiv.style.display = 'block'; |
| downloadBtn.style.display = 'inline-flex'; |
| } |
| |
| skipRowsInput.addEventListener('input', updatePreview); |
| numColumnsInput.addEventListener('input', updatePreview); |
| |
| downloadBtn.addEventListener('click', () => { |
| if (!extractedTable) return; |
| const skip = parseInt(skipRowsInput.value, 10); |
| const cols = parseInt(numColumnsInput.value, 10); |
| const sliced = extractedTable.slice(skip).map(row => row.slice(0, cols)); |
| |
| downloadTableAsDocx(sliced, 'extracted_table.docx'); |
| }); |
| |
| function downloadTableAsDocx(table, filename) { |
| let html = '<html><head><meta charset="UTF-8"></head><body><table>'; |
| table.forEach(row => { |
| html += '<tr>'; |
| row.forEach(cell => { |
| html += `<td>${cell}</td>`; |
| }); |
| html += '</tr>'; |
| }); |
| html += '</table></body></html>'; |
| |
| const blob = new Blob([html], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }); |
| const url = URL.createObjectURL(blob); |
| const a = document.createElement('a'); |
| a.href = url; |
| a.download = filename; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| URL.revokeObjectURL(url); |
| } |
| |
| function showStatus(message, type) { |
| statusDiv.textContent = message; |
| statusDiv.className = `status ${type}`; |
| statusDiv.style.display = 'block'; |
| } |
| </script> |
| </body> |
| </html> |
|
|