Science Experiment Tracker Generator

Science Experiment Tracker Generator

Science Experiment Tracker

1. Hypothesis & Variables

2. Procedure & Materials

Procedure and Materials List

    Add your procedure steps and materials above.

3. Data Observation Log

Date Trial / Condition Observation / Result Action
Logged data will appear here.

4. Conclusion & Reflection

Add your procedure steps and materials above.

'; return; } let procedureHtml = ''; let materialHtml = ''; procedureMaterials.forEach((item, index) => { const contentHtml = `
${item.type === 'Procedure' ? `${index + 1}.` : 'Item:'} ${item.content}
`; if (item.type === 'Procedure') { procedureHtml += `
  • ${contentHtml}
  • `; } else { materialHtml += `
  • ${contentHtml}
  • `; } }); itemList.innerHTML = `

    Procedure:

    ${procedureHtml || '

    None defined.

    '}

    Materials:

    ${materialHtml || '

    None defined.

    '} `; } function addProcedureMaterial() { const content = stepInput.value.trim(); const type = itemTypeSelect.value; if (!content) { alert("Please enter content for the step or material."); return; } procedureMaterials.push({ id: generateId(), content, type }); renderProcedureMaterials(); stepInput.value = ''; } function removeProcedureMaterial(id) { procedureMaterials = procedureMaterials.filter(item => item.id !== id); renderProcedureMaterials(); } // --- Data Log Management --- function renderObservationLog() { logTableBody.innerHTML = ''; if (observationLog.length === 0) { logTableBody.innerHTML = 'Logged data will appear here.'; return; } // Sort by date ascending observationLog.sort((a, b) => new Date(a.date) - new Date(b.date)); observationLog.forEach((log) => { const row = document.createElement('tr'); row.innerHTML = ` ${log.date} ${log.trial} ${log.observation} `; logTableBody.appendChild(row); }); } function addLog() { const date = logDateInput.value.trim(); const trial = logTrialInput.value.trim(); const observation = logObservationInput.value.trim(); if (!date || !trial || !observation) { alert("Please fill in Date, Trial/Condition, and Observation/Result."); return; } observationLog.push({ id: generateId(), date, trial, observation }); renderObservationLog(); logTrialInput.value = ''; logObservationInput.value = ''; } function removeLog(id) { observationLog = observationLog.filter(log => log.id !== id); renderObservationLog(); } // --- PDF Generation (Ensured functionality) --- function downloadPDF() { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library not loaded.'); return; } if (typeof window.jspdf.jsPDF.autoTable === 'undefined') { alert('Error: jsPDF-AutoTable library not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('portrait', 'pt', 'a4'); const margin = 40; const pageWidth = doc.internal.pageSize.getWidth(); let currentY = margin; const experimentTitle = experimentTitleInput.value || 'Untitled Science Experiment'; // --- Helper function to add structured text sections --- function addTextSection(title, content, startY) { const estimatedHeight = 15 + (content.split('\n').length * 12) + 10; if (startY + estimatedHeight > doc.internal.pageSize.getHeight() - margin) { doc.addPage(); startY = margin; } doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.setTextColor(124, 58, 237); // violet-600 doc.text(title, margin, startY); doc.setFontSize(10); doc.setFont('helvetica', 'normal'); doc.setTextColor(51, 65, 85); const lines = doc.splitTextToSize(content || 'N/A', pageWidth - margin * 2); doc.text(lines, margin, startY + 15); return startY + 15 + lines.length * 12 + 10; } // --- Header --- doc.setFontSize(20); doc.setFont('helvetica', 'bold'); doc.setTextColor(126, 34, 206); doc.text("Science Experiment Lab Report", pageWidth / 2, currentY, { align: 'center' }); currentY += 15; doc.setFontSize(14); doc.text(experimentTitle, pageWidth / 2, currentY, { align: 'center' }); currentY += 30; // --- Section 1: Hypothesis & Variables --- currentY = addTextSection("1. Hypothesis & Variables", currentY); const varData = [ ["Hypothesis:", hypothesisTextarea.value || 'N/A'], ["Independent Variable:", independentVarInput.value || 'N/A'], ["Dependent Variable:", dependentVarInput.value || 'N/A'], ]; doc.autoTable({ startY: currentY, body: varData, theme: 'striped', styles: { cellPadding: 3, fontSize: 10 }, columnStyles: { 0: { fontStyle: 'bold', fillColor: [245, 243, 255] } } }); currentY = doc.autoTable.previous.finalY + 15; // --- Section 2: Procedure & Materials --- currentY = addTextSection("2. Procedure & Materials", currentY); const procedureData = procedureMaterials.filter(item => item.type === 'Procedure').map((item, index) => [index + 1, item.content]); const materialData = procedureMaterials.filter(item => item.type === 'Material').map(item => [item.content]); // Procedure Table if (procedureData.length > 0) { doc.setFontSize(10); doc.setFont('helvetica', 'bold'); doc.text("Procedure Steps:", margin, currentY); currentY += 12; doc.autoTable({ startY: currentY, head: [['#', 'Action Step']], body: procedureData, theme: 'grid', headStyles: { fillColor: [147, 51, 234] }, styles: { fontSize: 9, minCellHeight: 12 }, columnStyles: { 0: { cellWidth: 30, halign: 'center' } } }); currentY = doc.autoTable.previous.finalY + 10; } // Materials Table if (materialData.length > 0) { doc.setFontSize(10); doc.setFont('helvetica', 'bold'); doc.text("Materials Used:", margin, currentY); currentY += 12; doc.autoTable({ startY: currentY, head: [['Material / Equipment']], body: materialData, theme: 'grid', styles: { fontSize: 9, minCellHeight: 12 }, margin: { left: margin, right: pageWidth / 2 } }); currentY = doc.autoTable.previous.finalY + 15; } // --- Section 3: Data Log --- currentY = addTextSection("3. Data Observation Log", currentY); if (observationLog.length > 0) { const logData = observationLog.map(log => [log.date, log.trial, log.observation]); doc.autoTable({ startY: currentY, head: [['Date', 'Trial / Condition', 'Observation / Result']], body: logData, theme: 'grid', headStyles: { fillColor: [147, 51, 234] }, styles: { fontSize: 9, minCellHeight: 15, overflow: 'linebreak' }, margin: { left: margin, right: margin } }); currentY = doc.autoTable.previous.finalY + 15; } else { doc.setFontSize(10); doc.text("No data was logged for this experiment.", margin, currentY); currentY += 20; } // --- Section 4: Conclusion --- currentY = addTextSection("4. Conclusion & Reflection", conclusionTextarea.value, currentY); doc.save('science-lab-report.pdf'); } // --- Event Listeners and Initial Load --- addItemBtn.addEventListener('click', addProcedureMaterial); stepInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { e.preventDefault(); addProcedureMaterial(); } }); logTableBody.addEventListener('click', (e) => { if (e.target.classList.contains('setg-btn-red')) { removeLog(e.target.dataset.id); } }); addLogBtn.addEventListener('click', addLog); // Set current date for log logDateInput.value = new Date().toISOString().split('T')[0]; pdfBtn.addEventListener('click', downloadPDF); // Initial load renderProcedureMaterials(); renderObservationLog(); });
    Scroll to Top