health-check domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/yogisv5/sitesbyyogi.com/wp-includes/functions.php on line 6260JavaScript Foundations β’ StreetGeek Academy
Combine all the concepts from previous modules to create a dynamic, data-driven, interactive portfolio website that responds to user input,
loads data automatically, and saves preferences locally.
Youβll take your existing portfolio site and upgrade it into a fully interactive application using modern JavaScript.
localStorage)
Make sure your project is clean and modular:
portfolio-site/
βββ index.html
βββ about.html
βββ projects.html
βββ contact.html
β
βββ css/
β βββ style.css
β
βββ js/
β βββ app.js
β βββ data.js
β βββ utils.js
β βββ theme.js
β
βββ images/
Create data.js:
const localProjects = [
{ title: "ShopBlocks WP", tech: "PHP / WooCommerce", link: "#" },
{ title: "Nfinite Dashboard", tech: "JavaScript / CSS Grid", link: "#" },
{ title: "BoldGrid Addon", tech: "WordPress / JavaScript", link: "#" }
];
Youβll display this data if GitHub data isnβt available.
In app.js:
async function loadProjects() {
const projectGrid = document.getElementById("project-grid");
projectGrid.innerHTML = "<p>β³ Loading projects...</p>";
try {
const response = await fetch("https://api.github.com/users/SitesByYogi/repos");
const repos = await response.json();
const projects = repos.slice(0, 6);
projectGrid.innerHTML = "";
projects.forEach(repo => {
const card = document.createElement("div");
card.className = "card";
card.innerHTML = `
<h3>${repo.name}</h3>
<p>${repo.language || "JavaScript"}</p>
<a href="${repo.html_url}" target="_blank">View Project</a>
`;
projectGrid.appendChild(card);
});
localStorage.setItem("repos", JSON.stringify(projects));
localStorage.setItem("lastUpdated", new Date().toLocaleString());
} catch (error) {
projectGrid.innerHTML = "<p>β οΈ Unable to fetch GitHub projects. Showing local data.</p>";
localProjects.forEach(p => {
const card = document.createElement("div");
card.className = "card";
card.innerHTML = `
<h3>${p.title}</h3>
<p>${p.tech}</p>
<a href="${p.link}">View</a>
`;
projectGrid.appendChild(card);
});
}
}
loadProjects();
In theme.js:
const toggleBtn = document.getElementById("theme-toggle");
const body = document.body;
// Apply saved preference
if (localStorage.getItem("theme") === "dark") {
body.classList.add("dark");
}
// Toggle mode
toggleBtn.addEventListener("click", () => {
body.classList.toggle("dark");
const mode = body.classList.contains("dark") ? "dark" : "light";
localStorage.setItem("theme", mode);
});
Add this near your header or hero area:
<p id="greeting"></p>
const greeting = document.getElementById("greeting");
const hour = new Date().getHours();
if (hour < 12) {
greeting.textContent = "βοΈ Good morning, StreetGeek!";
} else if (hour < 18) {
greeting.textContent = "π€οΈ Good afternoon, Developer!";
} else {
greeting.textContent = "π Good evening, Coder!";
}
Reuse the validated form logic from Module 8:
const form = document.getElementById("contactForm");
const status = document.getElementById("form-status");
form.addEventListener("submit", (e) => {
e.preventDefault();
const email = document.getElementById("email").value;
if (!email.includes("@") || !email.includes(".")) {
status.textContent = "β οΈ Invalid email!";
status.style.color = "red";
return;
}
status.textContent = "β
Message sent successfully!";
status.style.color = "green";
form.reset();
});
const cards = document.querySelectorAll(".card");
window.addEventListener("scroll", () => {
cards.forEach(card => {
const position = card.getBoundingClientRect().top;
if (position < window.innerHeight - 100) {
card.style.opacity = "1";
card.style.transform = "translateY(0)";
}
});
});
.card {
opacity: 0;
transform: translateY(50px);
transition: all 0.6s ease-out;
}
Add a βLast Updatedβ footer text:
<footer id="update-footer"></footer>
const footer = document.getElementById("update-footer");
const lastUpdated = localStorage.getItem("lastUpdated");
footer.textContent = lastUpdated
? `Last Updated: ${lastUpdated}`
: "No data fetched yet.";
| Feature | Description |
|---|---|
| π Search | Add a search bar that filters projects in real-time. |
| π¬ Modal | Create a modal popup for project details. |
| π§ Save State | Use localStorage to remember selected filter or tab. |
| β° Update Button | Add a button to re-fetch new data manually. |
| β‘ Lazy Loading | Only load visible cards for faster performance. |
1) What is the purpose of the async keyword?
2) How does localStorage differ from sessionStorage?
3) How can you show fallback data when a fetch request fails?
4) What does await do inside a function?
5) How can you persist user preferences like dark mode?
localStorage persistenceHost it on GitHub Pages or your VPS β share your live interactive portfolio with the world π.
Youβve officially completed JavaScript Foundations: From Static to Interactive.
You now understand the full front-end stack:
HTML + CSS + JavaScript β Dynamic, Professional Websites.
Next up β PHP Foundations: From Interactive Frontend to Dynamic Backend
Weβll connect your JavaScript-powered portfolio to a live database and server, creating full-stack applications that can store and serve real data.