Html_Doc_convertor / index.html
Fanu2's picture
Updated design and layout
26da80c
<!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>