├── color-picker ├── script.js ├── index.html └── style.css ├── faq-accordion ├── index.html ├── style.css └── script.js ├── weather-app ├── index.html ├── style.css └── script.js ├── number-guessing-game ├── index.html ├── style.css └── script.js ├── typing-speed-test ├── index.html ├── style.css └── script.js ├── paint-app ├── index.html ├── style.css └── script.js ├── todo-list ├── index.html ├── style.css └── script.js ├── random-quote-generator ├── index.html ├── style.css └── script.js ├── quiz-app ├── index.html ├── style.css └── script.js ├── count-down-timer ├── index.html ├── style.css └── script.js ├── calculator ├── style.css ├── index.html └── script.js ├── carousel-image-slider ├── script.js ├── index.html └── style.css ├── audio-app ├── index.html ├── style.css └── script.js └── shopping-cart ├── index.html ├── styles.css └── main.js /color-picker/script.js: -------------------------------------------------------------------------------- 1 | document.getElementById("colorInput").addEventListener("input", function(event){ 2 | // Get the Selected color from input 3 | let selectedColor = event.target.value 4 | 5 | // Update the color text 6 | document.getElementById("colorCode").textContent = selectedColor 7 | 8 | // Update the background color of the display box 9 | document.getElementById("colorDisplay").style.backgroundColor = selectedColor 10 | 11 | 12 | }) -------------------------------------------------------------------------------- /faq-accordion/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | FAQ Accordion 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /color-picker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Color Picker App 7 | 8 | 9 | 10 |
11 |

Color Picker

12 |
13 | 14 |

Selected Color: #ffffff

15 |
16 |
17 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /weather-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Weather App 7 | 8 | 9 | 10 |
11 |

Weather App

12 |
13 |

Your Location

14 |

Temperature: -- °C

15 |

Description: --

16 |
17 | 18 | 19 |
20 | 21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /number-guessing-game/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Number Guessing Game 7 | 8 | 9 | 10 |
11 |

Number Guessing Game

12 |

Guess a number between 1 and 100 !

13 | 14 | 15 | 16 | 17 | 18 |

Good Luck! Start Guessing . . .

19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /typing-speed-test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Typing Speed Test 7 | 8 | 9 | 10 |
11 |

Typing Speed Test

12 |

The quick brown fox jumps over the lazy dog.

13 | 15 | 16 |
17 |

Time : 0 seconds

18 |

Words per Minute: 0 WPM

19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /weather-app/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | font-family: Arial, sans-serif; 3 | background-color: #87ceeb; 4 | margin: 0; 5 | padding: 0; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | height: 100vh; 10 | } 11 | 12 | .weather-container{ 13 | background-color: #ffffff; 14 | padding: 30px; 15 | border-radius: 10px; 16 | box-shadow: 0 8px 20px rgba(0,0,0,0.1); 17 | width: 90%; 18 | max-width: 400px; 19 | text-align: center; 20 | } 21 | 22 | h1{ 23 | font-size: 2.5em; 24 | color: #333; 25 | margin-bottom: 20px; 26 | } 27 | .weather-info{ 28 | margin-bottom: 20px; 29 | } 30 | .weather-info h2{ 31 | font-size: 1.8em; 32 | color: #555; 33 | } 34 | .weather-info p { 35 | font-size: 1.2em; 36 | color: #666; 37 | } 38 | .weather-icon { 39 | margin-top: 20px; 40 | } 41 | .weather-icon img { 42 | width: 100px; 43 | height: 100px; 44 | } -------------------------------------------------------------------------------- /paint-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Simple Paint App 7 | 8 | 9 | 10 |
11 | 12 |
13 | 16 | 19 | 20 | 23 |
24 | 25 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /paint-app/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Arial', sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | height: 100vh; 9 | background-color: #f0f0f0; 10 | } 11 | 12 | .paint-app{ 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | background-color: #ffffff; 17 | border: 1px solid #cccccc; 18 | box-shadow: 0 4px 8px rgba(0,0,0,0.1); 19 | border-radius: 8px; 20 | padding: 10px; 21 | } 22 | 23 | .toolbar{ 24 | display: flex; 25 | align-items: center; 26 | margin-bottom: 10px; 27 | gap: 10px; 28 | } 29 | 30 | .tool{ 31 | background-color: #eeeeee; 32 | border: none; 33 | border-radius: 4px; 34 | padding: 8px; 35 | cursor: pointer; 36 | } 37 | .tool.active{ 38 | background-color: #cccccc; 39 | } 40 | 41 | canvas{ 42 | border: 1px solid #000000; 43 | border-radius: 4px; 44 | background-color: #fff; 45 | } -------------------------------------------------------------------------------- /color-picker/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | font-family: 'Arial', sans-serif; 3 | background-color: #f0f0f0; 4 | margin: 0; 5 | padding: 0; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | height: 100vh; 10 | } 11 | .container{ 12 | text-align: center; 13 | background-color: #ffffff; 14 | padding: 30px; 15 | border-radius: 12px; 16 | box-shadow: 0 4px 10px rgba(0,0,0,0.1); 17 | max-width: 400px; 18 | width: 100%; 19 | } 20 | h1{ 21 | font-size: 2em; 22 | color: #333333; 23 | margin-bottom: 20px; 24 | 25 | } 26 | .color-picker{ 27 | margin-bottom: 20px; 28 | } 29 | #colorInput{ 30 | width: 80px; 31 | height: 50px; 32 | border: none; 33 | cursor: pointer; 34 | } 35 | p{ 36 | font-size: 1.2em; 37 | color: #555555; 38 | margin: 10px 0; 39 | } 40 | .color-display{ 41 | width: 100%; 42 | height: 150px; 43 | border-radius: 12px; 44 | margin-top: 20px; 45 | transition: background-color 0.3s ease; 46 | } -------------------------------------------------------------------------------- /todo-list/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | To-Do List 7 | 8 | 9 | 10 |
11 |

To-Do List App

12 | 13 | 14 | 17 |
18 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /random-quote-generator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Random Quote Generator 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

Random Quote Generator

15 | 16 |
17 | 18 |

Click the button below to generate a random quote

19 | 20 |

- Author

21 |
22 | 23 | 24 |
25 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /quiz-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Quiz App 7 | 8 | 9 | 10 |
11 |

Quiz App

12 | 13 |
14 |

Question text goes here

15 |
16 |
17 | 18 |
19 | 20 | 23 | 24 | 25 | 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /typing-speed-test/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | font-family: Arial, sans-serif; 3 | background-color: #f4f4f4; 4 | color: #333; 5 | text-align: center; 6 | margin: 0; 7 | padding: 0; 8 | } 9 | .container{ 10 | max-width: 600px; 11 | margin: 100px auto; 12 | padding: 20px; 13 | background-color: #fff; 14 | border-radius: 8px; 15 | box-shadow: 0 0 10px rgba(0,0,0,0.1); 16 | } 17 | 18 | h1{ 19 | margin-bottom: 20px; 20 | } 21 | 22 | #text-to-type{ 23 | font-size: 18px; 24 | margin-bottom: 20px; 25 | line-height: 1.5; 26 | } 27 | 28 | #user-input{ 29 | width: 100%; 30 | height: 100px; 31 | padding: 10px; 32 | font-size: 16px; 33 | border-radius: 4px; 34 | border: 1px solid #ccc; 35 | margin-bottom: 20px; 36 | } 37 | 38 | #start-button{ 39 | padding: 10px 20px; 40 | font-size: 16px; 41 | color: #fff; 42 | background-color: #007bff; 43 | border: none; 44 | border-radius: 5px; 45 | cursor: pointer; 46 | } 47 | #results{ 48 | margin-top: 20px; 49 | } 50 | #results p{ 51 | font-size: 18px; 52 | margin: 5px 0; 53 | } 54 | 55 | #timer span, #wpm span{ 56 | font-weight: bold; 57 | } 58 | 59 | .correct { 60 | color: green; 61 | } 62 | 63 | .incorrect{ 64 | color: red; 65 | } -------------------------------------------------------------------------------- /faq-accordion/style.css: -------------------------------------------------------------------------------- 1 | body, h1, p { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body{ 7 | font-family: 'Arial', sans-serif; 8 | background-color: #f7f7f7; 9 | display: flex; 10 | justify-content: center; 11 | align-items: center; 12 | height: 100vh; 13 | } 14 | 15 | .accordion-container{ 16 | background-color: #fff; 17 | border-radius: 8px; 18 | box-shadow: 0 4px 6px rgba(0,0,0,0.1); 19 | width: 100%; 20 | max-width: 600px; 21 | } 22 | 23 | .accordion-item{ 24 | border-bottom: 1px solid #ddd; 25 | } 26 | 27 | .accordion-header{ 28 | background-color: #dedede; 29 | color: black; 30 | cursor: pointer; 31 | padding: 15px; 32 | width: 100%; 33 | text-align: left; 34 | border: none; 35 | outline: none; 36 | font-size: 16px; 37 | transition: background-color 0.3s ease; 38 | } 39 | 40 | .accordion-header:hover{ 41 | background-color: #bababa; 42 | } 43 | 44 | .accordion-content{ 45 | background-color: #f4f4f4; 46 | display: none; 47 | overflow: hidden; 48 | padding: 0 15px; 49 | max-height: 0; 50 | transition: max-height 0.3s ease, padding 0.3s ease; 51 | } 52 | 53 | .accordion-content p { 54 | padding: 15px 0; 55 | color: #2c3e50; 56 | } 57 | 58 | .accordion-header.active + .accordion-content{ 59 | display: block; 60 | max-height: 150px; 61 | padding: 15px; 62 | } -------------------------------------------------------------------------------- /number-guessing-game/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | font-family: 'Arial', sans-serif; 3 | background-color: #f0f8ff; 4 | margin: 0; 5 | padding: 0; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | height: 100vh; 10 | } 11 | 12 | .game-container{ 13 | text-align: center; 14 | background-color: #ffffff; 15 | padding: 30px; 16 | border-radius: 10px; 17 | box-shadow: 0 8px 15px rgba(0,0,0,0.1); 18 | max-width: 400px; 19 | width: 90%; 20 | } 21 | 22 | h1 { 23 | font-size: 2em; 24 | color: #333333; 25 | margin-bottom: 20px; 26 | } 27 | input { 28 | padding: 10px; 29 | font-size: 1.2em; 30 | border: 2px solid #00aaff; 31 | border-radius: 5px; 32 | width: 80%; 33 | margin-bottom: 20px; 34 | box-sizing: border-box; 35 | } 36 | 37 | button{ 38 | background-color: #00aaff; 39 | color: white; 40 | padding: 10px 20px; 41 | font-size: 1em; 42 | border: none; 43 | border-radius: 5px; 44 | cursor: pointer; 45 | margin-top: 10px; 46 | transition: background-color 0.3s ease; 47 | } 48 | 49 | button:hover{ 50 | background-color: #0077cc; 51 | } 52 | #message{ 53 | font-size: 1.2em; 54 | color: #333333; 55 | margin-top: 20px; 56 | } 57 | 58 | #restartButton{ 59 | background-color: #28a745; 60 | } 61 | 62 | #restartButton:hover{ 63 | background-color: #218838; 64 | } -------------------------------------------------------------------------------- /count-down-timer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Countdown Timer 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

Countdown Timer

15 | 16 | 17 |
18 | 00 Days 19 | 00 Hours 20 | 00 Minutes 21 | 00 Seconds 22 |
23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /random-quote-generator/style.css: -------------------------------------------------------------------------------- 1 | /* Basic styling for the body */ 2 | body { 3 | font-family: 'Arial', sans-serif; 4 | background-color: #f5f5f5; 5 | margin: 0; 6 | padding: 0; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | height: 100vh; 11 | } 12 | .quote-container{ 13 | text-align: center; 14 | background-color: #ffffff; 15 | padding: 40px; 16 | border-radius: 12px; 17 | box-shadow: 0 8px 20px rgba(0,0,0,0.1); 18 | max-width: 600px; 19 | width: 90%; 20 | } 21 | 22 | /* Styling for the Title */ 23 | h1{ 24 | font-size: 2em; 25 | color: #333333; 26 | margin-bottom: 20px; 27 | } 28 | 29 | /* Styling for the box displaying the quote */ 30 | .quote-box{ 31 | padding: 20px; 32 | margin: 20px 0; 33 | background-color: #e0f7fa; 34 | border-radius: 8px; 35 | box-shadow: 0 4px 10px rgba(0,0,0,0.1); 36 | } 37 | /* Styling for the quote text */ 38 | #quote{ 39 | font-size: 1.5em; 40 | color: #00796b; 41 | margin-bottom: 10px; 42 | } 43 | /* Styling Author Name */ 44 | #author{ 45 | font-size: 1.2em; 46 | color: #004d40; 47 | font-style: italic; 48 | } 49 | 50 | button{ 51 | background-color: #05d9c0; 52 | color: whilte; 53 | padding: 12px 24px; 54 | border: none; 55 | border-radius: 8px; 56 | cursor: pointer; 57 | font-size: 1em; 58 | transition: background-color 0.3s ease; 59 | } 60 | 61 | /* Hover Effect for the button */ 62 | button:hover { 63 | background-color: #04b799; 64 | } -------------------------------------------------------------------------------- /quiz-app/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | font-family: 'Arial', sans-serif; 3 | background-color: #f5f5f5; 4 | margin: 0; 5 | padding: 0; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | height: 100vh; 10 | } 11 | 12 | .quiz-container{ 13 | background-color: #ffffff; 14 | padding: 20px; 15 | border-radius: 10px; 16 | box-shadow: 0 8px 10px rgba(0,0,0,0.1); 17 | width: 100%; 18 | max-width: 600px; 19 | text-align: center; 20 | } 21 | 22 | h1{ 23 | font-size: 2em; 24 | color: #333; 25 | margin-bottom: 20px; 26 | } 27 | 28 | .question-container{ 29 | background-color: #e0f7fa; 30 | padding: 15px; 31 | border-radius: 8px; 32 | margin-bottom: 20px; 33 | box-shadow: 0 4px 8px rgba(0,0,0,0.1); 34 | } 35 | 36 | .answer-buttons{ 37 | display: grid; 38 | gap: 10px; 39 | margin-bottom: 20px; 40 | } 41 | 42 | button{ 43 | padding: 10px; 44 | font-size: 1em; 45 | background-color: #00bcd4; 46 | color: white; 47 | border: none; 48 | border-radius: 5px; 49 | cursor: pointer; 50 | transition: background-color 0.3s ease; 51 | } 52 | button:hover{ 53 | background-color: #0097a7; 54 | } 55 | 56 | .next-btn, .restart-btn{ 57 | background-color: #ff9800; 58 | } 59 | 60 | .next-btn:hover, .restart-btn:hover{ 61 | background-color: #f57c00; 62 | } 63 | 64 | .result-container{ 65 | text-align: center; 66 | } 67 | 68 | #score{ 69 | font-size: 1.5em; 70 | margin-bottom: 20px; 71 | color: #333; 72 | } -------------------------------------------------------------------------------- /random-quote-generator/script.js: -------------------------------------------------------------------------------- 1 | // Array that store a list of quotes and their autho 2 | const quotes = [ 3 | { text: "The only way to do great work is to love what you do.", author: "Steve Jobs" }, 4 | { text: "Success is not final, failure is not fatal: It is the courage to continue that counts.", author: "Winston Churchill" }, 5 | { text: "Believe you can and you’re halfway there.", author: "Theodore Roosevelt" }, 6 | { text: "Act as if what you do makes a difference. It does.", author: "William James" }, 7 | { text: "You are never too old to set another goal or to dream a new dream.", author: "C.S. Lewis" }, 8 | { text: "Your time is limited, so don’t waste it living someone else’s life.", author: "Steve Jobs" } 9 | ]; 10 | 11 | // Get HTML elements by their id 12 | const quoteText = document.getElementById('quote') 13 | const quoteAuthor = document.getElementById('author') 14 | const newQuoteButton = document.getElementById('new-quote') 15 | 16 | // function to generate a random quote 17 | function generateQuote(){ 18 | // Get a random index from the quotes array 19 | const randomIndex = Math.floor(Math.random() * quotes.length) 20 | const randomQuote = quotes[randomIndex] 21 | 22 | // Update the quote text and author in the Element HTML 23 | quoteText.textContent = `"${randomQuote.text}"` 24 | quoteAuthor.textContent = `- ${randomQuote.author}` 25 | 26 | } 27 | 28 | // Add Event Listener for the button, 29 | newQuoteButton.addEventListener('click', generateQuote) 30 | 31 | // Call generate quote when page first loaded 32 | generateQuote() -------------------------------------------------------------------------------- /count-down-timer/style.css: -------------------------------------------------------------------------------- 1 | /* Basic Styling for the body */ 2 | body{ 3 | font-family: 'Aria', sans-serif; 4 | background-color: #f0f8ff; 5 | margin: 0; 6 | padding: 0; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | height: 100vh; 11 | } 12 | /* Main container for the timer */ 13 | .timer-container{ 14 | text-align: center; 15 | background-color: #ffffff; 16 | padding: 30px; 17 | border-radius: 10px; 18 | box-shadow: 0 8px 15px rgba(0,0,0,0.1); 19 | max-width: 500px; 20 | width: 90%; 21 | } 22 | 23 | /* Styling for the title */ 24 | h1{ 25 | font-size: 2.5em; 26 | color: #333333; 27 | margin-bottom: 20px; 28 | } 29 | 30 | /* Contdown display */ 31 | .countdown{ 32 | font-size: 2em; 33 | color: #333333; 34 | margin-bottom: 20px; 35 | } 36 | 37 | /* Styling for inputs and button */ 38 | .input-container{ 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | gap: 10px; 43 | } 44 | input{ 45 | padding: 10px; 46 | font-size: 1.2em; 47 | border: 2px solid #00aaff; 48 | border-radius: 5px; 49 | width: 80px; 50 | text-align: center; 51 | box-sizing: border-box; 52 | } 53 | 54 | button{ 55 | background-color: #00aaff; 56 | color: white; 57 | padding: 10px 20px; 58 | font-size: 1em; 59 | border: none; 60 | border-radius: 5px; 61 | cursor: pointer; 62 | transition: background-color 0.3s ease; 63 | } 64 | 65 | /* Styling for button Hover Effects */ 66 | button:hover{ 67 | background-color: #0077cc; 68 | } -------------------------------------------------------------------------------- /calculator/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | box-sizing: border-box; 3 | } 4 | 5 | body{ 6 | font-family: 'Roboto', sans-serif; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | height: 100vh; 11 | background-color: #f4f4f9; 12 | margin: 0; 13 | } 14 | 15 | .calculator{ 16 | width: 320px; 17 | background-color: #ffffff; 18 | border-radius: 12px; 19 | box-shadow: 0 10 20px rgba(0,0,0,0.1); 20 | padding: 20px; 21 | } 22 | .calculator-screen{ 23 | height: 80px; 24 | background-color: #252525; 25 | border-radius: 8px; 26 | display: flex; 27 | align-items: center; 28 | justify-content: center; 29 | padding-right: 20px; 30 | margin-bottom: 20px; 31 | } 32 | .calculator-display{ 33 | width: 100%; 34 | height: 100%; 35 | font-size: 36px; 36 | color: white; 37 | border: none; 38 | background-color: transparent; 39 | text-align: right; 40 | outline: none; 41 | } 42 | .calculator-keys{ 43 | display: grid; 44 | grid-template-columns: repeat(4,1fr); 45 | grid-gap: 10px; 46 | } 47 | 48 | button{ 49 | height: 60px; 50 | font-size: 24px; 51 | border-radius: 8px; 52 | border: none; 53 | background-color: #eaeaea; 54 | transition: background-color 0.3s ease; 55 | cursor: pointer; 56 | } 57 | button:hover{ 58 | background-color: #d4d4d4; 59 | } 60 | .operator{ 61 | background-color: #f1c40f; 62 | } 63 | .equal-sign{ 64 | background-color: #1e90ff; 65 | color: white; 66 | } 67 | .zero{ 68 | grid-column: span 2; 69 | } 70 | .all-clear{ 71 | background-color: #ff3b30; 72 | } -------------------------------------------------------------------------------- /carousel-image-slider/script.js: -------------------------------------------------------------------------------- 1 | // Get Element HTML 2 | 3 | const carouselSlide = document.querySelector('.carousel-slide') // container for all image 4 | 5 | const carouselImages = document.querySelectorAll('.carousel-slide img') 6 | 7 | const prevBtn = document.querySelector('.prev-btn') 8 | const nextBtn = document.querySelector('.next-btn') 9 | 10 | const indicators = document.querySelectorAll('.indicator') 11 | 12 | let currentIndex = 0 // state current index 13 | const totalImages = carouselImages.length 14 | 15 | let autoSlideInterval; // variable to save interval for auto slide 16 | 17 | function updateCarousel(){ 18 | carouselSlide.style.transform = `translateX(${-currentIndex *100}%)` 19 | 20 | //update active indicators 21 | indicators.forEach((indicator, index) => { 22 | indicator.classList.toggle('active', index === currentIndex) 23 | }) 24 | } 25 | 26 | function nextSlide(){ 27 | currentIndex = (currentIndex + 1) % totalImages 28 | updateCarousel() 29 | resetAutoSlide() 30 | } 31 | 32 | function prevSlide(){ 33 | currentIndex = (currentIndex - 1 + totalImages) % totalImages 34 | updateCarousel() 35 | resetAutoSlide() 36 | } 37 | 38 | function resetAutoSlide(){ 39 | clearInterval(autoSlideInterval) 40 | 41 | autoSlideInterval = setInterval(nextSlide, 5000) 42 | } 43 | 44 | // add event listener for next 45 | nextBtn.addEventListener('click', nextSlide) 46 | 47 | // add event listener for prev 48 | prevBtn.addEventListener('click', prevSlide) 49 | 50 | autoSlideInterval = setInterval(nextSlide, 5000) 51 | 52 | // add event listener for indicators 53 | 54 | indicators.forEach((indicator, index) => { 55 | indicator.addEventListener('click', () => { 56 | currentIndex = index 57 | updateCarousel() 58 | resetAutoSlide() 59 | }) 60 | }) 61 | 62 | -------------------------------------------------------------------------------- /audio-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Custom Audio Player with Playlist 7 | 8 | 9 | 10 |
11 |
12 |
13 | 24 |
25 |
26 |
27 |
28 |

No Track Selected

29 | 30 |
31 | 32 |
33 | 36 | 37 | 40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /calculator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Calculator Apps 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /shopping-cart/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Shopping Cart 7 | 8 | 9 | 10 | 11 |
12 |

My Store

13 |
14 | 🛒 0 15 |
16 |
17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 | 26 | 38 | 39 | 40 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /number-guessing-game/script.js: -------------------------------------------------------------------------------- 1 | let randomNumber = Math.floor(Math.random() * 100) + 1 2 | 3 | let attempts = 0 4 | 5 | // Get Element HTML 6 | const guessInput = document.getElementById('guessInput') 7 | const guessButton = document.getElementById('guessButton') 8 | const message = document.getElementById('message') 9 | const restartButton = document.getElementById('restartButton') 10 | 11 | function checkGuess() { 12 | const userGuess = Number(guessInput.value) 13 | // after check user guess, attempts ++ 14 | attempts++ 15 | 16 | if(userGuess === randomNumber){ 17 | message.textContent = `Congratulations! You guessed the Number ${randomNumber} correctly in ${attempts} attempts.` 18 | message.style.color = '#28a745' 19 | endGame() 20 | } else if (userGuess > randomNumber){ 21 | message.textContent = "Too high! Try Again." 22 | message.style.color = "#dc3545" 23 | } else if (userGuess < randomNumber){ 24 | message.textContent = "Too low! Try Again." 25 | message.style.color = "#dc3545" 26 | } 27 | 28 | guessInput = '' 29 | guessInput.focus() 30 | } 31 | 32 | function endGame(){ 33 | guessInput.disabled = true 34 | guessButton.disabled = true 35 | restartButton.style.display = 'inline' 36 | } 37 | 38 | function resetGame() { 39 | attempts = 0 40 | randomNumber = Math.floor(Math.random() * 100) + 1 41 | guessInput.disabled = false 42 | guessButton.disabled = false 43 | message.textContent = 'Good Luck! Start Guessing . . .' 44 | message.style.color = '#333333' 45 | restartButton.style.display = "none" 46 | guessInput.value = '' 47 | guessInput.focus() 48 | } 49 | 50 | guessButton.addEventListener('click', checkGuess) 51 | 52 | restartButton.addEventListener('click', resetGame) 53 | 54 | guessInput.addEventListener('keydown', function(event) { 55 | if (event.key === 'Enter'){ 56 | checkGuess() 57 | } 58 | }) -------------------------------------------------------------------------------- /weather-app/script.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const apiKey = '3c02d6c14753359e454ae7d2c9b6dce0'; 4 | 5 | // ambil element html yang diperlukan 6 | 7 | const locationElement = document.getElementById('location'); 8 | const temperatureElement = document.getElementById('temperature'); 9 | const descriptionElement = document.getElementById('description'); 10 | const weatherIconElement = document.getElementById('weather-icon'); 11 | 12 | // function untuk mendapatkan cuaca dari openweather 13 | async function getWeather(latitude, longitude) { 14 | const apiURL = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&units=metric&appid=${apiKey}`; 15 | 16 | try { 17 | const response = await fetch(apiURL); 18 | const data = await response.json(); 19 | 20 | locationElement.textContent = `${data.name}, ${data.sys.country}` 21 | temperatureElement.textContent = `Temperature: ${data.main.temp} °C` 22 | descriptionElement.textContent = `Description: ${data.weather[0].description}` 23 | 24 | const iconCode = data.weather[0].icon 25 | weatherIconElement.innerHTML = `Weather Icon` 26 | 27 | 28 | } catch (error) { 29 | console.error('Error fetching weather data: '); 30 | alert('Failed to fetch weather data. Please try again later'); 31 | } 32 | } 33 | 34 | function getLocation() { 35 | if (navigator.geolocation) { 36 | navigator.geolocation.getCurrentPosition((position) => { 37 | const latitude = position.coords.latitude 38 | const longitude = position.coords.longitude 39 | getWeather(latitude, longitude) 40 | }, error => { 41 | alert('Failed to get your location. Please Enable location services') 42 | }); 43 | } else { 44 | alert('Geolocation is not supported by your browser') 45 | } 46 | } 47 | 48 | // memanggil fungsi getLocation ketika halaman dimuat 49 | window.onload = getLocation 50 | -------------------------------------------------------------------------------- /paint-app/script.js: -------------------------------------------------------------------------------- 1 | // get element from the DOM 2 | const canvas = document.getElementById('canvas') 3 | const ctx = canvas.getContext('2d') 4 | 5 | const brushBtn = document.getElementById('brush') 6 | const eraserBtn = document.getElementById('eraser') 7 | const clearBtn = document.getElementById('clear') 8 | const colorPicker = document.getElementById('colorPicker') 9 | 10 | // set canvas 11 | canvas.width = 800 12 | canvas.height = 500 13 | 14 | // variable to store state of the tools 15 | let painting = false 16 | let erasing = false 17 | let currentColor = '#000000' 18 | let lineWidth = 5 19 | 20 | // function to startPainting 21 | function startPosition(e){ 22 | painting= true 23 | draw(e) // function to draw on canvas 24 | } 25 | 26 | // function to stop painting 27 | function endPosition(e) { 28 | painting = false 29 | ctx.beginPath() // start new path, 30 | } 31 | 32 | // function to draw on canvas 33 | function draw (e){ 34 | if(!painting) return 35 | 36 | ctx.lineWidth = lineWidth 37 | ctx.lineCap = 'round' 38 | ctx.strokeStyle = erasing ? '#ffffff' : currentColor 39 | 40 | ctx.lineTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop) 41 | ctx.stroke() 42 | ctx.beginPath() 43 | ctx.moveTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop) 44 | } 45 | 46 | function selectBrush() { 47 | erasing = false 48 | brushBtn.classList.add('active') 49 | eraserBtn.classList.remove('active') 50 | } 51 | 52 | function selectEraser(){ 53 | erasing = true; 54 | eraserBtn.classList.add('active') 55 | brushBtn.classList.remove('active') 56 | } 57 | 58 | function clearCanvas(){ 59 | ctx.clearRect(0,0, canvas.width, canvas.height) 60 | } 61 | 62 | function changeColor(e){ 63 | currentColor = e.target.value 64 | } 65 | 66 | // add event listener to each tool 67 | brushBtn.addEventListener('click', selectBrush) 68 | eraserBtn.addEventListener('click', selectEraser) 69 | clearBtn.addEventListener('click', clearCanvas) 70 | colorPicker.addEventListener('input', changeColor) 71 | 72 | canvas.addEventListener('mousedown', startPosition) 73 | canvas.addEventListener('mouseup', endPosition) 74 | canvas.addEventListener('mousemove', draw) -------------------------------------------------------------------------------- /carousel-image-slider/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Image Carousel App 7 | 8 | 9 | 10 |
11 |
12 |

Welcome to Project Carousel

13 |

Follow for more project

14 |
15 | 37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /typing-speed-test/script.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | // ambil element html yang diperlukan dari "ID" 4 | const textToTypeElement = document.getElementById('text-to-type'); 5 | const textToType = textToTypeElement.innerHTML.split(' '); 6 | const userInput = document.getElementById('user-input'); 7 | const startButton = document.getElementById('start-button'); 8 | const timeDisplay = document.getElementById('time'); 9 | const wpmDisplay = document.getElementById('words-per-minute'); 10 | 11 | console.log(textToType) 12 | 13 | let startTime; 14 | let timerInterval; 15 | 16 | // function to start test 17 | function startTest() { 18 | startTime = new Date(); 19 | userInput.value = ''; 20 | userInput.focus(); 21 | timerInterval = setInterval(updateTimer, 1000); 22 | textToTypeElement.innerHTML = textToType 23 | .map((word) => `${word}`) 24 | .join(' '); 25 | } 26 | 27 | function updateTimer() { 28 | const currentTime = new Date(); 29 | const elapsedTime = Math.floor((currentTime - startTime) / 1000); 30 | timeDisplay.innerText = elapsedTime; 31 | } 32 | 33 | function calculateWPM() { 34 | const wordsTyped = userInput.value.trim().split(/\s+/).length; 35 | const elapsedTime = Math.floor((new Date() - startTime) / 1000); 36 | const minutes = elapsedTime / 60; 37 | const wpm = Math.floor(wordsTyped / minutes); 38 | wpmDisplay.innerHTML = wpm; 39 | } 40 | 41 | function checkInput() { 42 | const typedText = userInput.value.trim().split(' '); 43 | const spans = textToTypeElement.querySelectorAll('span'); 44 | typedText.forEach((word, index) => { 45 | if (spans[index]) { 46 | if (word === textToType[index]) { 47 | spans[index].className = 'correct'; 48 | } else { 49 | spans[index].className = 'incorrect'; 50 | } 51 | } 52 | }); 53 | 54 | // hapus kelas jika pengguna menghapus tesk yang sudah diketik sebelumnya 55 | for (let i = typedText.length; i < spans.length; i++) { 56 | spans[i].className = ''; 57 | } 58 | } 59 | 60 | function stopTest() { 61 | clearInterval(timerInterval); 62 | calculateWPM(); 63 | } 64 | 65 | startButton.addEventListener('click', () => { 66 | startTest(); 67 | }); 68 | 69 | userInput.addEventListener('input', () => { 70 | checkInput(); 71 | const typedText = userInput.value; 72 | if (typedText.trim() === textToType.join(' ')) { 73 | stopTest(); 74 | } 75 | }); 76 | -------------------------------------------------------------------------------- /audio-app/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | font-family: 'Arial', sans-serif; 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | height: 100vh; 7 | margin: 0; 8 | background-color: #e0f7fa; 9 | } 10 | 11 | .container{ 12 | display: flex; 13 | background-color: #004d40; 14 | padding: 20px; 15 | border-radius: 10px; 16 | box-shadow: 0 4px 10px rgba(0,0,0,0.1); 17 | } 18 | 19 | .audio-player{ 20 | display: flex; 21 | flex-direction: column; 22 | justify-content: center; 23 | align-items: center; 24 | padding-right: 20px; 25 | border-right: 2px solid #ffeb3b; 26 | } 27 | .audio-controls { 28 | display: flex; 29 | align-items: center; 30 | width: 300px; 31 | } 32 | 33 | .play-button { 34 | background-color: #ffeb3b; 35 | border: none; 36 | padding: 10px; 37 | border-radius: 50%; 38 | cursor: pointer; 39 | margin-right : 10px; 40 | } 41 | 42 | .progress-container { 43 | flex-grow: 1; 44 | height: 10px; 45 | background-color: #00796b; 46 | border-radius: 5px; 47 | overflow: hidden; 48 | position: relative; 49 | } 50 | 51 | #progress-bar{ 52 | height: 100%; 53 | background-color: #ffeb3b; 54 | width: 0.5%; 55 | min-width: 2px; 56 | transition: width 0.1 ease; 57 | } 58 | 59 | #current-track { 60 | margin-top: 10px; 61 | color: #ffeb3b; 62 | } 63 | 64 | .audio-list{ 65 | padding-left: 20px; 66 | display: flex; 67 | flex-direction: column; 68 | } 69 | 70 | .upload-label{ 71 | display: block; 72 | background-color: #ffeb3b; 73 | color: #00796b; 74 | padding: 5px; 75 | text-align: center; 76 | border-radius: 5px; 77 | cursor: pointer; 78 | margin-bottom: 10px; 79 | font-weight: bold; 80 | } 81 | 82 | #audio-upload{ 83 | display: none; 84 | } 85 | 86 | #playlist{ 87 | height: 200px; 88 | overflow-y: auto; 89 | padding: 0; 90 | margin: 0; 91 | scrollbar-width: thin; 92 | scrollbar-color: rgba(255,255,255,0.5) transparent; 93 | } 94 | 95 | #playlist::-webkit-scrollbar{ 96 | width: 6px; 97 | } 98 | #playlist::-webkit-scrollbar-thumb{ 99 | background-color: rgba(255,255,255,0.5); 100 | border-radius: 10px; /* agar scollbar bulat */ 101 | } -------------------------------------------------------------------------------- /carousel-image-slider/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | box-sizing: border-box; 3 | } 4 | 5 | body{ 6 | font-family: 'Aria', sans-serif; 7 | margin: 0; 8 | padding: 0; 9 | background-color: #f0f0f0; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | height: 100vh; 14 | overflow: hidden; 15 | } 16 | 17 | .container{ 18 | display: flex; 19 | flex-direction: column; 20 | } 21 | 22 | header{ 23 | text-align: center; 24 | margin-bottom: 30px; 25 | } 26 | header h1{ 27 | color: #333; 28 | font-size: 2em; 29 | margin-bottom: 10px; 30 | } 31 | 32 | header h2{ 33 | color: #666; 34 | font-size: 1em; 35 | font-weight: 400; 36 | } 37 | 38 | .carousel-container { 39 | position: relative; 40 | max-width: 1000px; 41 | width: 100%; 42 | overflow: hidden; 43 | border-radius: 15px; 44 | box-shadow: 0 10px 30px rgba(0,0,0,0.1); 45 | } 46 | .carousel-slide{ 47 | display: flex; 48 | transition: transform 0.4s ease-in-out; 49 | } 50 | 51 | .carousel-slide img{ 52 | width: 100%; 53 | border-radius: 15px; 54 | } 55 | 56 | .prev-btn, .next-btn{ 57 | position: absolute; 58 | top: 50%; 59 | transform: translateY(-50%); 60 | background-color: rgba(0,0,0,0.5); 61 | color: white; 62 | border: none; 63 | font-size: 30px; 64 | cursor: pointer; 65 | padding: 10px; 66 | border-radius: 50%; 67 | z-index: 1; 68 | } 69 | 70 | .prev-btn{ 71 | left: 10px; 72 | } 73 | 74 | .next-btn{ 75 | right: 10px; 76 | } 77 | 78 | .prev-btn:hover, .next-btn:hover{ 79 | background-color: rgba(0,0,0,0.8); 80 | } 81 | 82 | .carousel-indicators { 83 | position: absolute; 84 | bottom: 20px; 85 | left: 50%; 86 | transform: translateX(-50%); 87 | display: flex; 88 | justify-content: center; 89 | gap: 10px; 90 | } 91 | .indicator{ 92 | width: 12px; 93 | height: 12px; 94 | background-color: rgba(255,255,255,0.5); 95 | border-radius: 50%; 96 | cursor: pointer; 97 | transition: background-color 0.3s ease; 98 | } 99 | 100 | .indicator.active{ 101 | background-color: rgba(255,255,255,0.9); 102 | } 103 | .indicator:hover{ 104 | background-color: rgba(255,255,255,0.7); 105 | } 106 | 107 | /* Responsive */ 108 | 109 | @media (max-width: 768px){ 110 | .prev-btn, .next-btn { 111 | font-size: 24px; 112 | padding: 8px; 113 | } 114 | .indicator { 115 | width: 10px; 116 | height: 10px; 117 | } 118 | } -------------------------------------------------------------------------------- /count-down-timer/script.js: -------------------------------------------------------------------------------- 1 | // Getting the HTML elements 2 | const countdownElement = document.getElementById('countdown') 3 | const daysElement = document.getElementById('days') 4 | const hoursElement = document.getElementById('hours') 5 | const minutesElement = document.getElementById('minutes') 6 | const secondsElement = document.getElementById('seconds') 7 | const inputHours = document.getElementById('inputHours') 8 | const inputMinutes = document.getElementById('inputMinutes') 9 | const inputSeconds = document.getElementById('inputSeconds') 10 | const startButton = document.getElementById('startButton') 11 | 12 | // varible countdownInterval 13 | let countdownInterval; 14 | 15 | // function to start the timer 16 | function startTimer(){ 17 | // Get the input values from user 18 | let hours = parseInt(inputHours.value) || 0 19 | let minutes = parseInt(inputMinutes.value) || 0 20 | let seconds = parseInt(inputSeconds.value) || 0 21 | 22 | // convert the total time into seconds 23 | let totalTimeInSeconds = hours * 3600 + minutes * 60 + seconds 24 | 25 | // if no time is input, stop the function 26 | if (totalTimeInSeconds <=0){ 27 | alert("Please enter a valid time.") 28 | return 29 | } 30 | // clear the input after the timer starts 31 | inputHours.value = '' 32 | inputMinutes.value = '' 33 | inputSeconds.value = '' 34 | 35 | // function to update the time display every seconds 36 | countdownInterval = setInterval(()=> { 37 | // calculate remaining days, hours, minutes, and seconds 38 | const days = Math.floor(totalTimeInSeconds/86400) 39 | const hours = Math.floor((totalTimeInSeconds % 86400) / 3600) 40 | const minutes = Math.floor((totalTimeInSeconds%3600)/60) 41 | const seconds = Math.floor((totalTimeInSeconds % 60)) 42 | 43 | //Update the HTML element display 44 | daysElement.textContent = days.toString().padStart(2,'0') 45 | hoursElement.textContent = hours.toString().padStart(2,'0') 46 | minutesElement.textContent = minutes.toString().padStart(2,'0') 47 | secondsElement.textContent = seconds.toString().padStart(2,'0') 48 | 49 | // Decrease the total time by one seconds 50 | totalTimeInSeconds-- 51 | 52 | // Stop the timer if time runs out 53 | if(totalTimeInSeconds < 0) { 54 | clearInterval(countdownInterval) 55 | alert("Time's up!") 56 | } 57 | 58 | }, 1000 //miliseconds 59 | ) 60 | } 61 | 62 | // add Event Listener for the start button 63 | startButton.addEventListener('click', ()=> { 64 | // stop any running timer 65 | clearInterval(countdownInterval) 66 | // start a new timer 67 | startTimer() 68 | }) -------------------------------------------------------------------------------- /faq-accordion/script.js: -------------------------------------------------------------------------------- 1 | // list faq 2 | const faqData = [ 3 | { 4 | question: "What is HTML?", 5 | answer: "HTML stands for HyperText Markup Language. It is the standard language for creating web pages." 6 | }, 7 | { 8 | question: "What is CSS?", 9 | answer: "CSS stands for Cascading Style Sheets. It is used to style and layout web pages." 10 | }, 11 | { 12 | question: "What is JavaScript?", 13 | answer: "JavaScript is a programming language that is used to create dynamic and interactive effects on web pages." 14 | } 15 | ]; 16 | 17 | // get accordion Container 18 | const accordionContainer = document.getElementById('accordion') 19 | 20 | // create function to generate faqData from array faqData 21 | 22 | function generateAccordionItems(faqData){ 23 | faqData.forEach(item => { 24 | const accordionItem = document.createElement('div') 25 | 26 | accordionItem.classList.add('accordion-item') 27 | 28 | //create element for header 29 | const header = document.createElement('button') 30 | header.classList.add('accordion-header') 31 | header.textContent = item.question 32 | 33 | //create element for content 34 | const content = document.createElement('div') 35 | content.classList.add('accordion-content') 36 | 37 | const contentText = document.createElement('p') 38 | contentText.textContent = item.answer 39 | 40 | // insert element to HTML 41 | content.appendChild(contentText) 42 | accordionItem.appendChild(header) 43 | accordionItem.appendChild(content) 44 | 45 | // add accordion item to accordion container 46 | accordionContainer.appendChild(accordionItem) 47 | }) 48 | } 49 | // call function generate faq 50 | generateAccordionItems(faqData) 51 | 52 | 53 | 54 | // get element accordion header 55 | const accordionHeaders = document.querySelectorAll('.accordion-header') 56 | 57 | // add event listener for accordion 58 | accordionHeaders.forEach(header => { 59 | header.addEventListener('click', () => { 60 | header.classList.toggle('active') 61 | const accordionContent = header.nextElementSibling 62 | 63 | if(header.classList.contains('active')) { 64 | accordionContent.style.maxHeight = accordionContent.scrollHeight + 'px' 65 | } else { 66 | accordionContent.style.maxHeight = 0 67 | } 68 | 69 | accordionHeaders.forEach(otherHeader => { 70 | if(otherHeader !== header && otherHeader.classList.contains('active')) { 71 | otherHeader.classList.remove('active') 72 | otherHeader.nextElementSibling.style.maxHeight = 0 73 | } 74 | }) 75 | }) 76 | }) 77 | -------------------------------------------------------------------------------- /shopping-cart/styles.css: -------------------------------------------------------------------------------- 1 | /* Styling dasar untuk body */ 2 | body { 3 | font-family: Arial, sans-serif; 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | /* Styling untuk header, termasuk cart icon */ 10 | header { 11 | display: flex; 12 | justify-content: space-between; 13 | align-items: center; 14 | padding: 20px; 15 | background-color: #333; 16 | color: white; 17 | } 18 | 19 | /* Styling untuk ikon cart */ 20 | .cart-icon { 21 | cursor: pointer; 22 | } 23 | 24 | /* Styling untuk bagian utama (main) */ 25 | main { 26 | padding: 20px; 27 | } 28 | 29 | /* Grid layout untuk daftar produk */ 30 | #product-list { 31 | display: grid; 32 | grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 33 | gap: 20px; 34 | } 35 | 36 | /* Styling untuk setiap kartu produk */ 37 | .product-card { 38 | border: 1px solid #ccc; 39 | border-radius: 5px; 40 | padding: 15px; 41 | text-align: center; 42 | } 43 | 44 | /* Menampilkan gambar produk dalam kartu produk */ 45 | img { 46 | max-width: 100%; 47 | height: auto; 48 | border-radius: 5px; 49 | } 50 | 51 | /* Styling untuk tombol */ 52 | button { 53 | padding: 10px 20px; 54 | margin-top: 10px; 55 | cursor: pointer; 56 | border: none; 57 | border-radius: 5px; 58 | background-color: #333; 59 | color: white; 60 | } 61 | 62 | /* Hover effect untuk tombol */ 63 | button:hover { 64 | background-color: #555; 65 | } 66 | 67 | /* Styling untuk modal */ 68 | .modal { 69 | display: none; 70 | position: fixed; 71 | z-index: 1; 72 | left: 0; 73 | top: 0; 74 | width: 100%; 75 | height: 100%; 76 | overflow: auto; 77 | background-color: rgba(0, 0, 0, 0.5); 78 | } 79 | 80 | /* Konten dalam modal */ 81 | .modal-content { 82 | background-color: white; 83 | margin: 10% auto; 84 | padding: 20px; 85 | border-radius: 5px; 86 | width: 80%; 87 | max-width: 500px; 88 | position: relative; 89 | } 90 | 91 | /* Tombol close untuk modal */ 92 | .close { 93 | position: absolute; 94 | top: 10px; 95 | right: 20px; 96 | color: black; 97 | font-size: 30px; 98 | font-weight: bold; 99 | cursor: pointer; 100 | } 101 | 102 | /* Hover effect untuk tombol close */ 103 | .close:hover { 104 | color: red; 105 | } 106 | 107 | /* Styling untuk daftar item dalam cart */ 108 | #cart-items { 109 | list-style: none; 110 | padding: 0; 111 | margin: 0; 112 | } 113 | 114 | /* Styling untuk total harga dalam cart */ 115 | .total-price { 116 | margin-top: 20px; 117 | font-size: 18px; 118 | font-weight: bold; 119 | text-align: right; 120 | } 121 | -------------------------------------------------------------------------------- /todo-list/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | font-family: 'Arial', sans-serif; 3 | background-color: #f0f4f8; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | height: 100vh; 8 | margin: 0; 9 | } 10 | .container{ 11 | background-color: white; 12 | border-radius: 10px; 13 | box-shadow: 0 10px 20px rgba(0,0,0,0.1); 14 | padding: 20px; 15 | width: 400px; 16 | } 17 | h1{ 18 | text-align: center; 19 | color: #333; 20 | } 21 | #new-task{ 22 | width: 80%; 23 | padding: 10px; 24 | margin-bottom: 20px; 25 | border: 1px solid #ddd; 26 | border-radius: 5px; 27 | outline: none; 28 | } 29 | #add-task-btn{ 30 | background: #28a745; 31 | padding: 10px 15px; 32 | color: white; 33 | border: none; 34 | cursor: pointer; 35 | border-radius: 5px; 36 | font-weight: bold; 37 | } 38 | #add-task-btn:hover{ 39 | background: #218838; 40 | } 41 | #todo-list{ 42 | list-style: none; 43 | padding: 0; 44 | } 45 | li{ 46 | display: flex; 47 | justify-content: space-between; 48 | align-items: center; 49 | padding: 10px; 50 | margin-bottom: 10px; 51 | background: #f8f9fa; 52 | border-radius: 5px; 53 | box-shadow: 0 2px 5px rgba(0,0,0,0.1); 54 | } 55 | li span{ 56 | 57 | flex: 1; 58 | font-size: 16px; 59 | } 60 | .buttons{ 61 | display: flex; 62 | gap: 5px; 63 | } 64 | button{ 65 | padding: 5px 10px; 66 | border: none; 67 | cursor: pointer; 68 | border-radius: 3px ; 69 | font-weight: bold; 70 | } 71 | .edit-btn{ 72 | background: #007bff; 73 | color: white; 74 | } 75 | .edit-btn:hover{ 76 | background: #0056b3; 77 | } 78 | .delete-btn{ 79 | background: #dc3545; 80 | color: white; 81 | } 82 | .delete-btn:hover{ 83 | background: #c83333; 84 | } 85 | .modal{ 86 | display: none; 87 | position: fixed; 88 | top: 0; 89 | left: 0; 90 | width: 100%; 91 | height: 100%; 92 | justify-content: center; 93 | align-items: center; 94 | background: rgba(0,0,0,0.5); 95 | 96 | } 97 | .modal-content{ 98 | background: white; 99 | padding: 20px; 100 | border-radius: 5px; 101 | width: 300px; 102 | text-align: center; 103 | } 104 | .modal input{ 105 | width: 80%; 106 | padding: 10px; 107 | margin-bottom: 10px; 108 | border: 1px solid #ddd; 109 | border-radius: 5px; 110 | outline: none; 111 | } 112 | .modal button{ 113 | padding: 10px 15px; 114 | margin: 5px; 115 | } 116 | .close-modal-btn{ 117 | background: #dc3545; 118 | color: white; 119 | border: none; 120 | cursor: pointer; 121 | border-radius: 5px; 122 | } 123 | .save-modal-btn{ 124 | background: #28a745; 125 | color: white; 126 | border: none; 127 | cursor: pointer; 128 | border-radius: 5px; 129 | } -------------------------------------------------------------------------------- /todo-list/script.js: -------------------------------------------------------------------------------- 1 | let currentEditElement = null 2 | 3 | document.addEventListener('DOMContentLoaded', () => { 4 | loadTasksFromLocalStorage() 5 | }) 6 | 7 | function loadTasksFromLocalStorage(){ 8 | let tasks = JSON.parse(localStorage.getItem('tasks')) || [] 9 | tasks.forEach(task => { 10 | const li = createTaskElement(task) 11 | document.getElementById('todo-list').appendChild(li) 12 | }) 13 | } 14 | 15 | function createTaskElement(task){ 16 | const li = document.createElement('li') 17 | const span = document.createElement('span') 18 | span.textContent = task 19 | 20 | const editBtn = document.createElement('button') 21 | editBtn.textContent = 'Edit' 22 | editBtn.classList.add('edit-btn') 23 | editBtn.onclick = () => openModal(li,span) 24 | 25 | const deleteBtn = document.createElement('button') 26 | deleteBtn.textContent = 'Delete' 27 | deleteBtn.classList.add('delete-btn') 28 | deleteBtn.onclick = () => { 29 | li.remove(); 30 | deleteTaskFromLocalStorage(task) 31 | } 32 | 33 | const buttons = document.createElement('div') 34 | buttons.classList.add('buttons') 35 | buttons.appendChild(editBtn) 36 | buttons.appendChild(deleteBtn) 37 | 38 | 39 | li.appendChild(span) 40 | li.appendChild(buttons) 41 | 42 | return li 43 | } 44 | function openModal(li,span){ 45 | currentEditElement = {li, span} 46 | document.getElementById('edit-task-input').value = span.textContent 47 | document.getElementById('editModal').style.display = 'flex' 48 | } 49 | 50 | function closeModal(){ 51 | document.getElementById('editModal').style.display = 'none' 52 | } 53 | 54 | function addTask(){ 55 | const taskInput = document.getElementById('new-task') 56 | const task = taskInput.value.trim() 57 | 58 | if(task){ 59 | const li = createTaskElement(task) 60 | document.getElementById('todo-list').appendChild(li) 61 | 62 | saveTaskToLocalStorage(task) 63 | taskInput.value = '' 64 | } 65 | } 66 | 67 | function saveTask(){ 68 | const editedTask = document.getElementById('edit-task-input').value.trim() 69 | if(editedTask){ 70 | const originalTask = currentEditElement.span.textContent 71 | currentEditElement.span.textContent = editedTask 72 | updateTaskInLocalStorage(originalTask, editedTask) 73 | closeModal() 74 | } 75 | } 76 | // handle localstorage 77 | function saveTaskToLocalStorage(task){ 78 | let tasks = JSON.parse(localStorage.getItem('tasks')) || [] 79 | tasks.push(task) 80 | localStorage.setItem('tasks', JSON.stringify(tasks)) 81 | } 82 | 83 | function deleteTaskFromLocalStorage(task){ 84 | let tasks = JSON.parse(localStorage.getItem('tasks')) || [] 85 | tasks = tasks.filter(t => t !== task) 86 | localStorage.setItem('tasks', JSON.stringify(tasks)) 87 | } 88 | 89 | function updateTaskInLocalStorage(originalTask, editedTask){ 90 | let tasks = JSON.parse(localStorage.getItem('tasks')) || [] 91 | const taskIndex = tasks.indexOf(originalTask) 92 | if(taskIndex > -1){ 93 | tasks[taskIndex] = editedTask 94 | } 95 | localStorage.setItem('tasks', JSON.stringify(tasks)) 96 | } -------------------------------------------------------------------------------- /audio-app/script.js: -------------------------------------------------------------------------------- 1 | // ambil audio jika ada di local storage 2 | 3 | let playlist = JSON.parse(localStorage.getItem('playlist')) || [] 4 | 5 | // ambil element dari dom, "ID" 6 | const audio = document.getElementById('audio') 7 | const playPauseButton = document.getElementById('play-pause') 8 | const playIcon = document.getElementById('play-icon') 9 | const pauseIcon = document.getElementById('pause-icon') 10 | const progressBar = document.getElementById('progress-bar') 11 | const playlistElement = document.getElementById('playlist') 12 | const audioUpload = document.getElementById('audio-upload') 13 | const currentTrack = document.getElementById('current-track') 14 | 15 | // fungsi untuk membuat list audio 16 | function createPlaylist(){ 17 | playlistElement.innerHTML = '' 18 | playlist.forEach((track, index) => { 19 | const li = document.createElement('li') 20 | li.textContent = track.title 21 | li.dataset.src = track.src 22 | 23 | li.addEventListener('click', () => { 24 | audio.src = track.src 25 | audio.play() 26 | updatePlayPauseIcon() 27 | 28 | document.querySelectorAll('#playlist li').forEach(item => item.classList.remove('active')) 29 | 30 | li.classList.add('active'); 31 | 32 | currentTrack.textContent = track.title 33 | }) 34 | 35 | playlistElement.appendChild(li) 36 | }); 37 | } 38 | 39 | function updatePlayPauseIcon(){ 40 | if(audio.paused){ 41 | playIcon.style.display = 'block' 42 | pauseIcon.style.display = 'none' 43 | } else { 44 | playIcon.style.display = 'none' 45 | pauseIcon.style.display = 'block' 46 | } 47 | } 48 | 49 | // fungsi untuk save playlist ke local storage 50 | function savePlaylist(){ 51 | localStorage.setItem('playlist', JSON.stringify(playlist)) 52 | } 53 | 54 | // inisialisasi playlist 55 | createPlaylist() 56 | 57 | // event listener untuk upload file audio 58 | audioUpload.addEventListener('change', (event) => { 59 | const files = Array.from(event.target.files) 60 | files.forEach(file => { 61 | const reader = new FileReader() 62 | reader.onload = function(e){ 63 | const track = { 64 | title: file.name, 65 | src: e.target.result 66 | } 67 | playlist.push(track) 68 | console.log(playlist) 69 | createPlaylist() 70 | savePlaylist() 71 | } 72 | reader.readAsDataURL(file) 73 | }) 74 | }) 75 | 76 | // event listener ke play pause 77 | playPauseButton.addEventListener('click', () => { 78 | if(audio.paused) { 79 | audio.play() 80 | } else { 81 | audio.pause() 82 | } 83 | updatePlayPauseIcon() 84 | }) 85 | 86 | // update progress bar saat audioo diputar 87 | audio.addEventListener('timeupdate', () => { 88 | const progress = (audio.currentTime / audio.duration) * 100 89 | progressBar.style.width = progress + '%' 90 | }) 91 | 92 | // reset ketika audio berakhir 93 | audio.addEventListener('ended', () => { 94 | updatePlayPauseIcon() 95 | progressBar.style.width = '0' 96 | audio.pause() 97 | playIcon.style.display = 'block' 98 | pauseIcon.style.display = 'none' 99 | }) -------------------------------------------------------------------------------- /calculator/script.js: -------------------------------------------------------------------------------- 1 | const calculator = { 2 | displayValue: '0', 3 | firstOperand: null, 4 | waitingForSecondOperand: false, 5 | operator: null 6 | } 7 | 8 | function updateDisplay(){ 9 | const display = document.querySelector('.calculator-display') 10 | display.value = calculator.displayValue 11 | } 12 | 13 | function inputDigit(digit){ 14 | const {displayValue, waitingForSecondOperand} = calculator 15 | 16 | if(waitingForSecondOperand === true){ 17 | calculator.displayValue = digit 18 | calculator.waitingForSecondOperand = false 19 | } else { 20 | calculator.displayValue = displayValue === '0' ? digit : displayValue + digit 21 | } 22 | updateDisplay() 23 | } 24 | 25 | function inputDecimal(dot){ 26 | if(!calculator.displayValue.includes(dot)){ 27 | calculator.displayValue += dot 28 | } 29 | } 30 | 31 | function handleOperator(nextOperator){ 32 | const {firstOperand, displayValue, operator} = calculator 33 | const inputValue = parseFloat(displayValue) 34 | 35 | if(operator && calculator.waitingForSecondOperand){ 36 | calculator.operator = nextOperator 37 | return 38 | } 39 | 40 | if(firstOperand === null && !isNaN(inputValue)){ 41 | calculator.firstOperand = inputValue 42 | } else if (operator){ 43 | const result = calculate(firstOperand, inputValue, operator) 44 | calculator.displayValue = `${parseFloat(result.toFixed(7))}` 45 | calculator.firstOperand = result 46 | } 47 | 48 | calculator.waitingForSecondOperand = true 49 | calculator.operator = nextOperator 50 | 51 | updateDisplay() 52 | } 53 | 54 | function calculate(firstOperand, secondOperand, operator){ 55 | if(operator === '+'){ 56 | return firstOperand + secondOperand 57 | } else if(operator === '-'){ 58 | return firstOperand - secondOperand 59 | } else if(operator === '*'){ 60 | return firstOperand * secondOperand 61 | } else if(operator === '/'){ 62 | return firstOperand / secondOperand 63 | } else if(operator === '√'){ 64 | return Math.sqrt(firstOperand) 65 | } else if (operator === '%') { 66 | return firstOperand / 100 67 | } 68 | 69 | return secondOperand 70 | } 71 | 72 | function resetCalculator(){ 73 | calculator.displayValue = '0' 74 | calculator.firstOperand = null; 75 | calculator.waitingForSecondOperand = false 76 | calculator.operator = null 77 | updateDisplay() 78 | } 79 | 80 | function handleEqual(){ 81 | const {firstOperand, displayValue, operator} = calculator 82 | const inputValue = parseFloat(displayValue) 83 | 84 | if(operator && !calculator.waitingForSecondOperand){ 85 | const result = calculate(firstOperand, inputValue, operator) 86 | calculator.displayValue = `${parseFloat(result.toFixed(7))}` 87 | calculator.firstOperand = null 88 | calculator.operator = null 89 | calculator.waitingForSecondOperand = false 90 | updateDisplay() 91 | } 92 | } 93 | 94 | document.querySelector('.calculator-keys').addEventListener('click', (event) => { 95 | const {target} = event 96 | 97 | if(!target.matches('button')){ 98 | return 99 | } 100 | 101 | if(target.classList.contains('operator')){ 102 | handleOperator(target.value) 103 | return 104 | } 105 | 106 | if(target.classList.contains('decimal')){ 107 | inputDecimal(target.value) 108 | return 109 | } 110 | 111 | if(target.classList.contains('all-clear')){ 112 | resetCalculator() 113 | return 114 | } 115 | 116 | if(target.classList.contains('equal-sign')){ 117 | handleEqual() 118 | return 119 | } 120 | 121 | inputDigit(target.value) 122 | 123 | }) -------------------------------------------------------------------------------- /quiz-app/script.js: -------------------------------------------------------------------------------- 1 | // array pertanyaan dan jawaban 2 | const questions = [ 3 | { 4 | question: "What is the capital of France?", 5 | answers: [ 6 | { text: "Paris", correct: true }, 7 | { text: "London", correct: false }, 8 | { text: "Rome", correct: false }, 9 | { text: "Berlin", correct: false } 10 | ] 11 | }, 12 | { 13 | question: "Who wrote 'To Kill a Mockingbird'?", 14 | answers: [ 15 | { text: "Harper Lee", correct: true }, 16 | { text: "Mark Twain", correct: false }, 17 | { text: "Ernest Hemingway", correct: false }, 18 | { text: "F. Scott Fitzgerald", correct: false } 19 | ] 20 | }, 21 | { 22 | question: "What is the largest planet in our solar system?", 23 | answers: [ 24 | { text: "Jupiter", correct: true }, 25 | { text: "Saturn", correct: false }, 26 | { text: "Earth", correct: false }, 27 | { text: "Mars", correct: false } 28 | ] 29 | }, 30 | { 31 | question: "Which language is primarily used for web development?", 32 | answers: [ 33 | { text: "JavaScript", correct: true }, 34 | { text: "Python", correct: false }, 35 | { text: "C++", correct: false }, 36 | { text: "Java", correct: false } 37 | ] 38 | } 39 | ]; 40 | 41 | let currentQuestionIndex = 0 42 | let score = 0 43 | 44 | // ambil semua element yang kita butuhkan dari "ID" 45 | const questionContainer = document.getElementById("question-container") 46 | const questionElement = document.getElementById("question") 47 | const answerButtons = document.getElementById("answer-buttons") 48 | const nextButton = document.getElementById("next-btn") 49 | const resultContainer = document.getElementById("result-container") 50 | const scoreElement = document.getElementById("score") 51 | const restartButton = document.getElementById("restart-btn") 52 | 53 | // buat fungsi untuk memulai quiz 54 | function startQuiz(){ 55 | currentQuestionIndex = 0 56 | score = 0 57 | nextButton.style.display = 'none' 58 | resultContainer.style.display = 'none' 59 | questionContainer.style.display = 'block' 60 | 61 | //function untuk menampilkan question 62 | showQuestion() 63 | } 64 | 65 | function showQuestion(){ 66 | // untuk mereset state 67 | resetState() 68 | 69 | const currentQuestion = questions[currentQuestionIndex] 70 | questionElement.textContent = currentQuestion.question 71 | 72 | // buat button untuk jawaban secara dinamis 73 | currentQuestion.answers.forEach(answer => { 74 | const button = document.createElement('button') 75 | button.textContent = answer.text 76 | button.classList.add('answer-btn') 77 | if(answer.correct){ 78 | button.dataset.correct = answer.correct 79 | } 80 | button.addEventListener('click', selectAnswer) 81 | answerButtons.appendChild(button) 82 | }) 83 | } 84 | 85 | function resetState(){ 86 | nextButton.style.display = 'none' 87 | while(answerButtons.firstChild){ 88 | answerButtons.removeChild(answerButtons.firstChild) 89 | } 90 | } 91 | 92 | function selectAnswer(e) { 93 | const selectedButton = e.target 94 | const correct = selectedButton.dataset.correct === 'true' 95 | if(correct) { 96 | score++ 97 | selectedButton.style.backgroundColor = '#4caf50' 98 | } else{ 99 | selectedButton.style.backgroundColor = '#f44336' 100 | } 101 | //disable all button after select answer 102 | Array.from(answerButtons.children).forEach(button => { 103 | button.disabled = true 104 | if(button.dataset.correct){ 105 | button.style.backgroundColor = '#4caf50' 106 | } 107 | }) 108 | 109 | // tampilkan next button jika pertanyaan masih ada 110 | if(currentQuestionIndex < questions.length - 1){ 111 | nextButton.style.display = 'inline-block' 112 | } else{ 113 | showResult() 114 | } 115 | } 116 | 117 | // handle event listener nextbtn 118 | 119 | nextButton.addEventListener('click', () => { 120 | currentQuestionIndex++ 121 | showQuestion() 122 | }) 123 | 124 | function showResult(){ 125 | questionContainer.style.display = 'none' 126 | resultContainer.style.display = 'block' 127 | scoreElement.textContent = `Your score : ${score} / ${questions.length}` 128 | } 129 | 130 | restartButton.addEventListener('click', startQuiz) 131 | 132 | 133 | 134 | 135 | 136 | startQuiz() -------------------------------------------------------------------------------- /shopping-cart/main.js: -------------------------------------------------------------------------------- 1 | class Product{ 2 | constructor(id,name,description, price, image){ 3 | this.id = id 4 | this.name = name 5 | this.description = description 6 | this.price = price 7 | this.image = image 8 | } 9 | 10 | displayCard(){ 11 | return ` 12 | ${this.name} 13 |

${this.name}

14 |

$${this.price.toFixed(2)}

15 |

Quantity in cart: 0

16 | 17 | 18 | 19 | ` 20 | } 21 | } 22 | 23 | class CartItem{ 24 | constructor(product, quantity =1){ 25 | this.product = product 26 | this.quantity = quantity 27 | } 28 | incrementQuantity(){ 29 | this.quantity ++ 30 | } 31 | getTotalPrice(){ 32 | return this.quantity * this.product.price 33 | } 34 | } 35 | 36 | class ShoppingCart{ 37 | constructor(){ 38 | this.products = [] 39 | this.cart = [] 40 | } 41 | addProduct(product){ 42 | this.products.push(product) 43 | } 44 | displayProducts(){ 45 | const productList = document.getElementById('product-list') 46 | productList.innerHTML = '' 47 | this.products.forEach(product => { 48 | const productCard = document.createElement('div') 49 | productCard.classList.add('product-card') 50 | productCard.innerHTML = product.displayCard() 51 | productList.appendChild(productCard) 52 | }) 53 | } 54 | 55 | viewProductDetail(id){ 56 | const product = this.products.find(p => p.id === id) 57 | document.getElementById('detail-image').src = product.image 58 | document.getElementById('detail-title').textContent = product.name 59 | document.getElementById('detail-description').textContent = product.description 60 | 61 | document.getElementById('detail-price').textContent = `$${product.price.toFixed(2)}` 62 | 63 | document.getElementById('add-to-cart-btn').setAttribute('onclick', `shoppingCart.addToCart(${product.id})`) 64 | 65 | this.toggleProductDetail() 66 | } 67 | 68 | toggleProductDetail(addFromDisplay = false){ 69 | const modal = document.getElementById('product-modal') 70 | modal.style.display = modal.style.display === 'block'? 'none' : "block" 71 | } 72 | 73 | addToCart(id){ 74 | const product = this.products.find(p=> p.id === id) 75 | const existingItem = this.cart.find(item => item.product.id === id) 76 | 77 | if (existingItem){ 78 | existingItem.incrementQuantity() 79 | } else { 80 | this.cart.push(new CartItem(product)) 81 | } 82 | 83 | this.updateCart() 84 | this.updateProductQuantity(id) 85 | } 86 | 87 | updateCart(){ 88 | const cartItems = document.getElementById('cart-items') 89 | cartItems.innerHTML = '' 90 | let totalPrice = 0 91 | 92 | this.cart.forEach((item,index)=> { 93 | const itemTotalPrice = item.getTotalPrice() 94 | totalPrice += itemTotalPrice 95 | 96 | const li = document.createElement('li') 97 | 98 | li.innerHTML = ` 99 | ${item.product.name} - $${item.product.price.toFixed(2)} x ${item.quantity} = $${itemTotalPrice.toFixed(2)} 100 | 101 | ` 102 | cartItems.appendChild(li) 103 | }) 104 | 105 | document.getElementById('cart-count').textContent = this.cart.reduce((sum,item) => sum + item.quantity,0) 106 | document.getElementById('total-price').textContent = totalPrice.toFixed(2) 107 | } 108 | 109 | updateProductQuantity(id){ 110 | const cartItem = this.cart.find(item => item.product.id === id) 111 | const quantityElement = document.getElementById(`quantity-${id}`) 112 | 113 | 114 | if(cartItem) { 115 | quantityElement.textContent = `Quantity in cart: ${cartItem.quantity}` 116 | } else { 117 | quantityElement.textContent = `Quantity in cart: 0` 118 | } 119 | } 120 | 121 | removeFromCart(index){ 122 | const removedItem = this.cart.splice(index,1)[0] 123 | this.updateCart() 124 | this.updateProductQuantity(removedItem.product.id) 125 | } 126 | 127 | toggleCart(){ 128 | const modal = document.getElementById('cart-modal') 129 | modal.style.display = modal.style.display === 'block' ? 'none': 'block' 130 | } 131 | } 132 | 133 | // inisialisasi shopping cart dan product 134 | const shoppingCart = new ShoppingCart() 135 | 136 | shoppingCart.addProduct( 137 | new Product( 138 | 1, 139 | 'Product 1', 140 | 'This is the description for Product 1', 141 | 10.0, 142 | 'https://images.unsplash.com/photo-1633455583769-4d8c28b50286?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fHw%3D' 143 | ) 144 | ); 145 | shoppingCart.addProduct( 146 | new Product( 147 | 2, 148 | 'Product 2', 149 | 'This is the description for Product 2', 150 | 20.0, 151 | 'https://images.unsplash.com/photo-1613140506142-277c6241b858?q=80&w=1964&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D' 152 | ) 153 | ); 154 | shoppingCart.addProduct( 155 | new Product( 156 | 3, 157 | 'Product 3', 158 | 'This is the description for Product 3', 159 | 30.0, 160 | 'https://images.unsplash.com/photo-1520170350707-b2da59970118?q=80&w=1965&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D' 161 | ) 162 | ); 163 | 164 | shoppingCart.displayProducts() 165 | 166 | window.onclick = function(event) { 167 | const cartModal = document.getElementById('cart-modal') 168 | const productModal = document.getElementById('product-modal') 169 | 170 | if(event.target == cartModal){ 171 | cartModal.style.display = "none" 172 | } 173 | if(event.target == productModal){ 174 | productModal.style.display = "none" 175 | } 176 | } 177 | --------------------------------------------------------------------------------