Build a Parcel Data Structures Lab
Build a browser app that demonstrates queues, stacks, and Map lookups.
Introduction
30 Second Summary
At a parcel desk, every package needs a clear place in the flow. Without that order, waiting items become difficult to process or find.
In this project, you will build a local parcel simulator with HTML, CSS, and JavaScript. Every button reveals how a different data structure organizes the same parcel records.
What You'll Build
You'll demo a browser parcel desk where every click visibly changes a waiting parcel, a processed parcel, or a tracking result.
By the end of this project, you'll have:
- An array-backed arrival view that keeps generated parcel records in order on screen.
- A FIFO queue that processes the earliest waiting parcel first. A LIFO stack that restores the most recently processed parcel first.
- A keyed Map lookup that retrieves a parcel's recipient and current status from its tracking ID.
- Secret Mission: Split incoming work into urgent and standard FIFO lanes while preserving arrival order inside each lane.
Are there any prerequisites?
No prior coding experience is required.
The first step helps you verify Visual Studio Code and Microsoft Edge on your Windows computer.
Before We Start
Before the hands-on work begins, lock in what you are building and why each operation matters. You will create a local parcel desk that makes ordered storage, FIFO processing, LIFO undo, and keyed tracking-ID lookup visible.
Verify Tools and Create the App Shell
Setup problems can make a working page look broken. Confirming the editor and browser first gives you a reliable way to separate tool issues from code issues.
In this step, you will use Visual Studio Code to create a local HTML file. You will open that file in Microsoft Edge to confirm the app shell works before adding parcel behavior.
In this step, get ready to:
- Confirm Visual Studio Code is available.
- Confirm Microsoft Edge is available.
- Build a styled app shell that opens in Microsoft Edge.
Verify Visual Studio Code
Visual Studio Code gives you one place to manage the project folder and edit its files. A quick launch check confirms the editor is ready before you create anything.
- Press the Windows key to open Windows search.
- Type Visual Studio Code into the search field.
- Press Enter to launch the application.
✔️ Visual Studio Code opens
That confirms your editor is ready. Keep Visual Studio Code open because you will create the project inside it shortly.
ⓧ Visual Studio Code is missing
The Windows User setup installs Visual Studio Code for your Windows account. It does not require administrator permissions.
- Visit the official Visual Studio Code Windows setup page.
- Download the User setup installer for Windows.
- Run the downloaded installer.
- Complete the installer using its default options.
- Press the Windows key after installation.
- Type Visual Studio Code into the search field.
- Press Enter to launch the application.
Visual Studio Code still missing?
Confirm that the installer finished before searching again. If installation failed, download a fresh copy from the official setup page.
Still stuck? Help me troubleshoot my Visual Studio Code installation on Windows.
Verify Microsoft Edge
The browser turns your local file into the visible parcel desk. Confirming Microsoft Edge now gives you a consistent place to check every change you make.
- Press the Windows key to open Windows search.
- Type Microsoft Edge into the search field.
- Press Enter to launch the browser.
✔️ Microsoft Edge opens
Your browser is ready to display the local app. Leave Microsoft Edge open so you can load the finished file later in this step.
ⓧ Microsoft Edge is missing
- Visit the official Microsoft Edge download page.
- Download Microsoft Edge for Windows.
- Run the downloaded installer.
- Complete the installation prompts.
- Press the Windows key after installation.
- Type Microsoft Edge into the search field.
- Press Enter to launch the browser.
Microsoft Edge still missing?
Confirm that the Edge installer completed successfully. Download a fresh installer from Microsoft if the first installation stopped early.
Need another pair of eyes? Help me troubleshoot my Microsoft Edge installation on Windows.
Create and launch the app shell
A static page can combine HTML, CSS and JavaScript without a package manager or local server. You will build the visible shell first so every later data-structure change has somewhere to appear.
- Switch back to Visual Studio Code.
- Select File from the top menu.
- Select Open Folder....
- Select your Desktop in the folder dialog.
- Select New Folder.
- Enter parcel-structures as the folder name.
- Select Select Folder to load the folder into Visual Studio Code.
You should see parcel-structures at the top of the Explorer view. This confirms that Visual Studio Code is working inside the correct folder.
- Select the New File... button in the Explorer view.
- Enter index.html as the file name.
- Press Enter to create the file.
- Create the document frame and page header by pasting this code into index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Parcel Data Structures Lab</title>
<style>
</style>
</head>
<body>
<main>
<header>
<h1>Parcel Data Structures Lab</h1>
<p>Add parcels, process the queue, undo with a stack, and search by tracking ID.</p>
</header>
</main>
<script>
</script>
</body>
</html>
What does this document frame do?
- The document metadata gives the browser the page title and responsive viewport settings.
- The header introduces the parcel desk and summarizes the operations it will support.
- The empty style and script areas reserve clear locations for the page design and later JavaScript behavior.
- Save index.html by pressing Ctrl+S.
- Switch back to Microsoft Edge.
- Press Ctrl+O to open a file from your computer.
- Choose Desktop\parcel-structures\index.html in the file dialog.
- Select Open.
You should see the Parcel Data Structures Lab heading with its short description. That first local page is working.
Page blank or file missing?
- Confirm that the Explorer view shows index.html inside parcel-structures.
- Save the file again if its editor tab shows an unsaved-change indicator.
- Still stuck? Help me open my local index.html file in Microsoft Edge.
The heading proves that the browser can read your file. Next, the controls give each future data-structure operation a visible trigger.
- Return to index.html in Visual Studio Code.
- Find the closing </header> tag.
- Add the control cards directly below that tag by pasting:
<section class="controls">
<div class="card">
<h2>Add a parcel</h2>
<div class="row">
<input id="recipient-input" type="text" placeholder="Recipient name">
<button id="add-button" type="button">Add Parcel</button>
</div>
</div>
<div class="card">
<h2>Move parcel records</h2>
<div class="row">
<button id="process-button" type="button">Process Next</button>
<button id="undo-button" class="secondary" type="button">Undo Last</button>
</div>
</div>
<div class="card">
<h2>Find by tracking ID</h2>
<div class="row">
<input id="tracking-input" type="text" placeholder="Example: PKG-1">
<button id="find-button" type="button">Find Parcel</button>
</div>
</div>
</section>
How are the controls organized?
- The recipient control will create parcel records through recipient-input and add-button.
- The process controls reserve separate actions for FIFO processing and LIFO undo.
- The tracking control will use tracking-input and find-button for direct lookup.
- Save index.html by pressing Ctrl+S.
- Reload the local page in Microsoft Edge.
You should see recipient, process, undo and lookup controls beneath the heading. The buttons are ready for the JavaScript you will add in later steps.
Controls not appearing?
- Check that the controls section sits between </header> and </main>.
- Confirm that every opening <div> has a matching closing </div>.
- Need help? Help me find why my parcel controls are missing.
The output panels make each structure observable. They will show where parcels are waiting, which parcels were processed and what a tracking lookup returns.
- Find the closing </section> tag for the controls section.
- Add the output panels and status area directly below that tag by pasting:
<section class="grid">
<article class="card">
<h2>Waiting Queue</h2>
<pre id="queue-output"></pre>
</article>
<article class="card">
<h2>Processed Stack</h2>
<pre id="stack-output"></pre>
</article>
<article class="card">
<h2>Lookup Result</h2>
<pre id="lookup-output">No lookup yet.</pre>
</article>
<article class="card">
<h2>Structures in Use</h2>
<pre>Array: stores records
Queue: earliest waiting parcel first
Stack: latest processed parcel first
Map: tracking ID to parcel record</pre>
</article>
</section>
<p id="status" aria-live="polite">App ready. Add a parcel to begin.</p>
What will these outputs reveal?
- The queue-output panel will expose the earliest waiting parcel.
- The stack-output panel will expose the latest processed parcel.
- The lookup panel and status area provide immediate feedback after each future operation.
- Save index.html by pressing Ctrl+S.
- Reload the local page in Microsoft Edge.
You should see four output panels below the controls. The page now exposes every area needed by the parcel simulator.
Panels missing or nested incorrectly?
- Place the grid section after the complete controls section.
- Keep the status paragraph inside <main>.
- Need help? Help me fix the output-panel structure in index.html.
The structure works, but styling makes its groups easier to scan. This first CSS layer sets the page background and turns the header plus cards into clear surfaces.
- Find the empty <style> element near the top of index.html.
- Add the base page and card styles inside it by pasting:
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f7fb;
color: #172033;
}
main {
width: min(960px, 92%);
margin: 32px auto;
}
header, .card {
background: white;
border: 1px solid #dce3ee;
border-radius: 14px;
padding: 20px;
box-shadow: 0 8px 24px rgba(23, 32, 51, 0.06);
}
header { margin-bottom: 18px; }
h1, h2 { margin-top: 0; }
h2 { font-size: 1rem; }
What does this styling change?
- The body rules establish the page colors and readable default font.
- The main element keeps the app centered within the browser window.
- The shared header and card rules create consistent containers for every part of the simulator.
- Save index.html by pressing Ctrl+S.
- Reload the local page in Microsoft Edge.
You should see a pale background with white rounded cards. The parcel desk now has a clear visual hierarchy.
Page still looks unstyled?
- Confirm that the CSS sits between <style> and </style>.
- Check that every CSS rule has matching braces.
- Need help? Help me find why my base CSS is not applying.
The next style layer arranges related controls into rows and responsive columns. It also gives primary and secondary actions distinct colors.
- Place the layout and control styles below the existing h2 rule by pasting:
.controls, .grid {
display: grid;
gap: 14px;
}
.controls {
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
margin-bottom: 18px;
}
.grid {
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.row { display: flex; gap: 8px; flex-wrap: wrap; }
input, button {
font: inherit;
border-radius: 9px;
padding: 10px 12px;
}
input {
min-width: 0;
flex: 1;
border: 1px solid #aeb9ca;
}
button {
border: 0;
background: #315efb;
color: white;
cursor: pointer;
}
button.secondary { background: #526174; }
How does the layout adapt?
- The grid rules create as many columns as the browser width can comfortably hold.
- The row rule keeps related inputs and buttons together.
- The shared form rules make every control consistent without repeating styles.
- Save index.html by pressing Ctrl+S.
- Reload the local page in Microsoft Edge.
You should see the controls arranged across responsive cards. The action buttons should appear blue or gray.
Controls still stacked awkwardly?
- Confirm that the selectors begin with .controls and .grid.
- Check that the control section still uses class="controls".
- Need help? Help me troubleshoot the control layout in my parcel app.
The final styles turn raw output text into readable data panels. The status area gets a colored edge so operation feedback remains easy to find.
- Place the output and status styles below the secondary button rule by pasting:
pre {
min-height: 96px;
margin: 0;
padding: 12px;
border-radius: 9px;
background: #eef3fb;
white-space: pre-wrap;
font-family: Consolas, monospace;
}
#status {
margin-top: 18px;
padding: 12px;
border-left: 4px solid #315efb;
background: white;
}
Why use preformatted outputs?
- The preformatted panels preserve spaces and line breaks used by future queue and stack markers.
- The minimum height keeps empty structures visible before any parcel records exist.
- The status styling separates operation feedback from stored parcel data.
- Save index.html by pressing Ctrl+S.
- Reload the local page in Microsoft Edge.
You should see pale output areas inside the four cards. You should also see the status message beneath the grid.
Output areas still look like plain text?
- Confirm that the output elements use <pre> tags.
- Check that the status selector includes the leading # before status.
- Need help? Help me fix the output-panel and status styles.
✔️ Awesome, I've got everything!
Great. Keep index.html saved in the parcel-structures folder.
ⓧ I'd like to double check the full code
- Compare your complete index.html file with this reference:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Parcel Data Structures Lab</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f7fb;
color: #172033;
}
main {
width: min(960px, 92%);
margin: 32px auto;
}
header, .card {
background: white;
border: 1px solid #dce3ee;
border-radius: 14px;
padding: 20px;
box-shadow: 0 8px 24px rgba(23, 32, 51, 0.06);
}
header { margin-bottom: 18px; }
h1, h2 { margin-top: 0; }
h2 { font-size: 1rem; }
.controls, .grid {
display: grid;
gap: 14px;
}
.controls {
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
margin-bottom: 18px;
}
.grid {
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.row { display: flex; gap: 8px; flex-wrap: wrap; }
input, button {
font: inherit;
border-radius: 9px;
padding: 10px 12px;
}
input {
min-width: 0;
flex: 1;
border: 1px solid #aeb9ca;
}
button {
border: 0;
background: #315efb;
color: white;
cursor: pointer;
}
button.secondary { background: #526174; }
pre {
min-height: 96px;
margin: 0;
padding: 12px;
border-radius: 9px;
background: #eef3fb;
white-space: pre-wrap;
font-family: Consolas, monospace;
}
#status {
margin-top: 18px;
padding: 12px;
border-left: 4px solid #315efb;
background: white;
}
</style>
</head>
<body>
<main>
<header>
<h1>Parcel Data Structures Lab</h1>
<p>Add parcels, process the queue, undo with a stack, and search by tracking ID.</p>
</header>
<section class="controls">
<div class="card">
<h2>Add a parcel</h2>
<div class="row">
<input id="recipient-input" type="text" placeholder="Recipient name">
<button id="add-button" type="button">Add Parcel</button>
</div>
</div>
<div class="card">
<h2>Move parcel records</h2>
<div class="row">
<button id="process-button" type="button">Process Next</button>
<button id="undo-button" class="secondary" type="button">Undo Last</button>
</div>
</div>
<div class="card">
<h2>Find by tracking ID</h2>
<div class="row">
<input id="tracking-input" type="text" placeholder="Example: PKG-1">
<button id="find-button" type="button">Find Parcel</button>
</div>
</div>
</section>
<section class="grid">
<article class="card">
<h2>Waiting Queue</h2>
<pre id="queue-output"></pre>
</article>
<article class="card">
<h2>Processed Stack</h2>
<pre id="stack-output"></pre>
</article>
<article class="card">
<h2>Lookup Result</h2>
<pre id="lookup-output">No lookup yet.</pre>
</article>
<article class="card">
<h2>Structures in Use</h2>
<pre>Array: stores records
Queue: earliest waiting parcel first
Stack: latest processed parcel first
Map: tracking ID to parcel record</pre>
</article>
</section>
<p id="status" aria-live="polite">App ready. Add a parcel to begin.</p>
</main>
<script>
</script>
</body>
</html>
Before the final check, which visible areas do you expect the browser to show from the code you just assembled?
- Reload index.html in Microsoft Edge.
You should see the “Parcel Data Structures Lab” heading, all four controls, four output panels and the app-status area. The buttons do not move parcel data yet because their JavaScript behavior belongs to the next steps.
Your local parcel desk now has a working visual shell. Next, you will store the first parcel records in an ordered array and render them on this page.
Store Parcel Records in an Array
Your app shell displays every control that a parcel desk needs. However, the page cannot remember the parcel records that arrive.
In this step, you will add JavaScript state built around an array. You will see each new parcel remain in arrival order. You will also discover why storage alone cannot decide how a parcel should be processed.
In this step, get ready to:
- Define the state that stores parcel arrivals.
- Create parcel records from recipient names.
- Test what array storage can do without queue behavior.
Define the parcel state
State holds information that can change while the app runs. Here, the array holds each parcel while two number values track the future queue position and the next tracking ID.
- In the open index.html file, click inside the empty <script> area.
- Add the parcel state and tracking helper by pasting this code:
const queueItems = [];
let queueHead = 0;
let nextParcelNumber = 1;
function createTrackingId() {
const trackingId = `PKG-${nextParcelNumber}`;
nextParcelNumber += 1;
return trackingId;
}
What does this code do?
- The queueItems array starts empty because no parcels have arrived.
- The queueHead value starts at 0. The next step uses this position to identify the earliest waiting record.
- The nextParcelNumber value increases whenever createTrackingId() generates an ID.
- Save index.html.
- Switch back to Microsoft Edge.
- Refresh the page.
You should still see the Parcel Data Structures Lab heading and all four panels. This confirms the browser accepted the new JavaScript without stopping the page.
Page stops loading?
- Check that the opening and closing braces around createTrackingId() match.
- Check that the tracking ID uses backticks around PKG-${nextParcelNumber}.
- Still stuck? Help me check the parcel state and tracking helper.
Add and render parcel records
A traversal visits the items in a structure one at a time. The first version of render() traverses the array from the beginning so the screen matches arrival order.
- Place this function below createTrackingId() inside the existing <script> area:
function render() {
let queueText = "";
for (let index = 0; index < queueItems.length; index += 1) {
const parcel = queueItems[index];
queueText += `${parcel.trackingId} | ${parcel.recipient}\n`;
}
document.getElementById("queue-output").textContent = queueText || "No parcels waiting.";
}
render();
How does rendering work?
- The loop reads every record in queueItems from the first array position to the last.
- The queueText string collects one tracking ID and recipient per line.
- The final assignment uses textContent to place the result inside queue-output.
- Save index.html.
- Refresh the page in Microsoft Edge.
You should see No parcels waiting. in the Waiting Queue panel. The array is empty, so the renderer shows its empty-state message.
Waiting Queue stays blank?
- Check that render(); appears after the closing brace of render().
- Check that queue-output matches the ID already used by the Waiting Queue panel.
- Still stuck? Help me troubleshoot the waiting-queue renderer.
Each parcel needs related values that move together. An object groups the generated tracking ID, the entered recipient, and the current status into one record.
- Place this code below render().
- Keep the existing final render(); call below the new code:
function setStatus(message) {
document.getElementById("status").textContent = message;
}
function addParcel() {
const recipientInput = document.getElementById("recipient-input");
const recipient = recipientInput.value;
if (recipient === "") {
setStatus("Enter a recipient name first.");
return;
}
const parcel = {
trackingId: createTrackingId(),
recipient: recipient,
status: "Waiting"
};
queueItems.push(parcel);
recipientInput.value = "";
setStatus(`Added ${parcel.trackingId} for ${parcel.recipient}.`);
render();
}
document.getElementById("add-button").addEventListener("click", addParcel);
How does a parcel reach the array?
- The recipient value comes from the recipient input.
- The empty-input check stops the function before an incomplete record reaches the array.
- The parcel object keeps trackingId, recipient, and status together.
- The push() call appends the new record at the end of queueItems.
- Save index.html.
- Refresh the page in Microsoft Edge.
- Enter Ava in the recipient field.
- Select Add Parcel.
You should see PKG-1 | Ava in the Waiting Queue. The recipient field clears after the record reaches the array.
Parcel does not appear?
- Check that add-button matches the ID on the existing Add Parcel button.
- Check that queueItems.push(parcel); appears before the call to render();.
- Still stuck? Help me debug the Add Parcel behavior.
Test array storage and its limit
Appending records preserves their arrival order. The array still needs a processing policy before the Process Next button can move the correct record.
- Insert this temporary listener below the existing add-button listener:
document.getElementById("process-button").addEventListener("click", () => {
setStatus("Array storage works, but queue processing is not implemented yet.");
});
Why add temporary feedback?
This listener proves that the Process Next button receives the click. Its message also makes the missing behavior visible before queue logic is introduced.
- Save index.html.
- Refresh the page in Microsoft Edge.
- Enter Ava in the recipient field.
- Select Add Parcel.
- Enter Ben in the recipient field.
- Select Add Parcel.
You should see PKG-1 | Ava above PKG-2 | Ben. The array preserves the order in which you added the records.
Before you select Process Next, predict whether the array knows which parcel should leave first.
- Select Process Next.
The status area explains that array storage works while queue processing is still missing. This shortfall is intentional because an ordered collection does not define a dequeue operation by itself.
Process Next shows no message?
- Check that the temporary listener appears before the final render(); call.
- Check that process-button matches the ID on the existing Process Next button.
- Still stuck? Help me check the temporary Process Next listener.
✔️ Awesome, I've got everything!
Your app now stores parcel records in arrival order. Double check that you saved index.html.
ⓧ I'd like to double check the full code
- Compare your complete index.html file with this version:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Parcel Data Structures Lab</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f7fb;
color: #172033;
}
main {
width: min(960px, 92%);
margin: 32px auto;
}
header, .card {
background: white;
border: 1px solid #dce3ee;
border-radius: 14px;
padding: 20px;
box-shadow: 0 8px 24px rgba(23, 32, 51, 0.06);
}
header { margin-bottom: 18px; }
h1, h2 { margin-top: 0; }
h2 { font-size: 1rem; }
.controls, .grid {
display: grid;
gap: 14px;
}
.controls {
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
margin-bottom: 18px;
}
.grid {
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.row { display: flex; gap: 8px; flex-wrap: wrap; }
input, button {
font: inherit;
border-radius: 9px;
padding: 10px 12px;
}
input {
min-width: 0;
flex: 1;
border: 1px solid #aeb9ca;
}
button {
border: 0;
background: #315efb;
color: white;
cursor: pointer;
}
button.secondary { background: #526174; }
pre {
min-height: 96px;
margin: 0;
padding: 12px;
border-radius: 9px;
background: #eef3fb;
white-space: pre-wrap;
font-family: Consolas, monospace;
}
#status {
margin-top: 18px;
padding: 12px;
border-left: 4px solid #315efb;
background: white;
}
</style>
</head>
<body>
<main>
<header>
<h1>Parcel Data Structures Lab</h1>
<p>Add parcels, process the queue, undo with a stack, and search by tracking ID.</p>
</header>
<section class="controls">
<div class="card">
<h2>Add a parcel</h2>
<div class="row">
<input id="recipient-input" type="text" placeholder="Recipient name">
<button id="add-button" type="button">Add Parcel</button>
</div>
</div>
<div class="card">
<h2>Move parcel records</h2>
<div class="row">
<button id="process-button" type="button">Process Next</button>
<button id="undo-button" class="secondary" type="button">Undo Last</button>
</div>
</div>
<div class="card">
<h2>Find by tracking ID</h2>
<div class="row">
<input id="tracking-input" type="text" placeholder="Example: PKG-1">
<button id="find-button" type="button">Find Parcel</button>
</div>
</div>
</section>
<section class="grid">
<article class="card">
<h2>Waiting Queue</h2>
<pre id="queue-output"></pre>
</article>
<article class="card">
<h2>Processed Stack</h2>
<pre id="stack-output"></pre>
</article>
<article class="card">
<h2>Lookup Result</h2>
<pre id="lookup-output">No lookup yet.</pre>
</article>
<article class="card">
<h2>Structures in Use</h2>
<pre>Array: stores records
Queue: earliest waiting parcel first
Stack: latest processed parcel first
Map: tracking ID to parcel record</pre>
</article>
</section>
<p id="status" aria-live="polite">App ready. Add a parcel to begin.</p>
</main>
<script>
const queueItems = [];
let queueHead = 0;
let nextParcelNumber = 1;
function createTrackingId() {
const trackingId = `PKG-${nextParcelNumber}`;
nextParcelNumber += 1;
return trackingId;
}
function render() {
let queueText = "";
for (let index = 0; index < queueItems.length; index += 1) {
const parcel = queueItems[index];
queueText += `${parcel.trackingId} | ${parcel.recipient}\n`;
}
document.getElementById("queue-output").textContent = queueText || "No parcels waiting.";
}
function setStatus(message) {
document.getElementById("status").textContent = message;
}
function addParcel() {
const recipientInput = document.getElementById("recipient-input");
const recipient = recipientInput.value;
if (recipient === "") {
setStatus("Enter a recipient name first.");
return;
}
const parcel = {
trackingId: createTrackingId(),
recipient: recipient,
status: "Waiting"
};
queueItems.push(parcel);
recipientInput.value = "";
setStatus(`Added ${parcel.trackingId} for ${parcel.recipient}.`);
render();
}
document.getElementById("add-button").addEventListener("click", addParcel);
document.getElementById("process-button").addEventListener("click", () => {
setStatus("Array storage works, but queue processing is not implemented yet.");
});
render();
</script>
</body>
</html>
That is your first live data structure working in the browser. Next, you will give these ordered records a FIFO processing rule.
Process Parcels with a FIFO Queue
Your array now preserves each parcel's arrival position. The Process Next control still needs a rule for choosing which waiting parcel moves first.
A FIFO queue reads the earliest waiting record before later arrivals. A head index applies that rule without deleting records from the array.
In this step, get ready to:
- Implement head-index FIFO processing.
- Render both data-structure views.
- Demonstrate the earliest-arrival rule.
Implement FIFO processing
The head index points to the earliest record that still needs processing. Moving that index forward makes the next arrival the new queue front.
- Switch back to index.html in VS Code.
- Find the state declarations near the top of the script element.
- Add the processed-record array directly below the queueHead declaration by entering this line:
const completionStack = [];
Why Store Processed Records?
The completionStack array keeps references to parcels after processing. The app uses those records to build the Processed Stack panel.
- Find the closing brace of addParcel().
- Add the processing function directly below addParcel() by pasting this code:
function processNextParcel() {
if (queueHead >= queueItems.length) {
setStatus("The waiting queue is empty.");
return;
}
const parcel = queueItems[queueHead];
queueHead += 1;
parcel.status = "Processed";
completionStack.push(parcel);
setStatus(`Processed ${parcel.trackingId} using FIFO order.`);
render();
}
What Does This Function Do?
- The empty-queue check stops the function when queueHead has reached the array length.
- The queueItems[queueHead] expression selects the earliest unprocessed record.
- The queueHead += 1 line moves the front position to the next record.
- The status change records that the selected parcel has been processed.
- The completionStack.push(parcel) call stores the processed record for display.
- The render() call refreshes the output panels after the state changes.
- Find the temporary event listener for process-button at the bottom of index.html.
- Replace the entire temporary listener with this final connection:
document.getElementById("process-button").addEventListener("click", processNextParcel);
How Does the Button Reach the Function?
The event listener calls processNextParcel whenever you select the Process Next button. The function now owns the complete processing action.
- Save index.html.
- Switch back to Microsoft Edge.
- Refresh the page.
- Enter Ava in the recipient input.
- Select Add Parcel.
- Select Process Next.
The status confirms that the parcel was processed with FIFO order. The Waiting Queue still includes Ava because its display loop uses the previous rendering rule.
Does Process Next Still Show the Temporary Message?
- Confirm that you replaced the entire temporary event listener for process-button.
- Check that the replacement listener names processNextParcel exactly.
- Still stuck? Help me connect Process Next to my FIFO function.
Render the queue front
The array keeps every record that has arrived. The rendering loop starts at queueHead so processed records disappear from the waiting region.
- Find the current render() function in index.html.
- Replace the complete function with this version:
function render() {
let queueText = "";
for (let index = queueHead; index < queueItems.length; index += 1) {
const marker = index === queueHead ? "FRONT -> " : " ";
const parcel = queueItems[index];
queueText += `${marker}${parcel.trackingId} | ${parcel.recipient}\n`;
}
let stackText = "";
for (let index = completionStack.length - 1; index >= 0; index -= 1) {
const marker = index === completionStack.length - 1 ? "TOP -> " : " ";
const parcel = completionStack[index];
stackText += `${marker}${parcel.trackingId} | ${parcel.recipient}\n`;
}
document.getElementById("queue-output").textContent = queueText || "No parcels waiting.";
document.getElementById("stack-output").textContent = stackText || "No parcels processed.";
}
How Does Rendering Show FIFO Order?
- The queue loop begins at queueHead so it skips every processed record.
- The first remaining record receives the FRONT -> marker.
- The processed-record loop reads completionStack from its final index toward its first index.
- The two textContent assignments replace each panel with the latest state.
- Save index.html.
- Switch back to Microsoft Edge.
- Refresh the page.
- Enter Ava in the recipient input.
- Select Add Parcel.
You should see PKG-1 beside the FRONT -> marker. This confirms that the display recognizes the current queue front.
Is the Front Marker Missing?
- Check that the queue loop starts with index = queueHead.
- Confirm that the marker condition compares index with queueHead.
- Still stuck? Help me fix the queue front rendering.
Run the FIFO test
The complete test needs two arrivals because FIFO describes the order between records. Resetting the page also resets the generated tracking IDs.
- Refresh the page.
- Enter Ava in the recipient input.
- Select Add Parcel.
- Enter Ben in the recipient input.
- Select Add Parcel.
Before you select Process Next, which tracking ID do you expect the FIFO queue to process?
- Select Process Next.
You should see PKG-1 move to Processed Stack. You should see PKG-2 become the queue front.
That result locks in FIFO behavior. The parcel that arrived first was processed first.
One more control remains. Before you select Undo Last, what do you expect the current version to do?
- Select Undo Last.
You'll see a status message explaining that undo still needs stack logic. This intended gap shows that processing order works while recovery remains unavailable.
Does the FIFO Result Look Different?
- Refresh the page before repeating the test so the tracking IDs restart from PKG-1.
- Confirm that processNextParcel() reads the record before increasing queueHead.
- Check that render() begins the queue loop at queueHead.
- Still stuck? Help me debug my FIFO parcel test.
Your parcel desk now processes arrivals in FIFO order. Next up, you'll make the latest processing action reversible.
Undo Work with a LIFO Stack
The FIFO queue now moves the earliest waiting parcel into the Processed Stack. A mistaken processing click still leaves the parcel in the wrong place.
During this step, completionStack becomes a LIFO stack that can reverse the latest processing action. The app will also reveal why tracking lookup needs its own structure.
In this step, get ready to:
- Render the latest completed parcel at the top of the Processed Stack.
- Restore the latest processed parcel with LIFO behavior.
- Expose the missing direct tracking lookup.
Render the stack top first
A LIFO stack removes the most recently added item first. Because processNextParcel() already pushes each completed record onto completionStack, the final array item represents the stack top.
- Switch back to parcel-structures/index.html in Visual Studio Code.
- Find the existing render() function.
- Replace the entire function with this version:
function render() {
let queueText = "";
for (let index = queueHead; index < queueItems.length; index += 1) {
const marker = index === queueHead ? "FRONT -> " : " ";
const parcel = queueItems[index];
queueText += `${marker}${parcel.trackingId} | ${parcel.recipient}\n`;
}
let stackText = "";
for (let index = completionStack.length - 1; index >= 0; index -= 1) {
const marker = index === completionStack.length - 1 ? "TOP -> " : " ";
const parcel = completionStack[index];
stackText += `${marker}${parcel.trackingId} | ${parcel.recipient}\n`;
}
document.getElementById("queue-output").textContent = queueText || "No parcels waiting.";
document.getElementById("stack-output").textContent = stackText || "No parcels processed.";
}
What does this code do?
- The queue loop begins at queueHead so processed records stay out of the Waiting Queue.
- The stack loop begins at the final completionStack index so the newest completed record appears first.
- The TOP -> marker identifies the record that an undo operation must remove.
- The textContent assignments safely replace the visible queue output and stack output.
- Save index.html.
- Return to the app in Microsoft Edge.
- Refresh the page.
- Enter Ava in the input under Add a parcel.
- Select Add Parcel.
- Select Process Next.
Good progress. You should see TOP -> PKG-1 | Ava in the Processed Stack panel.
Stack top not showing?
- Confirm that the stack loop starts at completionStack.length - 1.
- Check that you saved index.html before refreshing Microsoft Edge.
- Still stuck? Help me troubleshoot my Processed Stack rendering.
Add the undo operation
Undo follows the stack removal rule. The pop() method removes the latest array item so the app can restore that parcel to the waiting region.
- Find the closing brace of processNextParcel() in index.html.
- Add the undo function directly below processNextParcel() by pasting this code:
function undoLastParcel() {
if (completionStack.length === 0) {
setStatus("The processed stack is empty.");
return;
}
const parcel = completionStack.pop();
queueHead -= 1;
parcel.status = "Waiting";
setStatus(`Returned ${parcel.trackingId} using LIFO order.`);
render();
}
How does undo restore a parcel?
- The length check stops the function when no processed parcel is available.
- The pop() call removes the latest record from completionStack.
- Decreasing queueHead moves the queue front back by one position.
- Changing status to Waiting restores the record before render() redraws both panels.
- Find the existing event listener connected to undo-button.
- Replace that listener with this line:
document.getElementById("undo-button").addEventListener("click", undoLastParcel);
What does this listener do?
The listener calls undoLastParcel() whenever you select Undo Last. This connects the visible control to the stack operation.
✔️ Awesome, I've got everything!
Your undo function and event listener now match the LIFO behavior required by the app.
ⓧ I'd like to double check the full code
- Compare your complete index.html file with this version:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Parcel Data Structures Lab</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f7fb;
color: #172033;
}
main {
width: min(960px, 92%);
margin: 32px auto;
}
header, .card {
background: white;
border: 1px solid #dce3ee;
border-radius: 14px;
padding: 20px;
box-shadow: 0 8px 24px rgba(23, 32, 51, 0.06);
}
header { margin-bottom: 18px; }
h1, h2 { margin-top: 0; }
h2 { font-size: 1rem; }
.controls, .grid {
display: grid;
gap: 14px;
}
.controls {
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
margin-bottom: 18px;
}
.grid {
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.row { display: flex; gap: 8px; flex-wrap: wrap; }
input, button {
font: inherit;
border-radius: 9px;
padding: 10px 12px;
}
input {
min-width: 0;
flex: 1;
border: 1px solid #aeb9ca;
}
button {
border: 0;
background: #315efb;
color: white;
cursor: pointer;
}
button.secondary { background: #526174; }
pre {
min-height: 96px;
margin: 0;
padding: 12px;
border-radius: 9px;
background: #eef3fb;
white-space: pre-wrap;
font-family: Consolas, monospace;
}
#status {
margin-top: 18px;
padding: 12px;
border-left: 4px solid #315efb;
background: white;
}
</style>
</head>
<body>
<main>
<header>
<h1>Parcel Data Structures Lab</h1>
<p>Add parcels, process the queue, undo with a stack, and search by tracking ID.</p>
</header>
<section class="controls">
<div class="card">
<h2>Add a parcel</h2>
<div class="row">
<input id="recipient-input" type="text" placeholder="Recipient name">
<button id="add-button" type="button">Add Parcel</button>
</div>
</div>
<div class="card">
<h2>Move parcel records</h2>
<div class="row">
<button id="process-button" type="button">Process Next</button>
<button id="undo-button" class="secondary" type="button">Undo Last</button>
</div>
</div>
<div class="card">
<h2>Find by tracking ID</h2>
<div class="row">
<input id="tracking-input" type="text" placeholder="Example: PKG-1">
<button id="find-button" type="button">Find Parcel</button>
</div>
</div>
</section>
<section class="grid">
<article class="card">
<h2>Waiting Queue</h2>
<pre id="queue-output"></pre>
</article>
<article class="card">
<h2>Processed Stack</h2>
<pre id="stack-output"></pre>
</article>
<article class="card">
<h2>Lookup Result</h2>
<pre id="lookup-output">No lookup yet.</pre>
</article>
<article class="card">
<h2>Structures in Use</h2>
<pre>Array: stores records
Queue: earliest waiting parcel first
Stack: latest processed parcel first
Map: tracking ID to parcel record</pre>
</article>
</section>
<p id="status" aria-live="polite">App ready. Add a parcel to begin.</p>
</main>
<script>
const queueItems = [];
let queueHead = 0;
const completionStack = [];
let nextParcelNumber = 1;
function createTrackingId() {
const trackingId = `PKG-${nextParcelNumber}`;
nextParcelNumber += 1;
return trackingId;
}
function setStatus(message) {
document.getElementById("status").textContent = message;
}
function addParcel() {
const recipientInput = document.getElementById("recipient-input");
const recipient = recipientInput.value;
if (recipient === "") {
setStatus("Enter a recipient name first.");
return;
}
const parcel = {
trackingId: createTrackingId(),
recipient: recipient,
status: "Waiting"
};
queueItems.push(parcel);
recipientInput.value = "";
setStatus(`Added ${parcel.trackingId} for ${parcel.recipient}.`);
render();
}
function processNextParcel() {
if (queueHead >= queueItems.length) {
setStatus("The waiting queue is empty.");
return;
}
const parcel = queueItems[queueHead];
queueHead += 1;
parcel.status = "Processed";
completionStack.push(parcel);
setStatus(`Processed ${parcel.trackingId} using FIFO order.`);
render();
}
function undoLastParcel() {
if (completionStack.length === 0) {
setStatus("The processed stack is empty.");
return;
}
const parcel = completionStack.pop();
queueHead -= 1;
parcel.status = "Waiting";
setStatus(`Returned ${parcel.trackingId} using LIFO order.`);
render();
}
function render() {
let queueText = "";
for (let index = queueHead; index < queueItems.length; index += 1) {
const marker = index === queueHead ? "FRONT -> " : " ";
const parcel = queueItems[index];
queueText += `${marker}${parcel.trackingId} | ${parcel.recipient}\n`;
}
let stackText = "";
for (let index = completionStack.length - 1; index >= 0; index -= 1) {
const marker = index === completionStack.length - 1 ? "TOP -> " : " ";
const parcel = completionStack[index];
stackText += `${marker}${parcel.trackingId} | ${parcel.recipient}\n`;
}
document.getElementById("queue-output").textContent = queueText || "No parcels waiting.";
document.getElementById("stack-output").textContent = stackText || "No parcels processed.";
}
document.getElementById("add-button").addEventListener("click", addParcel);
document.getElementById("process-button").addEventListener("click", processNextParcel);
document.getElementById("undo-button").addEventListener("click", undoLastParcel);
document.getElementById("find-button").addEventListener("click", () => {
setStatus("Tracking lookup is not implemented yet.");
});
render();
</script>
</body>
</html>
- Save index.html.
- Return to Microsoft Edge.
- Refresh the page.
- Enter Ava in the input under Add a parcel.
- Select Add Parcel.
- Enter Ben in the input under Add a parcel.
- Select Add Parcel.
Before you process both parcels, which tracking ID do you expect to finish at the stack top?
- Select Process Next.
- Select Process Next again.
You should see TOP -> PKG-2 | Ben above PKG-1 | Ava in the Processed Stack panel.
Before you select Undo Last, which parcel do you expect to return to the queue front?
- Select Undo Last.
That is your LIFO recovery working. You should see FRONT -> PKG-2 | Ben in the Waiting Queue while PKG-1 | Ava remains in the Processed Stack.
Undo returning the wrong parcel?
- Confirm that undoLastParcel() uses completionStack.pop().
- Check that the function decreases queueHead by exactly 1.
- Verify that the undo-button listener names undoLastParcel.
- Still stuck? Help me debug my LIFO undo behavior.
Expose the missing tracking lookup
The queue now controls processing order. The stack now controls recovery order.
A tracking ID still needs a way to retrieve its parcel record directly. The existing Find Parcel control exposes that missing capability.
Before you select Find Parcel, do you think the app can retrieve PKG-1 from its tracking ID?
- Enter PKG-1 in the input under Find by tracking ID.
- Select Find Parcel.
You should see the existing status message explain that tracking lookup has not been implemented. This visible shortfall proves that processing order alone cannot provide direct retrieval.
Your simulator can now reverse its latest processing action while preserving FIFO order. Next, you will add a Map so each tracking ID can retrieve its parcel record directly.
Find Parcels with a Keyed Map
Your parcel desk now uses a FIFO queue to process the earliest arrival. Its LIFO stack restores the latest processed parcel.
Finding one tracking ID still requires a manual scan through the records. A keyed Map connects each tracking ID directly to its parcel record.
In this step, get ready to:
- Store every new parcel under its tracking ID.
- Retrieve matching records through the keyed Map.
- Demonstrate the four data structures in one browser run.
Store each parcel in a keyed Map
An array keeps the parcel records in arrival order. The Map gives every record a separate key for direct retrieval.
The trackingId becomes the key. The parcel object becomes the value stored under that key.
- In the <script> section of index.html, find const completionStack = [];.
- Paste the Map declaration directly below that line:
const parcelById = new Map();
What does this declaration do?
- The parcelById variable holds the keyed Map for every parcel.
- Each generated tracking ID can now point to one parcel record.
- Inside addParcel(), find queueItems.push(parcel);.
- Paste the Map registration immediately after that line:
parcelById.set(parcel.trackingId, parcel);
How is the record stored?
- The set() call stores the current parcel under its generated tracking ID.
- The same parcel object remains inside queueItems.
- A later status change is visible through the Map because both structures refer to the same record.
- Save index.html.
- Switch back to Microsoft Edge.
- Refresh the page.
- Enter Kai in the recipient field.
- Click Add Parcel.
You will see FRONT -> PKG-1 | Kai in the Waiting Queue. The status reports Added PKG-1 for Kai..
That is a solid checkpoint. The parcel still reaches the queue while the Map stores the same record under its tracking ID.
Parcel not appearing?
- Check that const parcelById = new Map(); appears before nextParcelNumber.
- Check that the set() line remains inside addParcel().
- Still stuck? Help me check why adding a parcel stopped working after I created parcelById and called set().
Retrieve a parcel by tracking ID
The get() method retrieves the value stored under a specific key. The lookup can then display the parcel record through textContent.
- In index.html, find the end of undoLastParcel().
- Paste the complete lookup function before render():
function findParcel() {
const trackingId = document.getElementById("tracking-input").value;
const parcel = parcelById.get(trackingId);
const output = document.getElementById("lookup-output");
if (parcel === undefined) { output.textContent = `No parcel found for ${trackingId || "that ID"}.`; setStatus("Lookup finished with no match."); return; }
output.textContent = `${parcel.trackingId}\nRecipient: ${parcel.recipient}\nStatus: ${parcel.status}`;
setStatus(`Found ${parcel.trackingId} with one Map lookup.`);
}
What does this function do?
- The trackingId variable reads the value from tracking-input.
- The get() call retrieves the parcel stored under that exact tracking ID.
- The missing-record branch writes a clear result when the Map returns undefined.
- The matching-record branch displays the tracking ID, recipient, and current status.
- Save index.html.
- Switch back to Microsoft Edge.
- Refresh the page.
- Enter Nia in the recipient field.
- Click Add Parcel.
You will see PKG-1 | Nia in the Waiting Queue. This confirms the new function has not interrupted the existing script.
Add Parcel stopped responding?
- Check that findParcel() has one opening brace and one closing brace.
- Check that the function sits outside undoLastParcel().
- Need another pair of eyes? Help me find a syntax or placement problem in my findParcel function.
Connect and test the final lookup
An event listener connects a button click to the function that handles it. The final listener replaces the temporary message attached to Find Parcel.
- At the bottom of <script>, select the event-listener section from the Add Parcel listener through the temporary Find Parcel listener.
- Replace the selection with the final listener group by pasting:
document.getElementById("add-button").addEventListener("click", addParcel);
document.getElementById("process-button").addEventListener("click", processNextParcel);
document.getElementById("undo-button").addEventListener("click", undoLastParcel);
document.getElementById("find-button").addEventListener("click", findParcel);
How are the controls connected?
- The Add Parcel listener calls addParcel().
- The Process Next listener calls processNextParcel().
- The Undo Last listener calls undoLastParcel().
- The Find Parcel listener now calls findParcel().
✔️ Awesome, I've got everything!
Great. Double check that you have saved index.html before testing the completed parcel simulator.
ⓧ I'd like to double check the full code
- Compare your index.html file with the complete version below:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Parcel Data Structures Lab</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; font-family: Arial, sans-serif; background: #f4f7fb; color: #172033; }
main { width: min(960px, 92%); margin: 32px auto; }
header, .card { background: white; border: 1px solid #dce3ee; border-radius: 14px; padding: 20px; box-shadow: 0 8px 24px rgba(23, 32, 51, 0.06); }
header { margin-bottom: 18px; }
h1, h2 { margin-top: 0; }
h2 { font-size: 1rem; }
.controls, .grid { display: grid; gap: 14px; }
.controls { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); margin-bottom: 18px; }
.grid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.row { display: flex; gap: 8px; flex-wrap: wrap; }
input, button { font: inherit; border-radius: 9px; padding: 10px 12px; }
input { min-width: 0; flex: 1; border: 1px solid #aeb9ca; }
button { border: 0; background: #315efb; color: white; cursor: pointer; }
button.secondary { background: #526174; }
pre { min-height: 96px; margin: 0; padding: 12px; border-radius: 9px; background: #eef3fb; white-space: pre-wrap; font-family: Consolas, monospace; }
#status { margin-top: 18px; padding: 12px; border-left: 4px solid #315efb; background: white; }
</style>
</head>
<body>
<main>
<header><h1>Parcel Data Structures Lab</h1><p>Add parcels, process the queue, undo with a stack, and search by tracking ID.</p></header>
<section class="controls">
<div class="card"><h2>Add a parcel</h2><div class="row"><input id="recipient-input" type="text" placeholder="Recipient name"><button id="add-button" type="button">Add Parcel</button></div></div>
<div class="card"><h2>Move parcel records</h2><div class="row"><button id="process-button" type="button">Process Next</button><button id="undo-button" class="secondary" type="button">Undo Last</button></div></div>
<div class="card"><h2>Find by tracking ID</h2><div class="row"><input id="tracking-input" type="text" placeholder="Example: PKG-1"><button id="find-button" type="button">Find Parcel</button></div></div>
</section>
<section class="grid">
<article class="card"><h2>Waiting Queue</h2><pre id="queue-output"></pre></article>
<article class="card"><h2>Processed Stack</h2><pre id="stack-output"></pre></article>
<article class="card"><h2>Lookup Result</h2><pre id="lookup-output">No lookup yet.</pre></article>
<article class="card"><h2>Structures in Use</h2><pre>Array: stores records
Queue: earliest waiting parcel first
Stack: latest processed parcel first
Map: tracking ID to parcel record</pre></article>
</section>
<p id="status" aria-live="polite">App ready. Add a parcel to begin.</p>
</main>
<script>
const queueItems = [];
let queueHead = 0;
const completionStack = [];
const parcelById = new Map();
let nextParcelNumber = 1;
function createTrackingId() { const trackingId = `PKG-${nextParcelNumber}`; nextParcelNumber += 1; return trackingId; }
function setStatus(message) { document.getElementById("status").textContent = message; }
function addParcel() {
const recipientInput = document.getElementById("recipient-input");
const recipient = recipientInput.value;
if (recipient === "") { setStatus("Enter a recipient name first."); return; }
const parcel = { trackingId: createTrackingId(), recipient: recipient, status: "Waiting" };
queueItems.push(parcel); parcelById.set(parcel.trackingId, parcel); recipientInput.value = "";
setStatus(`Added ${parcel.trackingId} for ${parcel.recipient}.`); render();
}
function processNextParcel() {
if (queueHead >= queueItems.length) { setStatus("The waiting queue is empty."); return; }
const parcel = queueItems[queueHead]; queueHead += 1; parcel.status = "Processed"; completionStack.push(parcel);
setStatus(`Processed ${parcel.trackingId} using FIFO order.`); render();
}
function undoLastParcel() {
if (completionStack.length === 0) { setStatus("The processed stack is empty."); return; }
const parcel = completionStack.pop(); queueHead -= 1; parcel.status = "Waiting";
setStatus(`Returned ${parcel.trackingId} using LIFO order.`); render();
}
function findParcel() {
const trackingId = document.getElementById("tracking-input").value;
const parcel = parcelById.get(trackingId);
const output = document.getElementById("lookup-output");
if (parcel === undefined) { output.textContent = `No parcel found for ${trackingId || "that ID"}.`; setStatus("Lookup finished with no match."); return; }
output.textContent = `${parcel.trackingId}\nRecipient: ${parcel.recipient}\nStatus: ${parcel.status}`;
setStatus(`Found ${parcel.trackingId} with one Map lookup.`);
}
function render() {
let queueText = "";
for (let index = queueHead; index < queueItems.length; index += 1) { const marker = index === queueHead ? "FRONT -> " : " "; const parcel = queueItems[index]; queueText += `${marker}${parcel.trackingId} | ${parcel.recipient}\n`; }
let stackText = "";
for (let index = completionStack.length - 1; index >= 0; index -= 1) { const marker = index === completionStack.length - 1 ? "TOP -> " : " "; const parcel = completionStack[index]; stackText += `${marker}${parcel.trackingId} | ${parcel.recipient}\n`; }
document.getElementById("queue-output").textContent = queueText || "No parcels waiting.";
document.getElementById("stack-output").textContent = stackText || "No parcels processed.";
}
document.getElementById("add-button").addEventListener("click", addParcel);
document.getElementById("process-button").addEventListener("click", processNextParcel);
document.getElementById("undo-button").addEventListener("click", undoLastParcel);
document.getElementById("find-button").addEventListener("click", findParcel);
render();
</script>
</body>
</html>
What should you compare?
- Confirm that parcelById is declared with the other state values.
- Confirm that addParcel() stores each parcel with set().
- Confirm that findParcel() uses get() before the final listener connects it to the button.
- Save index.html.
Before you test the complete app, predict whether a lookup for PKG-1 will show Ava as waiting or processed.
- Switch back to Microsoft Edge.
- Refresh the page.
- Enter Ava in the recipient field.
- Click Add Parcel.
- Enter Ben in the recipient field.
- Click Add Parcel.
The Waiting Queue lists PKG-1 before PKG-2. This confirms the array still preserves arrival order.
- Click Process Next.
- Enter PKG-1 in the Find by tracking ID field.
- Click Find Parcel.
The Lookup Result shows PKG-1 for Ava with a current status of Processed.
You have direct lookup working. The Map finds the processed parcel without changing its place in the queue or stack.
One final sequence proves that keyed lookup keeps reporting the same record after a stack operation.
- Click Process Next.
- Click Undo Last.
- Enter PKG-2 in the Find by tracking ID field.
- Click Find Parcel.
You will see PKG-2 back at the queue front. Its lookup status now reads Waiting, while PKG-1 remains processed.
Find Parcel still shows the temporary message?
- Check that the final Find Parcel listener ends with findParcel.
- Remove any earlier Find Parcel listener that only calls setStatus().
- Still seeing the old behavior? Help me connect the Find Parcel button to findParcel().
Your parcel simulator now demonstrates every structure in one browser run. The lookup also reports the latest status after queue or stack changes.
Secret mission
Add Urgent and Standard Queue Lanes
Give urgent parcels their own FIFO lane without losing arrival order. Your app will process urgent work first while keeping undo and tracking lookup accurate.
Clean Up Your Resources
Clean Up Your Resources
This local project has no ongoing costs. Choose a cleanup option below.
Resources you used:
- The local parcel-structures folder containing index.html.
Keep everything running
No action is needed while you are still testing the priority lanes. Your local files remain ready for another session.
- The parcel-structures/index.html file stays on your computer.
- The Priority Parcel Data Structures Lab can stay open in Microsoft Edge.
- The project can stay open in Visual Studio Code.
Pause - I'll come back to this later
A paused session clears your open workspace without removing the project.
- Switch back to Microsoft Edge from earlier.
- Close the browser tab displaying the Priority Parcel Data Structures Lab.
- Switch back to Visual Studio Code from earlier.
- Close Visual Studio Code.
That gives you a clean stopping point. Every project change remains in parcel-structures/index.html.
Delete - I don't want to use this again
Deleting a project can feel final. This removes only the parcel-structures folder.
Microsoft Edge and Visual Studio Code remain installed.
- Switch back to Microsoft Edge from earlier.
- Close the browser tab displaying the Priority Parcel Data Structures Lab.
- Switch back to Visual Studio Code from earlier.
- Locate index.html in the file sidebar.
- Right-click index.html.
- Select the option that reveals the file in the Windows file manager.
The Windows file manager now shows index.html inside parcel-structures.
- Close Visual Studio Code.
- Move up one level in the Windows file manager.
- Delete the parcel-structures folder.
- Open the Windows area that holds deleted items.
- Permanently delete only the parcel-structures folder from that area.
- Return to the location that previously contained parcel-structures.
You should no longer see the parcel-structures folder. That clears the only project resource from your computer.
Nice Work!
Nice Work!
That’s a wrap! You built a working priority parcel simulator in parcel-structures/index.html. Your app now moves parcel records through each data structure on screen.
You've learned how to:
- Store ordered parcel records in an array so each arrival remains visible in sequence.
- Apply FIFO queue processing to handle the earliest waiting parcel first. Apply LIFO stack recovery to undo the latest processing action first.
- Retrieve recipient details through a keyed Map using a displayed tracking ID.
- Complete the Secret Mission by splitting waiting work into urgent and standard FIFO lanes. Preserve arrival order inside each lane.
Ready to quiz yourself?