Final Project: Mood Changer App
CHECK OUT my REVOLUTIONARY mood changer app! I've added 127 themes including "Radioactive Slime," "Unicorn Vomit," and "Existential Dread Pink!" Each one automatically changes your system clock, browser history, AND wifi password!
Um... I thought we were just making a simple theme switcher? You know, for the Chapter 6 final project? With like... five themes max?
DO YOU HAVE ANY IDEA how many SECURITY VULNERABILITIES you've introduced?! Changing system settings through a WEB APP?! This is EXACTLY how SkyNet gets admin privileges on nuclear launch systems! ONE CLICK on "Radioactive Slime" and we're ALL DOOMED!
Actually, if we analyze the requirements, we need just three things: multiple theme options, live preview, and saved preferences. I've prepared a 42-slide presentation on optimal theme implementation strategies based on user experience research...
Pfff... if you want a REAL mood changer, you need the Premium Experience™. My version has haptic feedback that literally makes your monitor vibrate when you select "Earthquake Mode." That's what separates the basic users from the PREMIUM ones.
According to the WCAG accessibility guidelines, all our themes must maintain a minimum contrast ratio of 4.5:1 for normal text. Also, I've drafted a 37-page user agreement for the preference saving feature to ensure GDPR compliance...
In my day, we had ONE theme: green text on a black screen. And if you wanted to save a preference, you wrote it down ON PAPER. Let me show you how to build a simple, functional mood changer without all this nonsense.
FINE! But I'm keeping my "Unicorn Vomit" theme. It took me THREE HOURS to get the gradient just right!
Final Project Overview
Mood Changer App Requirements:
- Multiple theme options - Create different color schemes users can select
- Live preview - Show theme changes in real-time
- Save preference - Remember the user's theme choice for future visits
In this final project, we'll bring together everything we've learned about JavaScript events to create an interactive mood changer app. This practical application will demonstrate how to manipulate the DOM, handle events, and save user preferences.
Let's break this down into small, manageable steps. The first thing we need is a set of themes with different colors and styles.
Interactive Demo
Try it yourself:
Mood Changer App
Welcome to your personalized space!
This is a sample content area where you can see how your theme affects text and layout. Different themes change the colors, fonts, and overall mood of your application.
Your theme preference will be saved automatically.
Notice how the theme changes are applied immediately when you click a button? That's our live preview requirement. Now let's look at how to implement these features.
Building the Mood Changer App
Step 1: Create the HTML Structure
First, we need to set up the basic HTML structure for our app:
<div class="mood-changer-app" id="moodChanger"> <div class="app-header"> <h2 class="app-title">Mood Changer App</h2> </div> <div class="app-body"> <div class="app-controls"> <button class="theme-btn" data-theme="cyberpunk">Cyberpunk</button> <button class="theme-btn" data-theme="pastel">Pastel</button> <button class="theme-btn" data-theme="forest">Forest</button> <button class="theme-btn" data-theme="ocean">Ocean</button> <button class="theme-btn" data-theme="sunset">Sunset</button> </div> <div class="sample-content"> <h3 class="content-header">Welcome to your space!</h3> <p>This is where you can see your theme in action.</p> </div> </div> </div>
Notice how we're using data-theme attributes on our buttons? That's a great way to store which theme each button should apply!
Step 2: Define Theme Styles with CSS Variables
CSS variables (custom properties) make it easy to switch themes by changing just a few values:
/* Theme Variables in CSS */ :root { /* Default theme variables */ --theme-bg: #05080f; --theme-text: white; --theme-primary: #18e6ff; --theme-secondary: #b266ff; --theme-accent: #ff71ce; --theme-border: rgba(24, 230, 255, 0.3); } /* Cyberpunk theme */ .theme-cyberpunk { --theme-bg: #05080f; --theme-text: white; --theme-primary: #18e6ff; --theme-secondary: #b266ff; --theme-accent: #ff71ce; --theme-border: rgba(24, 230, 255, 0.3); } /* Pastel theme */ .theme-pastel { --theme-bg: #f0f4f8; --theme-text: #5a5a5a; --theme-primary: #a5b4fc; --theme-secondary: #fda4af; --theme-accent: #c084fc; --theme-border: rgba(165, 180, 252, 0.3); } /* Additional themes defined similarly... */ /* Apply theme variables to elements */ .app-header { background-color: var(--theme-bg); color: var(--theme-text); border-bottom: 1px solid var(--theme-border); transition: all 0.3s ease; } .app-title { color: var(--theme-primary); transition: all 0.3s ease; } /* More styled elements using these variables... */
CSS variables are perfect for theme switching! Notice how we define the variables once, then reference them throughout the stylesheet. Now we only need to change the class on our container element to apply an entire theme.
Don't forget my UNICORN VOMIT theme! It needs at LEAST seven gradient layers and animated sparkles!
Let's stick with these five themes for now. Adding the transition property ensures smooth theme changes instead of jarring flashes.
Step 3: Add JavaScript for Theme Switching
Now we need to add event listeners to our theme buttons:
// JavaScript for theme switching document.addEventListener('DOMContentLoaded', function() { // Get references to our elements const moodChanger = document.getElementById('moodChanger'); const themeButtons = document.querySelectorAll('.theme-btn'); // Add click event listeners to all theme buttons themeButtons.forEach(function(button) { button.addEventListener('click', function() { // Get the theme name from the button's data attribute const theme = this.getAttribute('data-theme'); // Apply the theme by changing the class applyTheme(theme); }); }); // Function to apply a theme function applyTheme(theme) { // Remove all existing theme classes moodChanger.classList.remove('theme-cyberpunk', 'theme-pastel', 'theme-forest', 'theme-ocean', 'theme-sunset'); // Add the selected theme class moodChanger.classList.add(`theme-${theme}`); // Save the user's preference (we'll implement this next) saveThemePreference(theme); } });
AT LEAST you're removing the old theme classes before adding new ones! One small security blessing in this OCEAN OF VULNERABILITY! Still doesn't protect against MALICIOUS CSS INJECTION though!
Using template literals for the class name is clean, but you should always validate user inputs if accepting custom themes. This implementation is fine since we're using fixed theme options.
Step 4: Save User Preferences with localStorage
To remember the user's theme choice between visits, we'll use localStorage:
// Save theme preference to localStorage function saveThemePreference(theme) { // Store the theme name in localStorage localStorage.setItem('preferredTheme', theme); } // Load saved theme preference on page load function loadSavedTheme() { // Try to get the saved theme from localStorage const savedTheme = localStorage.getItem('preferredTheme'); // If a theme was saved, apply it if (savedTheme) { applyTheme(savedTheme); // Optional: Update UI to show the active theme const activeButton = document.querySelector(`.theme-btn[data-theme="${savedTheme}"]`); if (activeButton) { activeButton.classList.add('active'); } } } // Call this function when the page loads document.addEventListener('DOMContentLoaded', function() { // Set up event listeners like before... // Load the saved theme preference loadSavedTheme(); });
localStorage is like a free version of my Premium Cloud Syncing Service™. Limited, but gets the basic job done. For only $29.99 per month, you could store user preferences across ALL devices...
localStorage is more than enough for this project. It's simple, doesn't require a server, and works across page refreshes. Just remember it's specific to the domain and browser.
Step 5: Final Touches and Best Practices
Let's improve our code with some final touches:
// Complete Mood Changer implementation with best practices const MoodChanger = { // Store all our elements and state elements: { container: null, buttons: null }, themes: ['cyberpunk', 'pastel', 'forest', 'ocean', 'sunset'], currentTheme: 'cyberpunk', // Default theme // Initialize the app init: function() { // Get DOM elements this.elements.container = document.getElementById('moodChanger'); this.elements.buttons = document.querySelectorAll('.theme-btn'); if (!this.elements.container) { console.error('Mood changer container not found!'); return; } // Load saved theme first this.loadSavedTheme(); // Set up event listeners this.bindEvents(); console.log('Mood Changer initialized!'); }, // Set up all event listeners bindEvents: function() { const self = this; // Store reference to use in event callbacks this.elements.buttons.forEach(function(button) { button.addEventListener('click', function() { const theme = this.getAttribute('data-theme'); self.applyTheme(theme); }); }); }, // Apply a theme to the app applyTheme: function(theme) { // Validate the theme name if (!this.themes.includes(theme)) { console.error(`Unknown theme: ${theme}`); return; } // Remove all existing theme classes this.themes.forEach(themeName => { this.elements.container.classList.remove(`theme-${themeName}`); }); // Add the new theme class this.elements.container.classList.add(`theme-${theme}`); this.currentTheme = theme; // Update active button state this.updateActiveButton(); // Save to localStorage this.saveThemePreference(); // Show feedback to the user console.log(`Theme changed to: ${theme}`); }, // Update which button appears active updateActiveButton: function() { // Remove active class from all buttons this.elements.buttons.forEach(button => { button.classList.remove('active'); }); // Add active class to current theme's button const activeButton = document.querySelector(`.theme-btn[data-theme="${this.currentTheme}"]`); if (activeButton) { activeButton.classList.add('active'); } }, // Save current theme to localStorage saveThemePreference: function() { localStorage.setItem('preferredTheme', this.currentTheme); }, // Load saved theme from localStorage loadSavedTheme: function() { const savedTheme = localStorage.getItem('preferredTheme'); if (savedTheme && this.themes.includes(savedTheme)) { this.applyTheme(savedTheme); } } }; // Initialize when the DOM is ready document.addEventListener('DOMContentLoaded', function() { MoodChanger.init(); });
This object-oriented approach is much cleaner! Notice how we've organized the code into methods, added error checking, and made it more maintainable.
Activity: Build Your Own Mood Changer
Now it's your turn to build a Mood Changer app! Use the code examples and concepts you've learned to create your own version with at least three different themes.
Complete the following steps:
Create an HTML file with:
- A container for your mood changer app
- Theme selection buttons
- Content areas that will change with each theme
Don't forget to link your CSS and JavaScript files!
In your CSS file:
- Create CSS variables for each theme
- Define theme classes that change these variables
- Apply the variables to your page elements
- Add transitions for smooth theme changes
In your JavaScript file:
- Select your theme buttons and container
- Add click event listeners to the buttons
- Create a function to apply the selected theme
- Test that themes change when buttons are clicked
Enhance your JavaScript to:
- Save the selected theme to localStorage
- Load the saved theme when the page loads
- Make sure the active theme button is highlighted
Final testing and enhancements:
- Test in different browsers
- Make sure the theme is saved between page refreshes
- Add visual feedback when a theme is selected
- Consider adding theme descriptions or preview images
Challenge Extensions:
- Allow users to create and save their own custom themes
- Add animations when switching between themes
- Create a theme that changes based on the time of day
- Add a random theme button that selects a random theme
Final Approval
Well I STILL think we should add my "Unicorn Vomit" theme, but I guess this will do. The localStorage part is pretty cool though, not gonna lie.
I suppose it's MARGINALLY secure enough to pass inspection. But I'll be monitoring for any suspicious theme-related activities!
My user research indicates an 89% satisfaction rate with this implementation. The code structure is clean and the theme transitions are smooth. I approve.
I actually understood all of this code! That's a first for me. It's simple but effective.
I've reviewed the terms of service implications and can confirm that storing theme preferences falls within acceptable user data practices. Legally compliant!
It's... serviceable. Not PREMIUM quality, but it gets the job done. I suppose not everything needs to be gold-plated.
Clean, functional, and to the point. It does exactly what it needs to without unnecessary complications. I approve. Now let's call in the boss.
WOOF! WOOF!
*tail wagging intensifies*
CODE IS COMMITTED!
Chapter 6 Summary
Congratulations! You've completed Chapter 6: JavaScript Events – Click, Hover & Fun Reactions! In this chapter, you learned how to:
- Understand and navigate the Document Object Model (DOM)
- Select elements using getElementById and querySelector
- Change content dynamically with innerHTML and textContent
- Handle click events with onclick and event listeners
- Work with mouse events like onmouseover and onmouseout
- Modify element styles through JavaScript
- Toggle classes to create dynamic effects
- Select and manipulate multiple elements
- Use advanced event listeners for complex interactions
- Build a complete Mood Changer app that remembers user preferences
You're now ready to move on to Chapter 7, where you'll learn about drawing with JavaScript using the Canvas API!