The only way to to learn to code is to write more code -Aristoteles
Introduction to my Javascript journey
Hello! I'm MoStackito, a newbie web developer on a journey into coding, powered by the Scrimba JavaScript course. In part 1 of my blog, I shared my initial steps, including building a passenger counter app and a blackjack game, and I detailed the challenges and progress along the way. Encouraged by my tutor, Per Harald Borgen, to document this journey, I’m back with part 2.
This time, I'll dive into my latest projects: creating a Chrome extension and developing a mobile app. I'll walk you through how I built these, the obstacles I encountered, and the exciting moments where everything clicked. Stay tuned as I reflect on each project and the skills I’ve gained in my web development journey!
My third project was creating a Chrome extension app.
These are the following topics I have learned to build the chrome extension app;
"Mastering const: Locking Down Your Variables"
"Listening to User Actions with addEventListener()"
"Updating Content on the Fly with innerHTML"
"Capturing Input with input.value"
"Making Functions Flexible with Parameters"
"Spicing Up Your Code with Template Strings"
"Saving Data Permanently with localStorage"
"Parsing and Storing Data Using the JSON Object"
"Organising Data Smartly: Working with Objects in Arrays"
"Mastering const: Locking Down Your Variables"
An intro to const
and why variable security matters in JavaScript.
If possible use CONST if not, use let - KEEP THIS IN MIND WHEN CODING
const = constant which means variables values won’t change, if you declare it with let you can change the values with const you cant and its good practise to keep using const when declaring variables.
For example when you use methods such as getElementById() to grab the DOM element you use const variable as the value wont need to be changed.
The only variables you can change from let too const is the variable basePrice and discount those won’t need to be changed.
and also as bonus the variable fullPrice can be changed to const as its only been used once and has not been reassigned with a new value.
// If possible, use const. If not, use let.
// Which variables below should be changed from let to const?
// The customer wants to order some stuff. Here are the details:
let basePrice = 520 //const
let discount = 120 //const
let shippingCost = 12
let shippingTime = "5-12 days"
// Whops! Turns out the shipping will be a bit more complex
shippingCost = 15
shippingTime = "7-14 days"
// Calculating the full price
//const fullPrice
let fullPrice = basePrice - discount + shippingCost
// Finally, notifying the customer
console.log("Total cost: " + fullPrice + ". It will arrive in " + shippingTime)
"Listening to User Actions with addEventListener()"
Exploring how to make your extension interactive and responsive.
This is a method that allows you to listen for clicks and activate a function() you can attach this to a DOM element for example when this button is clicked console.log a message.
Example:let inputBtn = document.getElementById("input-btn")
this a DOM element we grab using getElementById() method.
now we can use the variable inputBtn and attach it to a addEventListener()inputBtn.addEventListener("click", function() {
console.log("Button clicked from addEventListener")
})
its crucial to remember that this method works with the DOM elements.
**Example:
**I grabbed the box from the DOM and stored it in variable , then i added a click event listener to the box and finally log out the message: “I want to open the box!” when its clicked
let box = document.getElementById("box")
box.addEventListener("click", function(){
console.log("I want to open the box!")
})
Console
›I want to open the box!
"Updating Content on the Fly with innerHTML"
Using innerHTML
to bring dynamic content to your Chrome extension.
innerHTML allows you to run HTML tags inside of JS code and opens up a world of possibilities how to manipulate the DOM, instead of innerText which cannot run HTML tags, the innerHTML allows you to run the HTML tags.
Example:
This is a for loop that is looping through the array myLeads and we are using the DOM element ulEl which is a ref to <ul> list, and I am using the property .innerHTML appending it and adding the HTML tags <li> and rendering the myLeads array with the loop. so the final results will be a list of items in bullet points.
let myLeads = ["www.awesomelead.com", "www.epiclead.com", "www.greatlead.com"]
const ulEl = document.getElementById("ul-el")
for (let i = 0; i < myLeads.length; i++) {
ulEl.innerHTML += "<li>" + myLeads[i] + "</li>"
}
Example:
So here we have a HTML DIV with ID container in our HTML code, the challenge was to render a buy! button inside the div container using what we learned above by using the property .innerHTML.
So we first created a variable named container and used the getElementById() method to grab the DOM element and store it. then we used the container variable and attached it with innerHTML and created a HTML tag: <button>buy!</button>
// Use .innerHTML to render a Buy! button inside the div container
const container = document.getElementById("container")
container.innerHTML = "<button>Buy!</button>"
Example:
When the button is clicked i created a function buy() to render a paragraph under the button in ( the container ) that says "Thank you for buying”
Inside of the container.innerHTML I added a onclick event buy()
I created buy() function inside of the function body I used the container.innerHTML and append it with += "<p>Thank you for buying!</p>"
so when clicked the message would appear below the button.
const container = document.getElementById("container")
container.innerHTML = "<button onclick='buy()'>Buy!</button>"
// When clicked, render a paragraph under the button (in the container)
// that says "Thank you for buying!"
function buy(){
container.innerHTML += "<p>Thank you for buying!</p>"
}
"Capturing Input with input.value"
How input fields give users control and help drive functionality.
Here I learned how to push the value of inputEl DOM element into the myLeads array, the inputEl DOM is a reference to input field tag.
I used the push() method for the array myLeads, and pushed the inputEl.value.
Pro-tip: .value is a property and not a METHOD, properties store data or values while methods are functions that perform an action, such as .push() it pushes values to an array ( ACTION )
let myLeads = []
const inputEl = document.getElementById("input-el")
const inputBtn = document.getElementById("input-btn")
inputBtn.addEventListener("click", function() {
// Push the value from the inputEl into the myLeads array
// instead of the hard-coded "www.awesomeleads.com" value
// Google -> "get value from input field javascript"
myLeads.push("www.awesomelead.com")
console.log(myLeads)
})
//Solution:
inputBtn.addEventListener("click", function() {
myLeads.push(inputEl.value)
console.log(myLeads)
})
Console
›["www.example.com"]
"Making Functions Flexible with Parameters"
The role of function parameters in customising your code.
A parameter in a function acts like a placeholder think of it like an empty variable inside of the function, you will need to pass data values as an ARGUMENT outside of the function scope.
Example:
In this challenge the task was to give the function a greetings parameter that would replace the part of the string “Welcome Back,”
I used the “howdy“ as the argument outside of the function.
const welcomeEl = document.getElementById("welcome-el")
// Give the function a parameter, greeting, that replaces "Welcome back"
function greetUser(greeting) {
welcomeEl.textContent = greeting + ", Per Harald Borgen 👋"
}
greetUser("Howdy")
Example:
In this challenge i rewrote the expression using template literals and I also used multiple parameters. Here I used the template liters with the back-tick `` and added both of the parameters ${}
and the argument outside of the function i passed the string message, “Howdy“ , “James“
const welcomeEl = document.getElementById("welcome-el")
function greetUser(greeting, name) {
// Rewrite the expression using template literals
// welcomeEl.textContent = greeting + ", " + name + " 👋"
welcomeEl.textContent = `${greeting}, ${name} 👋`
}
greetUser("Howdy", "James")
In the final challenge I added an emoji the same way, i created a parameter emoji and added the ${} to welcomeEl.textContent to render.
Now the value is added as an argument to fill that emoji placeholder.
const welcomeEl = document.getElementById("welcome-el")
// Add the ability to choose the emoji as well!
function greetUser(greeting, name, emoji) {
welcomeEl.textContent = `${greeting}, ${name} ${emoji}`
}
greetUser("Howdy", "James", "😎"
I also created a function that adds two numbers together and returns the total sum.
i created 2 parameter num1, num2 and used the return statement to return it back to the function call. Outside of the function I used the console.log add (3, 4 ) (9, 102 ) the arguments. The results is 7 and 111
// Create a function, add(), that adds two numbers together and returns the sum
function add(num1, num2) {
return num1 + num2
}
console.log( add(3, 4) ) // should log 7
console.log( add(9, 102) ) // should log 111
Console
›7
›111
ARGUMENTS Vs PARAMETERS
The parameters are inside of the function scope and the arguments are outside of the function scope.
greeting and name are parameters
“Howdy” and “James“ are arguments
num1 and num2 are parameters
3 and 4 are arguments.
// What are greeting and name?
// What are "Howdy" and "James"?
// What are num1 and num2?
// What are 3 and 4?
//. parameters
function greetUser(greeting, name) {
welcomeEl.textContent = `${greeting}, ${name} 👋`
}
//. arguments
let hi = "Howdy"
greetUser(hi, "James")
function add(num1, num2) {
return num1 + num2
}
add(3, 4)
A pictures is worth a thousand words..
The last challenge was to create a function, getFirst(arr)
, that returns the first item in the array. This was challenging because it involved working with arrays.
Here’s how I solved it:
I started by creating the getFirst(arr)
function. Inside the function body, I used the return
statement to send back arr[0]
—the first element in the array.
Outside the function, I created a variable, firstCard
, which calls the getFirst
function and passes in an array [10, 5, 6]
as the argument.
Finally, when I console.log(firstCard)
, it outputs 10
, the first item in the array.
// Create a function, getFirst(arr), that returns the first item in the array
function getFirst(arr){
return arr[0]
}
// Call it with an array as an argument to verify that it works
let firstCard = getFirst([10, 5, 6])
console.log(firstCard)
Console
›10
"Spicing Up Your Code with Template Strings"
Simplifying string creation and adding style to your Chrome extension.
Template strings allow you to create HTML structure with back-ticks `` and simplify strings in Javascript , instead of ““ + concatenation and the code becoming very complicated to read.
Template strings simplify the readability of our code and we can break the code into multiple lines and nest it like HTML, you can also use variables and arrays with ${}
Example:
Below you can see how much template string simplify our readability of the code. its nicely nested and broken down in multiple lines similar to HTML code
// listItems += "<li><a target='_blank' href='" + myLeads[i] + "'>" + myLeads[i] + "</a></li>"
listItems += `
<li>
<a target='_blank' href='${myLeads[i]}'>
${myLeads[i]}
</a>
</li>
`
Example:
The refactored code below is I added the back-sticks `` and turned it into a template strings, and I removed all of the ““ and concatenation + the variable recipient I wrapped it with ${}
// template strings/literals
const recipient = "James"
// Refactor the email string to use template strings
//const email = "Hey " + recipient + "! How is it going? Cheers Per"
const email =` Hey ${recipient}! How is it going? Cheers Per`
console.log(email)
Console
›Hey James! How is it going? Cheers Per
Example:
In this challenge i created a new variable with the value my name “Moshtak“ and replaced Per with ${sender}
// template strings/literals
const sender = "Moshtak"
const recipient = "James"
// Create a new variable, sender, and set its value to your name
// Use your sender variable instead of "Per"
//const email = `Hey ${recipient}! How is it going? Cheers Per`
const email = `Hey ${recipient}! How is it going? Cheers ${sender}`
console.log(email)
Console
›Hey James! How is it going? Cheers Moshtak
“Saving Data Permanently with localStorage”
Why localStorage is essential for storing user data in extensions
localStorage acts like a mini-database within the browser, allowing you to save user preferences or settings. For instance, if a user enables dark mode, this preference can be saved in localStorage as a key-value pair. This ensures the settings persist even after the browser is refreshed.
localStorage is an object that stores data as key-value pairs. Since it communicates via strings, any non-string data (like arrays or objects) must be converted using JSON.stringify().
localStorage; my first localStorage:
// 1. Save a key-value pair in localStorage
// 2. Refresh the page. Get the value and log it to the console
// 3. Clear localStorage
// HINTS:
// localStorage.setItem(key, value)
localStorage.setItem("country", "UK");
// localStorage.getItem(key)
let myCountry = localStorage.getItem("country") //logs out UK
Console
›UK
// localStorage.clear()
localStorage.clear();
Console
›null
Local Storage: Storing Arrays
I learned that storing arrays in localStorage requires converting them into strings because localStorage only accepts strings. To store an array, you use JSON.stringify() to convert it into a string. Later, to retrieve and use the array, you use JSON.parse() to convert it back to its original form.
Challenge:
turning the myLeads string into an array I used the
JSON.parce()
method.I used the push() method of the array to push a new element into the myLeads array.
Turning the array back into a string I used the
JSON.stringify()
methodConfirming the string i used the
console.log(typeof)
passing in the myLeads to confirm its a string in the console.
let myLeads = `["www.awesomelead.com"]`
JSON.parse(myLeads)
myLeads.push("www.scrimba.com")
myLeads.JSON.stringify(myLeads)
console.log(myleads)
Console
›["www.awesomelead.com","wwww.scrimba.com"]
console.log(typeof myleads)
Console
›string
Local Storage: Retrieving Leads
In this challenge, I learned how to retrieve data from localStorage and store it in a variable.
1. I used localStorage.getItem("myLeads") to access the key-value pair stored under "myLeads".
2. To convert the retrieved string back into an array, I wrapped the result in JSON.parse().
3. Finally, I stored the converted array in a variable called leadsFromLocalStorage and logged it to the console to verify the data.
let leadsFromLocalStorage = JSON.parse(localStorage.getItem("myLeads"))
console.log(leadsFromLocalStorage)
Console
›["www.awesomelead.com", "www.epiclead.com"]
“Parsing and Storing Data Using the JSON Object”
How JSON simplifies handling complex data structures.
The JSON object provides two key methods: JSON.stringify() and JSON.parse().
• JSON.stringify() converts an array or object into a string, as localStorage only accepts strings.
• JSON.parse() converts the string back into its original array or object format when you need to work with the data again.
These methods make it easy to handle complex data structures when storing and retrieving them from places like localStorage.
let myArray = [1, 2, 3]; // Original array
// Convert the array to a JSON string
let jsonString = JSON.stringify(myArray);
console.log(jsonString); // Output: "[1,2,3]"
// Convert the JSON string back to an array
let parsedArray = JSON.parse(jsonString);
console.log(parsedArray); // Output: [1, 2, 3]
“Organizing Data Smartly: Working with Objects in Arrays”
The power of combining objects and arrays to structure data efficiently.
In web development, it’s common to store objects inside arrays. For example, the tabs array below contains an object with a key (url) and a value (the link). To access and log the url value, I used an addEventListener on a button (tabBtn). Inside the function, I accessed the first object in the array using tabs[0] and retrieved the url key with .url.
const tabs = [
{url: "https://v2.scrimba.com/home"}
]
tabBtn.addEventListener("click", function(){
console.log(tabs[0].url)
})
Console
›https://v2.scrimba.com/home
Building the chrome extension app:
HTML Code Breakdown:
In the HTML code, I created an input field with the type text and assigned it an id of input-el to interact with it using the DOM.
I added three buttons—“Save Input,” “Save Tab,” and “Delete All”—each with unique ids (input-btn, tab-btn, delete-btn)
to make them functional with JavaScript.
Lastly, I included a <ul>
element with an id of ul-el
to dynamically display items.
<html>
<head>
<link rel="stylesheet" href="index.css">
</head>
<body>
<input type="text" id="input-el">
<button id="input-btn">SAVE INPUT</button>
<button id="tab-btn">SAVE TAB</button>
<button id="delete-btn">DELETE ALL</button>
<ul id="ul-el">
</ul>
<script src="index.js"></script>
</body>
</html>
JavaScript Code Breakdown:
1. Initial Setup and Variables
• Purpose: Describe the variables and their roles in the program:
2. Restoring Saved Leads
• Purpose: Mention how the app restores previously saved leads:
3. Adding Leads via Input
• Purpose: Describe the event listener for the inputBtn:
4. Saving Active Tab URL
• Purpose: Explain how the tabBtn event listener works:
5. Rendering the Leads
• Purpose: Break down the render() function:
6. Clearing All Leads
• Purpose: Detail the functionality of the deleteBtn event listener:
1. Initial Setup and Variables
• Purpose: Describe the variables and their roles in the program:
The first variable, myLeads, is an array used to store the leads. The next five variables (inputEl, inputBtn, ulEl, deleteBtn, and tabBtn) connect specific HTML elements to JavaScript using the getElementById() method. Finally, leadsFromLocalStorage retrieves stored leads from localStorage using localStorage.getItem("myLeads"), and JSON.parse() converts the stored string back into an array.
let myLeads = []
const inputEl = document.getElementById("input-el")
const inputBtn = document.getElementById("input-btn")
const ulEl = document.getElementById("ul-el")
const deleteBtn = document.getElementById("delete-btn")
const tabBtn = document.getElementById("tab-btn")
const leadsFromLocalStorage = JSON.parse( localStorage.getItem("myLeads") )
2. Restoring Saved Leads
• Purpose: Mention how the app restores previously saved leads:
“I used an if conditional statement to check if there’s any data in leadsFromLocalStorage. If it exists, I set the myLeads array to the stored data and then called the render(myLeads) function, passing in the myLeads array to display the restored leads.”
if (leadsFromLocalStorage) {
myLeads = leadsFromLocalStorage
render(myLeads)
}
3. Adding Leads via Input
• Purpose: Describe the event listener for the inputBtn:
I created the inputBtn variable to connect the HTML button to the JavaScript functionality using the getElementById()
method. Then, I added an addEventListener to inputBtn
, which listens for a “click” and executes a function.
Inside the function, I used the push()
method to add the value from inputEl.value
(the text entered in the input field) to the myLeads array. After saving the data, I cleared the input field to keep the UI clean.
Next, I updated the saved data in localStorage using localStorage.setItem
. I passed in the myLeads array but first converted it into a string using the JSON.stringify()
method because localStorage only stores strings.
Finally, I called the render()
function and passed in the myLeads array. This step updates the list displayed in the browser, ensuring that all changes are immediately reflected for the user.
inputBtn.addEventListener("click", function() {
myLeads.push(inputEl.value)
inputEl.value = ""
localStorage.setItem("myLeads", JSON.stringify(myLeads) )
render(myLeads)
})
4. Saving Active Tab URL
• Purpose: Explain how the tabBtn event listener works:
This tabBtn event listener is for the “SAVE TAB” button. I created the tabBtn variable to connect the HTML button to the JavaScript code using the getElementById() method. Then, I added an addEventListener that listens for a “click” and executes a function.
This part of the code uses the Chrome API. The chrome object is the main interface, and tabs is a key that refers to browser tabs. The .query() method is used to fetch details about the active tab. Inside .query(), I passed an object { active: true, currentWindow: true }, which specifies that it should target the currently active tab in the current browser window. The method also takes a callback function with tabs as its parameter.
Within the callback function, I used the push() method to add the URL of the first element (tabs[0].url) from the tabs array to the myLeads array. Next, I updated localStorage using the setItem() method, passing in myLeads and converting it to a string with JSON.stringify() since localStorage only stores strings.
Finally, I called the render() function, passing in the updated myLeads array. This ensures the new tab URL is displayed in the browser for the user.
tabBtn.addEventListener("click", function(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
myLeads.push(tabs[0].url)
localStorage.setItem("myLeads", JSON.stringify(myLeads) )
render(myLeads)
})
})
5. Rendering the Leads
• Purpose: Break down the render() function:
The render()
function takes a parameter, leads, and dynamically generates HTML for the list of leads.
• First, I created a variable, listItems, and set it to an empty string since we’ll append <li>
elements to it later.
• Then, I used a for loop
to iterate through the leads array. Inside the loop, I updated listItems using the += operator
and a template string wrapped in backticks (``). This creates an HTML structure for each lead, including a clickable <a>
link with the URL from the leads[i]
array item.
• Finally, I updated the DOM using ulEl.innerHTML = listItems
, which replaces the contents of the <ul>
element with the dynamically generated list.
This way, every time render()
is called, the list is updated with clickable links for each lead stored in the array.
function render(leads) {
let listItems = ""
for (let i = 0; i < leads.length; i++) {
listItems += `
<li>
<a target='_blank' href='${leads[i]}'>
${leads[i]}
</a>
</li>
`
}
ulEl.innerHTML = listItems
}
6. Clearing All Leads
• Purpose: Detail the functionality of the deleteBtn event listener:
For clearing all leads, I created a deleteBtn variable and linked it to the corresponding button in the HTML using the DOM. I added an event listener to the deleteBtn variable, which listens for a double-click (dblclick) and then runs a function.
Inside the function:
1. I used localStorage.clear() to completely clear all stored data.
2. I reset the myLeads array to an empty array.
Since myLeads is now empty, the final step is to call the render() function and pass in the updated myLeads array. This ensures that all leads are removed from the displayed list.
deleteBtn.addEventListener("dblclick", function() {
localStorage.clear()
myLeads = []
render(myLeads)
})
Final Code:
let myLeads = []
const inputEl = document.getElementById("input-el")
const inputBtn = document.getElementById("input-btn")
const ulEl = document.getElementById("ul-el")
const deleteBtn = document.getElementById("delete-btn")
const tabBtn = document.getElementById("tab-btn")
const leadsFromLocalStorage = JSON.parse( localStorage.getItem("myLeads") )
if (leadsFromLocalStorage) {
myLeads = leadsFromLocalStorage
render(myLeads)
}
tabBtn.addEventListener("click", function(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
myLeads.push(tabs[0].url)
localStorage.setItem("myLeads", JSON.stringify(myLeads) )
render(myLeads)
})
})
function render(leads) {
let listItems = ""
for (let i = 0; i < leads.length; i++) {
listItems += `
<li>
<a target='_blank' href='${leads[i]}'>
${leads[i]}
</a>
</li>
`
}
ulEl.innerHTML = listItems
}
deleteBtn.addEventListener("dblclick", function() {
localStorage.clear()
myLeads = []
render(myLeads)
})
inputBtn.addEventListener("click", function() {
myLeads.push(inputEl.value)
inputEl.value = ""
localStorage.setItem("myLeads", JSON.stringify(myLeads) )
render(myLeads)
})
Here’s a quick summary of the chrome extension app:
1. Initial Setup and Variables
I created variables to store leads (myLeads) and connected specific HTML elements to JavaScript using getElementById(). I also used localStorage.getItem() and JSON.parse() to retrieve any saved leads and convert them back into an array.
2. Restoring Saved Leads
Using an if condition, I checked if there’s any data in leadsFromLocalStorage. If it exists, I assigned it to myLeads and called render(myLeads) to display the saved leads in the browser.
3. Adding Leads via Input
I added an addEventListener to inputBtn for the “click” event. Inside the function, I used push() to add the user’s input (inputEl.value) to the myLeads array, cleared the input field, saved the updated array to localStorage, and called render() to display the updated list.
4. Saving Active Tab URL
I created a tabBtn listener for the “SAVE TAB” button using the Chrome API. It grabs the URL of the active tab with chrome.tabs.query(), adds it to the myLeads array, saves it in localStorage, and calls render() to display the new lead.
5. Rendering the Leads
The render() function loops through the leads array, generating a list of clickable <li> elements with <a> tags for each lead. It then updates the DOM by setting ulEl.innerHTML to the generated list, ensuring the UI reflects the current leads.
6. Clearing All Leads
I added a dblclick listener to deleteBtn. It clears localStorage, resets myLeads to an empty array, and calls render() to remove all leads from the displayed list in the browser.
My Final project was creating a mobile app.
These are the topics I learned to build the app:
Part 1: Mastering Firebase Basics
1. Importing Firebase Essentials for Seamless Integration
2. Initializing Firebase: Your App’s Starting Point
3. Connecting the Dots: Setting Up Firebase Database References
4. Adding Data Made Simple with Firebase push()
5. Listening to Real-Time Updates with Firebase onValue()
Part 2: Handling Data and Dynamic Elements
6. Making Sense of Firebase Snapshots
7. Working with Firebase IDs for Unique Data Management
8. Streamlining Deletions with Firebase remove()
9. Transforming Objects into Arrays for Easy Access
10. Bringing Pages to Life with createElement()
11. Effortless Layouts Using Flexbox and flex-wrap
Part 3: Refining Layouts and User Experience
12. Perfect Spacing with Flexbox gap
13. Enhancing Usability with user-select
14. Optimizing Display with Viewport Settings
15. Adding a Favicon for a Polished Look
16. Making Your App Feel Native with a Web Application Manifest
Part 1: Mastering Firebase Basics
“Importing Firebase Essentials for Seamless Integration”
The import and export features in JavaScript allow you to connect multiple files so your code can run seamlessly across templates. For example, if you have a function in one file, you can call it in another.
Here’s a quick setup:
1. In functions.js, I created a function and exported it:
export function add(a,b) {
return a + b
}
2. In index.js, I imported the function:
import { add } from "../functions.js"
At first, this gave an error. To fix it, I added type="module" in the <script> tags in the HTML file because I’m working with multiple files:
<script src="functions.js" type="module"></script>
<script src="index.js" type="module"></script>
This setup allows me to use features across files and ensures everything runs smoothly and efficiently.
“Initialising Firebase: Your App’s Starting Point”
We applied the import concept to bring Firebase features into our project. For example, I imported the initializeApp function directly from Firebase’s CDN link, without needing to export it from another file:
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js"
Challenge: Importing getDatabase
I tackled a challenge to import getDatabase. Here’s how I imported it from Firebase’s CDN:
import { getDatabase } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-database.js"
These imports are the starting point for initialising and connecting Firebase features in our app.
“Connecting the Dots: Setting Up Firebase Database References”
The ref() function in Firebase lets you create references in the database. Unlike some functions, ref() doesn’t require a separate import—it’s included when you import from Firebase Database:
import { getDatabase, ref } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-database.js"
I used the ref() function to create a reference in the Firebase database. First, I created a database variable to store the initialized database using getDatabase(). Then, I created a shoppingListInDB variable using ref(), passing in the database variable and naming the reference "shoppingList".
const database = getDatabase(app)
const shoppingListInDB = ref(database, "shoppingList")
This setup links the Firebase database to our app, enabling us to organize and store data efficiently.
“Adding Data Made Simple with Firebase push()”
The push() function in Firebase makes it easy to add values to your database. To use it, I included push() in my imports:
import { getDatabase, ref, push } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-database.js";
In this setup, I used push() to send user input from an HTML form to the database. First, I connected the button (addButtonEl) and input field (inputFieldEl) to my JavaScript using getElementById(). Then, I added an event listener to the button to handle click events.
Inside the event listener’s function, I created an inputValue variable and assigned it the .value of the input field. This grabs whatever the user typed in. Using the push() function, I passed in shoppingListInDB (my database reference) and inputValue to store the input value in the "shoppingList" database.
Lastly, I logged a success message to the console using a template string to confirm that the input was successfully added. Here’s the complete code:
const inputFieldEl = document.getElementById("input-field")
const addButtonEl = document.getElementById("add-button")
addButtonEl.addEventListener("click", function() {
let inputValue = inputFieldEl.value
// Challenge: Use the Firebase function 'push' to push inputValue to the database
push(shoppingListInDB, inputValue)
console.log(`${inputValue} added to database`)
})
With this setup, any value entered in the input field is sent to the database and logged in the console as:
Console
›oranges added to database
“Listening to Real-Time Updates with Firebase onValue()”
The onValue() function is used to fetch database items in real time. You include it in your imports:
import { getDatabase, ref, push, onValue } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-database.js";
This function works similarly to addEventListener() as it also takes two arguments:
1. The database reference (e.g., shoppingListInDB), which points to the location in the database.
2. A callback function with a snapshot parameter, which retrieves data from the specified reference.
Challenge: Log all items in the database
First, call onValue() with shoppingListInDB as the reference and a callback function. Use snapshot.val() to retrieve the database content and log it to the console:
onValue(shoppingListInDB, function (snapshot) {
console.log(snapshot.val());
})
Console
{-NMV2WNiQDMkVmqKp8е6: "Oranges", -NMV6Y7xiFC6psy7CPTQ: "Chocolate", ...}
Final Challenge: Convert database items into an array
To work with the data as an array, use Object.values() to convert snapshot.val() (an object) into an array. Here’s the updated code:
onValue(shoppingListInDB, function (snapshot) {
let itemsArray = Object.values(snapshot.val())
console.log(itemsArray)
})
Console
›["Oranges", "Chocolate", "Red bull", "Bread", "Bread"]
Part 2: Handling Data and Dynamic Elements
“Making Sense of Firebase Snapshots”
“Making Sense of Firebase Snapshots”
In the onValue() function, the snapshot parameter lets you access data stored in the database.
• First argument: The reference to the database (e.g., shoppingListInDB) points to a specific location, created using the ref() function:
const shoppingListInDB = ref(database, "shoppingList");
• Second argument: A callback function with the snapshot parameter retrieves the data from the database.
To access the actual data, use the .val() method on the snapshot.
onValue(shoppingListInDB, function (snapshot) {
console.log(snapshot.val());
// Console Output:
// {
// "-NMV2WNiQDMkVmqKp8е6": "Oranges",
// "-NMV6Y7xiFC6psy7CPTQ": "Chocolate",
// "-NMV6wtqavmVYCFMKYSf": "Red bull",
// "-NMe9Rx8I7LSiElb_XHT": "Bread",
// "-NMixkfE_xifAzm2xUOR": "Coffee",
// "-NMixy0pVQspZHyUVFaL": "Solo"
// }
});
“Working with Firebase IDs for Unique Data Management”
In Firebase, each item in the database has a unique identifier (ID). To perform actions like deleting specific items, you need to access these IDs.
The Object.entries() method transforms an object into an array of key-value pairs. Each pair includes the ID (key) and the value, which is exactly what we need for managing unique items.
onValue(shoppingListInDB, function(snapshot) {
let itemsArray = Object.entries(snapshot.val()) // Switch to .entries for ID + value pairs
console.log(itemsArray)
clearShoppingListEl()
for (let i = 0; i < itemsArray.length; i++) {
appendItemToShoppingListEl(itemsArray[i])
}
})
[
["-NMV2WNiQDMkVmqKp8е6", "Oranges"],
["-NMV6Y7xiFC6psy7CPTQ", "Chocolate"],
["-NMV6wtqavmVYCFMKYSf", "Red bull"],
["-NMe9Rx8I7LSiElb_XHT", "Bread"],
["-NMixkfE_xifAzm2xUOR", "Coffee"],
["-NMixy0pVQspZHyUVFaL", "Solo"]
];
Challenge:
Create two variables, currentItemID and currentItemValue, and assign them the correct values from currentItem. Since currentItem is an array (key-value pair), use indices [0] and [1] to extract the ID and value.
onValue(shoppingListInDB, function (snapshot) {
let itemsArray = Object.entries(snapshot.val())
clearShoppingListEl()
for (let i = 0; i < itemsArray.length; i++) {
let currentItem = itemsArray[i]
let currentItemID = currentItem[0] // Extract ID
let currentItemValue = currentItem[1] // Extract value
appendItemToShoppingListEl(currentItem)
}
})
Confirmation:
To confirm the correct data is being processed, you can replace currentItem with currentItemID or currentItemValue in the appendItemToShoppingListEl() function, and the app will reflect the change.
appendItemToShoppingListEl(currentItemID) // Displays IDs
appendItemToShoppingListEl(currentItemValue) // Displays values
“Streamlining Deletions with Firebase remove()”
The remove() function in Firebase allows you to delete items directly from your database. To use it, first ensure remove is included in your imports:
import { getDatabase, ref, push, onValue, remove } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-database.js";
Challenge 1: Logging the ID of an Item
The goal is to log the unique ID of an item when clicked. By attaching an event listener to newEl, the item’s ID is printed to the console when triggered:
function appendItemToShoppingListEl(item) {
let itemID = item[0]
let itemValue = item[1]
let newEl = document.createElement("li")
newEl.textContent = itemValue
newEl.addEventListener("dbclick", function(){
console.log(itemID)
})
shoppingListEl.append(newEl)
}
Console
›-NMV6Y7xiFC6psy7CPTQ
Challenge 2: Removing an Item
To delete an item, follow these steps:
1. Reference the exact location: Create a variable that uses the ref() function to navigate to the item’s location in the database.
2. Call remove(): Use the reference variable with remove() to delete the item.
Here’s how the final implementation looks:
function appendItemToShoppingListEl(item) {
let itemID = item[0]
let itemValue = item[1]
let newEl = document.createElement("li")
newEl.textContent = itemValue
newEl.addEventListener("click", function () {
let exactLocationOfItemInDB = ref(database, `shoppingList/${itemID}`)
remove(exactLocationOfItemInDB)
})
shoppingListEl.append(newEl)
}
“Transforming Objects into Arrays for Easy Access”
Working with arrays is often easier than working with objects. JavaScript provides three methods to convert objects into arrays:
• Object.values(): Returns an array of the object’s values.
• Object.keys(): Returns an array of the object’s keys.
• Object.entries(): Returns an array of the object’s key-value pairs.
Here’s a practical example. Suppose we have an object with user IDs as keys and their emails as values:
let scrimbaUsers = {
"00": "sindre@scrimba.com",
"01": "per@scrimba.com",
"02": "frode@scrimba.com"
}
Challenge
Create three variables to extract:
1. Values (emails)
2. Keys (user IDs)
3. Key-Value Pairs
Here’s how each method works:
let scrimbaUsersEmails = Object.values(scrimbaUsers)
console.log(scrimbaUsersEmails)
// Console Output:
// ["sindre@scrimba.com", "per@scrimba.com", "frode@scrimba.com"]
let scrimbaUsersIDs = Object.keys(scrimbaUsers)
console.log(scrimbaUsersIDs)
// Console Output:
// ["00", "01", "02"]
let scrimbaUsersEntries = Object.entries(scrimbaUsers)
console.log(scrimbaUsersEntries)
// Console Output:
// [["00", "sindre@scrimba.com"], ["01", "per@scrimba.com"], ["02", "frode@scrimba.com"]]
“Bringing Pages to Life with createElement()”
The createElement() method is a powerful alternative to .innerHTML for dynamically adding HTML elements. It follows three key steps:
1. Create the element: Use createElement() to generate an HTML tag.
2. Add content: Use .textContent to populate the tag with text.
3. Append the element: Use .append() to attach the new tag to a parent element in the DOM (e.g., a <ul>).
In this example, I created a new <li> tag and appended it to an existing <ul> using the variable shoppingListEl:
function appendItemToShoppingListEl(itemValue) {
// Instead of using innerHTML
// shoppingListEl.innerHTML += `<li>${itemValue}</li>`
let newEl = document.createElement("li") // Step 1: Create the <li> tag
newEl.textContent = "Something" // Step 2: Add content to the <li>
shoppingListEl.append(newEl) // Step 3: Append the <li> to the <ul>
}
Challenge: Replace "Something" with the actual item value so the <li> displays dynamic content.
In the example, instead of hardcoding "Something" in the .textContent property, you can use the itemValue parameter to dynamically set the content of the <li> tag. This makes your code flexible and ensures that each item in your shopping list is displayed correctly.
Here’s how it would look:
function appendItemToShoppingListEl(itemValue) {
let newEl = document.createElement("li") // Step 1: Create the <li> tag
newEl.textContent = itemValue // Step 2: Add the dynamic item value
shoppingListEl.append(newEl) // Step 3: Append the <li> to the <ul>
}
“Effortless Layouts Using Flexbox and flex-wrap”
The flex-wrap property in Flexbox controls how flex items wrap within a container. It’s useful when you have multiple elements (like images) that you want to display in a flexible layout. There are three values for flex-wrap:
• wrap: Items wrap onto multiple lines (default).
• wrap-reverse: Items wrap onto multiple lines in reverse order.
• nowrap: Items stay on a single line, overflowing the container if necessary.
To use flex-wrap, first set the container to display: flex. Then, you can adjust the wrapping behavior based on your needs.
Example:
.thumbnails {
display: flex;
flex-wrap: wrap; /* Allows items to wrap onto multiple lines */
max-width: 560px;
margin: 50px auto;
}
.thumbnails {
display: flex;
flex-wrap: wrap-reverse; /* Reverses the wrapping order */
max-width: 560px;
margin: 50px auto;
}
.thumbnails {
display: flex;
flex-wrap: nowrap; /* Prevents wrapping, forces items on a single line */
max-width: 560px;
margin: 50px auto;
}
Part 3: Refining Layouts and User Experience
“Perfect Spacing with Flexbox gap”
The gap property in Flexbox controls the spacing between elements both horizontally and vertically. Unlike margin, which only applies to a single element at a time, gap allows you to manage the spacing between all elements in a container simultaneously, making development faster.
The syntax for gap is gap: [horizontal spacing] [vertical spacing]. For example, gap: 30px 20px sets the horizontal space between items to 30px and the vertical space to 20px.
Example:
.beatles {
display: flex;
max-width: 440px;
margin: 30px auto;
flex-wrap: wrap;
gap: 30px 20px; /* Horizontal space of 30px, vertical space of 20px */
}
“Enhancing Usability with user-select”
The user-select property controls whether users can select text or elements on the page. When set to user-select: none;, it prevents users from selecting content, which can be useful for interactive elements. However, it should be used sparingly and only on specific elements, not the entire body of the page.
Example:
element {
user-select: none; /* Disables text selection on this element */
}
“Optimizing Display with Viewport Settings”
As screen sizes evolved, web pages initially scaled down to fit new devices, leading to poor user experience. HTML5 addressed this with the viewport meta tag, which ensures web pages remain responsive across various screen sizes.
The key code to include in the <head> section of your HTML is:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
This allows your page to dynamically adjust to different devices without distortion, providing an optimal viewing experience.
Example:
<!doctype html>
<html>
<head>
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css">
</head>
<body>
<h1>Hello world</h1>
<script src="index.js"></script>
</body>
</html>
This is an excellent youtube video of this topic check out this link!
“Adding a Favicon for a Polished Look”
In this scrim I learned what favicons are they are these small icons you see in your browser tabs. There is a favicon service you can use its called favicon.io ← link
First you need a favicon image in PNG then head over to favicon.io and add your image to the converter you can drag and drop it. you will get a zip file copy these files and add them to your project and also copy the HTML code and paste in to the head of the HTML.
<!doctype html>
<html>
<head>
<title></title>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="stylesheet" href="index.css">
</head>
<body>
<h1>Favicon</h1>
<script src="index.js"></script>
</body>
</html>
“Making Your App Feel Native with a Web Application Manifest”
A web app manifest is a JSON file that provides metadata about a web application, much like the manifest.json in a Chrome extension project. It helps the browser display the app correctly when installed on a device.
Common properties in a web manifest include:
• name: The full name of the app.
• short_name: A shorter version of the app’s name for limited spaces (e.g., mobile screens).
• icons: The icons displayed for the app (e.g., home screen).
• theme_color: Sets the color of the browser toolbar when the app is open.
• background_color: Defines the background color for the splash screen.
• display: Specifies how the app should appear (e.g., fullscreen, standalone, or in a browser tab).
{
"name": "My App",
"short_name": "App",
"icons": [
{"src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png"},
{"src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png"}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
Building the mobile app:
HTML Code Breakdown:
1. HTML Structure & Meta Tags:
I will begin by explaining how the HTML5 document is structured and the purpose of the meta tags to optimise the page for different screen sizes (mobile, desktop, etc.).
The <!DOCTYPE html>
declares the document as HTML5, ensuring the browser renders it with the latest standards. The <meta name="viewport" content="width=device-width, initial-scale=1.0">
optimises the page for different screen sizes, making it responsive and user-friendly on both mobile and desktop devices.
2. Head Section:
I am going to describe the metadata, external resources like Normalize.css, Google Fonts, and the icons that help make the app look better and function across devices.
• Title: The <title> tag sets the text displayed on the browser tab when the page is loaded.
• External Styles: The Normalize.css library (<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.css">) ensures consistent styling across browsers.
• Fonts: The Google Fonts link (<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300&display=swap" rel="stylesheet">) imports the Rubik font for the app’s design.
• Icons: Favicon links add app icons for different devices. These were generated with favicon.io and included in the <head>.
• Web App Manifest: The manifest file (<link rel="manifest" href="/site.webmanifest">) provides metadata about the app, helping browsers display it correctly when installed, similar to a manifest.json in Chrome extensions.
3. Body Content:
I will walk through the key elements like the container div, the image, input field, button, and the shopping list. I will explain how these elements interact with each other, especially in terms of user input and dynamic updates.
Container Div:<div class="container"> —
A wrapper div that groups all elements together. This is typically used for styling purposes like centering or padding the content
Image Element:<img src="assets/cat.png"> —
Displays an image ( in this case, a cat) from the assets folder. This could be a decorative image or branding for your app.
Input Field:<input type="text" id="input-field" placeholder="Bread">
A text input where the user can type an item name (e.g. “bread”) to add to the shopping list. The id="input-field"
makes it accessible for Javascript functionality
Button to Add Item:<button id="add-button">Add to cart</button> —
A button that triggers the action of adding the item typed in the input field to the shopping list. The id="add-button"
allows the button to tbe accessed for event handling via Javascript.
Shopping List (Unordered List):<ul id="shopping-list"> —
An empty list initially, where the items added by the user will be dynamically displayed as <li>
elements through Javascript.
4. External JavaScript Link:
I will finish by highlighting how the index.js script is linked to the HTML file, making the page interactive by adding functionality to the button and shopping list.
External JavaScript:<script src="index.js" type="module"></script> —
This links to an external JavaScript file, which contains the logic for adding items to the shopping cart and updating the shopping list.
Type “module”: The type="module" attribute allows the use of JavaScript modules for cleaner, modular code. This makes it easier to manage and reuse JavaScript across multiple files.
Final Code:
<!doctype html>
<html>
<head><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.css">
<title>Add to Cart</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300&display=swap" rel="stylesheet">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="container">
<img src="assets/cat.png">
<input type="text" id="input-field" placeholder="Bread">
<button id="add-button">Add to cart</button>
<ul id="shopping-list">
</ul>
</div>
<script src="index.js" type="module"></script>
</body>
</html>
CSS Code Breakdown:
Global Styles
• Purpose: Sets the base styling for the entire page.
• html, body:
I removed the default margin and padding for consistent spacing. I also set the font to ‘Rubik’ for a modern look.
I choose a light background color (#EEF0F4) and a dark text color (#432000) for good contrast.
Container Styling
Purpose: Centrally aligns content and organises layout.
• .container
I turned the container into a flexbox using display: flex and set flex-direction: column to stack elements vertically.
I limited the width to 320px with max-width for a clean, mobile-friendly design, ensuring the <button>, <input>, and <ul> fit neatly.
Finally, I centered the container on the page with margin: 30px auto.
Image Styling
• Purpose: Centers the image and defines its size.
• img
I set the image width to 150px with the width property and used margin: 0 auto to center it horizontally on the page.
Input Styling
• Purpose: Creates a visually appealing and user-friendly input field.
• input
I defined the text and background colors for readability, using a soft gray for the background. I removed the default borders and added border-radius: 8px for smooth edges. To improve spacing, I used padding inside the field and margin: 15px to separate it from the button. The text is centered with text-align: center, and the font size is set to 20px for better readability.
Button Styling
• Purpose: Styles the button for visibility and enhances interactivity.
• button
I styled the button with white text (color: #FDFDFD) and a bold reddish background for visibility. The borders were removed, and border-radius: 8px was added for smooth edges. Text was centered using text-align, and a font size of 20px ensures clarity.
• button:hover
For interactivity, I changed the background color on hover and added cursor: pointer to indicate the button is clickable.
Unordered List Styling
• Purpose: Formats the shopping list and organizes items in a flexible grid layout.
• ul
I removed the default bullet points with list-style-type: none and eliminated extra spacing using padding: 0. To create a grid-like layout, I applied display: flex and enabled item wrapping with flex-wrap: wrap. Finally, I added consistent spacing between list items using gap: 10px.
List Item Styling
•Purpose: Styles individual list items for readability and interactive feedback.
• ul li
I styled the list items with a font size of 20px for clarity and a soft background color #FFFDF8 to match the design. Padding of 15px and border-radius: 8px give it a polished look, while flex-grow ensures items scale uniformly with the button and input field. I centered the text using text-align and added a subtle box-shadow for a 3D effect.
For interactivity, I added a hover effect that changes the background color and set cursor: pointer to indicate clickability.
Final Code:
html, body {
margin: 0;
padding: 0;
font-family: 'Rubik', sans-serif;
background-color: #EEF0F4;
color: #432000;
user-select: none;
}
.container {
display: flex;
flex-direction: column;
max-width: 320px;
margin: 30px auto;
}
img {
width: 150px;
margin: 0 auto;
}
input {
color: #432000;
background-color: #DCE1EB;
border: 0;
padding: 15px;
border-radius: 8px;
font-size: 20px;
text-align: center;
font-family: 'Rubik', sans-serif;
margin: 10px 0;
}
button {
color: #FDFDFD;
background-color: #AC485A;
border: 0;
padding: 15px;
border-radius: 8px;
font-size: 20px;
text-align: center;
font-family: 'Rubik', sans-serif;
}
button:hover {
background-color: #432000;
cursor: pointer;
}
ul {
list-style-type: none;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
ul li {
font-size: 20px;
background-color: #FFFDF8;
padding: 15px;
border-radius: 8px;
flex-grow: 1;
text-align: center;
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.2)
}
ul li:hover {
background-color: #FFECC7;
cursor: pointer;
}
JavaScript Code Breakdown:
1. Firebase Initialization and App Setup
• Explain the app settings and how Firebase is initialized.
2. DOM Elements and Event Listeners
• Explain the app settings and how Firebase is initialized.
3. Real-Time Data Updates
• Describe the use of onValue() to listen for changes in the database.
4. Rendering the Shopping List
• Walk through the logic for rendering items dynamically.
5. Clearing Input and List Elements
• Explain helper functions:
6. Adding and Removing Items
• Detail how items are added and removed:
7. Modular and Real-Time Features
• Conclude by summarizing the modularity and real-time nature of the app:
1. Firebase Initialization and App Setup
• Explain the app settings and how Firebase is initialized.
The first step is importing Firebase functions like initializeApp() from the Firebase app link.
I created an appSettings object, which holds my unique database URL as a key-value pair. Then, I used initializeApp(appSettings) to connect to Firebase, storing the result in the app variable.
To interact with the Realtime Database, I imported functions like getDatabase() and ref(). The getDatabase() function lets me access the database, and I assigned it to a database variable by passing in app.
The ref() function creates a reference to a specific path in the database. For example, I used it to create shoppingListInDB by referencing the “shoppingList” path: ref(database, "shoppingList"). This is where the shopping list data will be stored.
2. DOM Elements and Event Listeners
• Explain the app settings and how Firebase is initialized.
I used the getElementById() method to link 3 key DOM elements to JavaScript, assigning them to variables for clarity:
1. inputFieldEl: Captures the user’s input.
2. addButtonEl: Represents the “Add to Cart” button that triggers adding an item.
3. shoppingListEl: The container for displaying the shopping list.
To make the “Add to Cart” button interactive, I added an addEventListener() method to addButtonEl, which listens for “click” events and executes a function.
• Inside the function:
• I captured the user’s input using inputFieldEl.value and stored it in a variable, inputValue. The .value property retrieves the content from the input field.
• I then used Firebase’s push() function to add inputValue to the shoppingListInDB reference, saving the new item to the database.
This process connects user actions directly to Firebase, ensuring the shopping list updates dynamically and efficiently.
3. Real-Time Data Updates
• Describe the use of onValue() to listen for changes in the database.
The onValue() function works like an event listener but specifically listens for real-time changes in the Firebase database. I passed shoppingListInDB as the first argument and a callback function with snapshot as the parameter.
Inside the function:
• Checking for Data: I used an if...else statement to check if the snapshot contains data with snapshot.exists(). If data exists, I process it; otherwise, I set shoppingListEl.innerHTML to “No items here… yet” as a placeholder.
• Converting Data: I created a variable itemsArray and used Object.entries(snapshot.val()) to convert the snapshot data into an array. The Object.entries() method extracts key-value pairs from the snapshot object, making it easier to work with the data.
4. Rendering the Shopping List
• Walk through the logic for rendering items dynamically.
Rendering the Shopping List
1. Clearing the List to Avoid Duplicates:
At the start of onValue(), the clearShoppingListEl() function is called to remove any existing items from the list. This ensures the shopping list is refreshed each time there is an update, avoiding duplicate entries.
2. Iterating Over the Items:
A for loop goes through itemsArray, which contains key-value pairs retrieved from the database.
• currentItem stores the key-value pair at each index.
• currentItemID (the key) and currentItemValue (the value) are extracted using currentItem[0] and currentItem[1].
3. Passing Items for Rendering:
Each currentItem is passed to the appendItemToShoppingListEl() function, which handles rendering the item.
4. Understanding appendItemToShoppingListEl():
Inside this function:
• The item parameter (which was passed as currentItem) is broken into itemID and itemValue using item[0] and item[1].
• This confirms that when you call appendItemToShoppingListEl(currentItem), you’re correctly passing the key-value pair as an array, which is then processed within the function.
5. Clearing Input and List Elements
• Explain helper functions:
1. clearInputFieldEl():
This helper function is called inside the addEventListener() method. It resets the input field after an item is added by clearing its value.
2. clearShoppingListEl():
This function clears the entire shopping list at the start of the onValue() function. It ensures that old items are removed to prevent duplication when the list is updated dynamically.
6. Adding and Removing Items
• Detail how items are added and removed:
1. Adding Items:
The appendItemToShoppingListEl() function creates a new <li> element using document.createElement() and assigns it the item’s value for display.
2. Attaching Click Event for Deletion:
Each <li> has a unique itemID and is linked to its location in the database. This is stored in the variable exactLocationOfItemInDB.
3. Removing Items:
A click event listener is attached to each <li> that calls the remove() function. This function deletes the item from Firebase by passing exactLocationOfItemInDB as its argument.
7. Modular and Real-Time Features
• Conclude by summarizing the modularity and real-time nature of the app:
1. Real-Time Synchronization:
Firebase ensures data stays in sync across all users with the onValue() function, which listens for changes in the database and updates the UI in real time. This functionality allows users to see updates instantly, fostering a seamless experience. For example, in a collaborative setting, multiple users could add or remove items, and the changes would reflect in real-time for everyone using the app.
2. Modularity for Clarity and Maintainability:
Functions like appendItemToShoppingListEl() improve clarity by isolating specific tasks, such as appending items to the shopping list.
• Reusability: The function takes a single item as a parameter, enabling consistent and reusable logic for rendering list items.
• Organization: Inside the function, <li> elements are dynamically created and linked to their corresponding data using itemValue, keeping the code modular and clean.
• Interactivity: Each <li> is assigned an event listener for user actions, such as removing items, allowing the app to handle interaction without redundant code.
This modular structure, combined with Firebase’s real-time synchronization, results in an app that is both easy to maintain and user-friendly.
3. Scalability and User Experience:
The combination of real-time data updates and modular design makes the app scalable for future enhancements. For instance, adding new features like item categories, user-specific lists, or sorting functionality would not disrupt existing code. Additionally, the user-friendly interface, backed by real-time updates, ensures that interactions feel seamless and intuitive. Whether the app is used by a single person or in a collaborative environment, the dynamic updates provide a smooth and connected experience.
Final Code:
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js"
import { getDatabase, ref, push, onValue, remove } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-database.js"
const appSettings = {
databaseURL: "https://realtime-database-2b08f-default-rtdb.europe-west1.firebasedatabase.app/"
}
const app = initializeApp(appSettings)
const database = getDatabase(app)
const shoppingListInDB = ref(database, "shoppingList")
const inputFieldEl = document.getElementById("input-field")
const addButtonEl = document.getElementById("add-button")
const shoppingListEl = document.getElementById("shopping-list")
addButtonEl.addEventListener("click", function() {
let inputValue = inputFieldEl.value
push(shoppingListInDB, inputValue)
clearInputFieldEl()
})
onValue(shoppingListInDB, function(snapshot) {
if (snapshot.exists()) {
let itemsArray = Object.entries(snapshot.val())
clearShoppingListEl()
for (let i = 0; i < itemsArray.length; i++) {
let currentItem = itemsArray[i]
let currentItemID = currentItem[0]
let currentItemValue = currentItem[1]
appendItemToShoppingListEl(currentItem)
}
} else {
shoppingListEl.innerHTML = "No items here... yet"
}
})
function clearShoppingListEl() {
shoppingListEl.innerHTML = ""
}
function clearInputFieldEl() {
inputFieldEl.value = ""
}
function appendItemToShoppingListEl(item) {
let itemID = item[0]
let itemValue = item[1]
let newEl = document.createElement("li")
newEl.textContent = itemValue
newEl.addEventListener("click", function() {
let exactLocationOfItemInDB = ref(database, `shoppingList/${itemID}`)
remove(exactLocationOfItemInDB)
})
shoppingListEl.append(newEl)
}
Here’s a quick summary of how the mobile app was broken down:
1. Firebase Initialization and Setup:
The app connects to Firebase using initializeApp(), accesses the Realtime Database with getDatabase(), and references the shopping list path using ref().
2. DOM Elements and Event Listeners:
Key elements like the input field, button, and shopping list are linked using getElementById(). The “Add to Cart” button has a click event listener that captures input and pushes it to Firebase.
3. Real-Time Data Updates:
The onValue() function listens for database changes and updates the shopping list dynamically. Data is converted into an array using Object.entries() for easier processing.
4. Rendering the Shopping List:
A clearShoppingListEl() function prevents duplicate entries by clearing old data. A loop iterates through the items array, and each item is passed to the appendItemToShoppingListEl() function for rendering.
5. Clearing Input and List Elements:
Helper functions like clearInputFieldEl() reset the input field, and clearShoppingListEl() clears the list to maintain a clean state.
6. Adding and Removing Items:
The appendItemToShoppingListEl() function dynamically creates list items, assigns values, and attaches click listeners for removal. Firebase’s remove() function deletes items from the database.
7. Conclusion:
The app is modular and user-friendly, with Firebase ensuring real-time synchronization. Modular functions and clear logic make it maintainable, scalable, and ready for future enhancements.
I want to take a moment to express my deepest gratitude to my incredible tutors, Per Borgen and Rafid Hoda, for their unwavering support and guidance throughout this amazing free Scrimba JavaScript course. @perborgen and @rafidhoda, your passion for teaching and your dedication to making coding accessible to everyone have truly inspired me.
Through your clear explanations and engaging lessons, I’ve not only built my first app but also gained confidence in my ability to code—something I never thought possible. This course has been life-changing, and it’s all thanks to your efforts.
As I move forward, I’m excited to continue learning and exploring more courses with Scrimba. Thank you for making this journey so memorable and for believing in every student’s potential.
Here’s to the community you’ve built and the countless lives you’re changing—one line of code at a time!
Twitter link for Rafid Hoda
Twitter link for Per Borgen
With immense gratitude and admiration,
Mostackito👨🏻💻❤️