024
LVL 01 — WAVE DEFENDER SESSION 024 DAY 24

FILE UPLOAD

🎫 PIXELCRAFT-012
🐛 Bug | 🔵 Easy | Priority: 🔴 Critical

The Upload button logs "Upload clicked" but doesn't open a file selection dialog. Users can't load images.
CONCEPTS.UNLOCKED
📁
<input type="file">
The browser's built-in file selector. Usually hidden and triggered programmatically by a styled button. The accept attribute can restrict to specific file types.
📖
The FileReader API
Reading file contents in the browser: FileReader.readAsDataURL() converts a file to a Base64 string that can be loaded as an image source.
🖼
Image() Object
new Image() creates an image element in memory. Set its .src to load data. When onload fires, the image is ready to draw to the canvas.
Asynchronous Operations
File reading doesn't happen instantly. Callbacks let you say "when the file is ready, run this function." The code doesn't wait — it continues and the callback fires later.
🔗
The Upload Pipeline
Click → file dialog → select → read → decode → draw to canvas. Each step depends on the previous one completing. A chain of asynchronous operations.
Loading Indicators
Users need feedback during async operations. While a large file reads, show a spinner or progress bar. Without feedback, users think the app is broken.
HANDS-ON.TASKS
01
Trigger the Hidden File Input

The <input type="file"> exists but is hidden. The Upload button needs to trigger it:

const fileInput = document.getElementById('file-input'); uploadBtn.addEventListener('click', () => { fileInput.click(); // Programmatically open file dialog });
Browsers don't let you style <input type="file"> well, so the standard pattern is: hide it, create a custom button, call .click() on the hidden input when the button is pressed.
02
Read the Selected File
fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; if (!file) return; // Validate (from Session 19) if (!validTypes.includes(file.type)) { showToast('Invalid file type', 'error'); return; } const reader = new FileReader(); reader.onload = function(e) { console.log("File read complete!"); // e.target.result contains the image data loadImageToCanvas(e.target.result); }; reader.onerror = function() { showToast('Failed to read file', 'error'); }; reader.readAsDataURL(file); });
reader.readAsDataURL(file) starts reading but doesn't wait — it returns immediately. When the file is ready (maybe 50ms later), the onload callback fires. This is asynchronous programming.
03
Load onto Canvas
function loadImageToCanvas(dataURL) { const img = new Image(); img.onload = function() { canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); showToast('Image loaded!', 'success'); }; img.src = dataURL; }

Another async operation: setting img.src starts loading, then onload fires when the image is decoded. Two layers of async chained together.

04
Test All File Types
FileExpected Result
.jpg photoLoads and displays ✅
.png with transparencyLoads and displays ✅
.webp imageLoads and displays ✅
.txt fileToast: "Invalid file type" 🔴
Very large file (50MB+)Toast: "File too large" 🔴
05
Add a Loading Indicator

Show the spinner from Session 17 while the file is being read. Hide it when the image loads.

Large images can take noticeable time to read and decode. Without a loading indicator, users click Upload and see nothing happen for 1–2 seconds. They think it's broken.
06
Close the Ticket
git switch -c bugfix/PIXELCRAFT-012-file-upload git add src/scripts/ src/index.html git commit -m "Fix upload button and implement file reading pipeline (PIXELCRAFT-012)" git push origin bugfix/PIXELCRAFT-012-file-upload # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

Asynchronous programming.

FileReader.readAsDataURL() starts reading but doesn't wait — it returns immediately. When the file is ready, the onload callback fires.

// Why async? Because reading a file from
// disk takes time, and JavaScript's single
// thread would be BLOCKED (frozen UI) if
// it waited.

// The model:
1. Start operation             → non-blocking
2. Continue running other code  → UI stays responsive
3. Callback fires when done     → process result

// This "start → continue → callback" model
// is the foundation of all modern web
// programming. You'll go much deeper in
// async in upcoming sessions.
"Upload UX"
[A] Add drag-and-drop upload: listen for 'dragover' and 'drop' events on the canvas area. Display a visual drop zone overlay when dragging a file over.
[B] Show image metadata after upload: filename, dimensions, file size, MIME type. Display it in a small info bar below the toolbar.
[C] Add paste support: listen for Ctrl+V → read image from clipboard → load to canvas. Test by taking a screenshot and pasting it.
REF.MATERIAL
ARTICLE
Mozilla Developer Network
Complete reference for FileReader: readAsDataURL, readAsArrayBuffer, readAsText, and all events (onload, onerror, onprogress).
FILE APIMDNREFERENCE
VIDEO
Traversy Media
Full walkthrough: file input, FileReader, preview, validation, and drag-and-drop — everything for building an upload feature.
UPLOADFILEREADER
ARTICLE
Mozilla Developer Network
How to draw images to canvas: drawImage() variants, scaling, cropping, and working with Image objects.
CANVASIMAGES
VIDEO
Web Dev Simplified
What callbacks are, why they exist, and how asynchronous patterns in JavaScript work. Foundation for Promises and async/await later.
CALLBACKSASYNC
ARTICLE
javascript.info
Asynchronous patterns explained from first principles: why callbacks exist, callback hell, and the path toward Promises.
CALLBACKSINTERACTIVE
// LEAVE EXCITED BECAUSE
Users can upload images! Click Upload → pick file → image appears on canvas → apply filter → see result. PixelCraft has a working edit pipeline.