26 |
27 |
--------------------------------------------------------------------------------
/44-45-modal/before/script.js:
--------------------------------------------------------------------------------
1 | /*
2 | TODO: 2. Select the elements with the following IDs
3 | * modal
4 | * open-modal-btn
5 | * close-modal-btn
6 | * BONUS: overlay
7 | */
8 |
9 | // TODO: 3. Create a click event listener for the open-modal-btn that adds the class "open" to the modal
10 | // BONUS: Also add the class "open" to the overlay
11 |
12 | // TODO: 4. Create a click event listener for the close-modal-btn that removes the class "open" from the modal
13 | // BONUS: Also remove the class "open" from the overlay
14 |
15 | // BONUS: Add a click event listener to the overlay that removes the class "open" from the modal and the overlay
16 |
--------------------------------------------------------------------------------
/44-45-modal/after/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Modal
5 |
6 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
23 |
24 |
--------------------------------------------------------------------------------
/68-google-maps-clone/script.js:
--------------------------------------------------------------------------------
1 | const MAPBOX_ACCESS_TOKEN =
2 | "pk.eyJ1Ijoid2ViZGV2c2ltcGxpZmllZCIsImEiOiJja2dyYTRqbW0weWl1MnJxaWF2dGR0ZHMwIn0.lU-OINCILi52P5N98qMbtA"
3 |
4 | navigator.geolocation.getCurrentPosition(successLocation, errorLocation, {
5 | enableHighAccuracy: true
6 | })
7 |
8 | function setupMap(centerPosition) {
9 | const map = new mapboxgl.Map({
10 | accessToken: MAPBOX_ACCESS_TOKEN,
11 | container: "map",
12 | style: "mapbox://styles/mapbox/streets-v11",
13 | center: centerPosition,
14 | zoom: 15
15 | })
16 |
17 | const navigationControls = new mapboxgl.NavigationControl()
18 | map.addControl(navigationControls)
19 |
20 | const directionControls = new MapboxDirections({
21 | accessToken: MAPBOX_ACCESS_TOKEN
22 | })
23 | map.addControl(directionControls, "top-left")
24 | }
25 |
26 | function successLocation(position) {
27 | setupMap([position.coords.longitude, position.coords.latitude])
28 | }
29 |
30 | function errorLocation() {
31 | setupMap([-2.24, 53.48])
32 | }
33 |
--------------------------------------------------------------------------------
/68-google-maps-clone/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 | Document
16 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/44-45-modal/after/script.js:
--------------------------------------------------------------------------------
1 | /*
2 | TODO: 2. Select the elements with the following IDs
3 | * modal
4 | * open-modal-btn
5 | * close-modal-btn
6 | * BONUS: overlay
7 | */
8 | const modal = document.querySelector("#modal")
9 | const openModalButton = document.querySelector("#open-modal-btn")
10 | const closeModalButton = document.querySelector("#close-modal-btn")
11 | const overlay = document.querySelector("#overlay")
12 |
13 | // TODO: 3. Create a click event listener for the open-modal-btn that adds the class "open" to the modal
14 | // BONUS: Also add the class "open" to the overlay
15 | openModalButton.addEventListener("click", () => {
16 | modal.classList.add("open")
17 | overlay.classList.add("open")
18 | })
19 |
20 | // TODO: 4. Create a click event listener for the close-modal-btn that removes the class "open" from the modal
21 | // BONUS: Also remove the class "open" from the overlay
22 | closeModalButton.addEventListener("click", closeModal)
23 |
24 | // BONUS: Add a click event listener to the overlay that removes the class "open" from the modal and the overlay
25 | overlay.addEventListener("click", closeModal)
26 |
27 | function closeModal() {
28 | modal.classList.remove("open")
29 | overlay.classList.remove("open")
30 | }
31 |
--------------------------------------------------------------------------------
/74-shopping-cart/after/items.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "name": "Red",
5 | "category": "Primary Color",
6 | "priceCents": 1600,
7 | "imageColor": "F00"
8 | },
9 | {
10 | "id": 2,
11 | "name": "Yellow",
12 | "category": "Primary Color",
13 | "priceCents": 2100,
14 | "imageColor": "FF0"
15 | },
16 | {
17 | "id": 3,
18 | "name": "Blue",
19 | "category": "Primary Color",
20 | "priceCents": 1200,
21 | "imageColor": "00F"
22 | },
23 | {
24 | "id": 4,
25 | "name": "Orange",
26 | "category": "Secondary Color",
27 | "priceCents": 1800,
28 | "imageColor": "F60"
29 | },
30 | {
31 | "id": 5,
32 | "name": "Green",
33 | "category": "Secondary Color",
34 | "priceCents": 1600,
35 | "imageColor": "0F0"
36 | },
37 | {
38 | "id": 6,
39 | "name": "Purple",
40 | "category": "Secondary Color",
41 | "priceCents": 2100,
42 | "imageColor": "60F"
43 | },
44 | {
45 | "id": 7,
46 | "name": "Light Gray",
47 | "category": "Grayscale",
48 | "priceCents": 1200,
49 | "imageColor": "AAA"
50 | },
51 | {
52 | "id": 8,
53 | "name": "Dark Gray",
54 | "category": "Grayscale",
55 | "priceCents": 1600,
56 | "imageColor": "333"
57 | }
58 | ]
--------------------------------------------------------------------------------
/74-shopping-cart/before/items.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "name": "Red",
5 | "category": "Primary Color",
6 | "priceCents": 1600,
7 | "imageColor": "F00"
8 | },
9 | {
10 | "id": 2,
11 | "name": "Yellow",
12 | "category": "Primary Color",
13 | "priceCents": 2100,
14 | "imageColor": "FF0"
15 | },
16 | {
17 | "id": 3,
18 | "name": "Blue",
19 | "category": "Primary Color",
20 | "priceCents": 1200,
21 | "imageColor": "00F"
22 | },
23 | {
24 | "id": 4,
25 | "name": "Orange",
26 | "category": "Secondary Color",
27 | "priceCents": 1800,
28 | "imageColor": "F60"
29 | },
30 | {
31 | "id": 5,
32 | "name": "Green",
33 | "category": "Secondary Color",
34 | "priceCents": 1600,
35 | "imageColor": "0F0"
36 | },
37 | {
38 | "id": 6,
39 | "name": "Purple",
40 | "category": "Secondary Color",
41 | "priceCents": 2100,
42 | "imageColor": "60F"
43 | },
44 | {
45 | "id": 7,
46 | "name": "Light Gray",
47 | "category": "Grayscale",
48 | "priceCents": 1200,
49 | "imageColor": "AAA"
50 | },
51 | {
52 | "id": 8,
53 | "name": "Dark Gray",
54 | "category": "Grayscale",
55 | "priceCents": 1600,
56 | "imageColor": "333"
57 | }
58 | ]
--------------------------------------------------------------------------------
/54-55-quiz/before/script.js:
--------------------------------------------------------------------------------
1 | /*
2 | TODO: 2. Select all elements needed
3 | * The form element (has the id `quiz-form`)
4 | * The answer inputs (have the class `answer`)
5 | * BONUS: The questions (have the class `question-item`)
6 | * BONUS: The alert (has the id `alert`)
7 | */
8 |
9 | // TODO: 3. Create a submit event listener for the form that does the following.
10 | // 1. Prevent the default behaviour
11 | // 2. Get all selected answers (use the `checked` property on the input to determine if it is selected or not)
12 | // 3. Loop through the selected answer to see if they are correct or not (Check the value of the answer to see if it is the string "true")
13 | // 4. For each correct answer add the class `correct` to the parent with the class `question-item` and remove the class `incorrect`.
14 | // 5. For each incorrect answer add the class `incorrect` to the parent with the class `question-item` and remove the class `correct`.
15 | // 6. BONUS: Make sure unanswered questions show up as incorrect. The easiest way to do this is to add the incorrect class and removing the correct class from all question items before checking the correct answers
16 | // 7. BONUS: If all answers are correct show the element with the id `alert` and hide it after one second (look into setTimeout) (use the class active to show the alert and remove the class to hide it)
17 |
--------------------------------------------------------------------------------
/56-57-form-validation/before/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Form Validation
5 |
6 |
15 |
16 |
17 |
18 |
49 |
50 |
--------------------------------------------------------------------------------
/56-57-form-validation/after/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Form Validation
5 |
6 |
15 |
16 |
17 |
18 |
19 |
50 |
51 |
--------------------------------------------------------------------------------
/74-shopping-cart/after/store.js:
--------------------------------------------------------------------------------
1 | import items from "./items.json"
2 | import formatCurrency from "./util/formatCurrency.js"
3 | import addGlobalEventListener from "./util/addGlobalEventListener.js"
4 | import { addToCart } from "./shoppingCart.js"
5 |
6 | const storeItemTemplate = document.querySelector("#store-item-template")
7 | const storeItemContainer = document.querySelector("[data-store-container]")
8 | const IMAGE_URL = "https://dummyimage.com/420x260"
9 |
10 | export function setupStore() {
11 | if (storeItemContainer == null) return
12 |
13 | addGlobalEventListener("click", "[data-add-to-cart-button]", e => {
14 | const id = e.target.closest("[data-store-item]").dataset.itemId
15 | addToCart(parseInt(id))
16 | })
17 |
18 | items.forEach(renderStoreItem)
19 | }
20 |
21 | function renderStoreItem(item) {
22 | const storeItem = storeItemTemplate.content.cloneNode(true)
23 |
24 | const container = storeItem.querySelector("[data-store-item]")
25 | container.dataset.itemId = item.id
26 |
27 | const name = storeItem.querySelector("[data-name]")
28 | name.innerText = item.name
29 |
30 | const category = storeItem.querySelector("[data-category]")
31 | category.innerText = item.category
32 |
33 | const image = storeItem.querySelector("[data-image]")
34 | image.src = `${IMAGE_URL}/${item.imageColor}/${item.imageColor}`
35 |
36 | const price = storeItem.querySelector("[data-price]")
37 | price.innerText = formatCurrency(item.priceCents / 100)
38 |
39 | storeItemContainer.appendChild(storeItem)
40 | }
41 |
--------------------------------------------------------------------------------
/56-57-form-validation/before/script.js:
--------------------------------------------------------------------------------
1 | // TODO: Select all elements needed
2 | // Use the HTML to figure out what classes/ids will work best for selecting each element
3 |
4 | // TODO: Create an event listener for when the form is submitted and do the following inside of it.
5 | // TODO: Create an array to store all error messages and clear any old error messages
6 | // TODO: Define the following validation checks with appropriate error messages
7 | // 1. Ensure the username is at least 6 characters long
8 | // 2. Ensure the password is at least 10 characters long
9 | // 3. Ensure the password and confirmation password match
10 | // 4. Ensure the terms checkbox is checked
11 | // TODO: If there are any errors then prevent the form from submitting and show the error messages
12 |
13 | // TODO: Define this function
14 | function clearErrors() {
15 | // Loop through all the children of the error-list element and remove them
16 | // IMPORTANT: This cannot be done with a forEach loop or a normal for loop since as you remove children it will modify the list you are looping over which will not work
17 | // I recommend using a while loop to accomplish this task
18 | // This is the trickiest part of this exercise so if you get stuck and are unable to progress you can also set the innerHTML property of the error-list to an empty string and that will also clear the children. I recommend trying to accomplish this with a while loop, though, for practice.
19 | // Also, make sure you remove the show class to the errors container
20 | }
21 |
22 | // TODO: Define this function
23 | function showErrors(errorMessages) {
24 | // Add each error to the error-list element
25 | // Make sure to use an li as the element for each error
26 | // Also, make sure you add the show class to the errors container
27 | }
28 |
--------------------------------------------------------------------------------
/66-67-expand-collapse/before/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Expand/Collapse
5 |
6 |
7 |
8 |
9 |
10 | Title 1
11 |
12 |
13 |
14 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Id voluptate dolorem reprehenderit nisi doloribus? Dolor officiis praesentium aperiam dolorem sequi obcaecati iste corporis voluptatum accusamus.
15 |
16 |
17 |
18 |
19 | Title 2
20 |
21 |
22 |
23 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Id voluptate dolorem reprehenderit nisi doloribus? Dolor officiis praesentium aperiam dolorem sequi obcaecati iste corporis voluptatum accusamus.
24 |
25 |
26 |
27 |
28 | Title 3
29 |
30 |
31 |
32 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Id voluptate dolorem reprehenderit nisi doloribus? Dolor officiis praesentium aperiam dolorem sequi obcaecati iste corporis voluptatum accusamus.
33 |
34 |
35 |
36 |
37 | Title 4
38 |
39 |
40 |
41 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Id voluptate dolorem reprehenderit nisi doloribus? Dolor officiis praesentium aperiam dolorem sequi obcaecati iste corporis voluptatum accusamus.
42 |
15 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Id voluptate dolorem reprehenderit nisi doloribus? Dolor officiis praesentium aperiam dolorem sequi obcaecati iste corporis voluptatum accusamus.
16 |
17 |
18 |
19 |
20 | Title 2
21 |
22 |
23 |
24 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Id voluptate dolorem reprehenderit nisi doloribus? Dolor officiis praesentium aperiam dolorem sequi obcaecati iste corporis voluptatum accusamus.
25 |
26 |
27 |
28 |
29 | Title 3
30 |
31 |
32 |
33 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Id voluptate dolorem reprehenderit nisi doloribus? Dolor officiis praesentium aperiam dolorem sequi obcaecati iste corporis voluptatum accusamus.
34 |
35 |
36 |
37 |
38 | Title 4
39 |
40 |
41 |
42 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Id voluptate dolorem reprehenderit nisi doloribus? Dolor officiis praesentium aperiam dolorem sequi obcaecati iste corporis voluptatum accusamus.
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/65-advanced-todo-list/after/script.js:
--------------------------------------------------------------------------------
1 | const form = document.querySelector("#new-todo-form")
2 | const todoInput = document.querySelector("#todo-input")
3 | const list = document.querySelector("#list")
4 | const template = document.querySelector("#list-item-template")
5 | const LOCAL_STORAGE_PREFIX = "ADVANCED_TODO_LIST"
6 | const TODOS_STORAGE_KEY = `${LOCAL_STORAGE_PREFIX}-todos`
7 | let todos = loadTodos()
8 | todos.forEach(renderTodo)
9 |
10 | list.addEventListener("change", e => {
11 | if (!e.target.matches("[data-list-item-checkbox]")) return
12 |
13 | const parent = e.target.closest(".list-item")
14 | const todoId = parent.dataset.todoId
15 | const todo = todos.find(t => t.id === todoId)
16 | todo.complete = e.target.checked
17 | saveTodos()
18 | })
19 |
20 | list.addEventListener("click", e => {
21 | if (!e.target.matches("[data-button-delete]")) return
22 |
23 | const parent = e.target.closest(".list-item")
24 | const todoId = parent.dataset.todoId
25 | parent.remove()
26 | todos = todos.filter(todo => todo.id !== todoId)
27 | saveTodos()
28 | })
29 |
30 | form.addEventListener("submit", e => {
31 | e.preventDefault()
32 |
33 | const todoName = todoInput.value
34 | if (todoName === "") return
35 | const newTodo = {
36 | name: todoName,
37 | complete: false,
38 | id: new Date().valueOf().toString()
39 | }
40 | todos.push(newTodo)
41 | renderTodo(newTodo)
42 | saveTodos()
43 | todoInput.value = ""
44 | })
45 |
46 | function renderTodo(todo) {
47 | const templateClone = template.content.cloneNode(true)
48 | const listItem = templateClone.querySelector(".list-item")
49 | listItem.dataset.todoId = todo.id
50 | const textElement = templateClone.querySelector("[data-list-item-text]")
51 | textElement.innerText = todo.name
52 | const checkbox = templateClone.querySelector("[data-list-item-checkbox]")
53 | checkbox.checked = todo.complete
54 | list.appendChild(templateClone)
55 | }
56 |
57 | function loadTodos() {
58 | const todosString = localStorage.getItem(TODOS_STORAGE_KEY)
59 | return JSON.parse(todosString) || []
60 | }
61 |
62 | function saveTodos() {
63 | localStorage.setItem(TODOS_STORAGE_KEY, JSON.stringify(todos))
64 | }
65 |
--------------------------------------------------------------------------------
/53-midi-piano/after/script.js:
--------------------------------------------------------------------------------
1 | const audioContext = new AudioContext()
2 |
3 | const NOTE_DETAILS = [
4 | { note: "C", key: "Z", frequency: 261.626, active: false },
5 | { note: "Db", key: "S", frequency: 277.183, active: false },
6 | { note: "D", key: "X", frequency: 293.665, active: false },
7 | { note: "Eb", key: "D", frequency: 311.127, active: false },
8 | { note: "E", key: "C", frequency: 329.628, active: false },
9 | { note: "F", key: "V", frequency: 349.228, active: false },
10 | { note: "Gb", key: "G", frequency: 369.994, active: false },
11 | { note: "G", key: "B", frequency: 391.995, active: false },
12 | { note: "Ab", key: "H", frequency: 415.305, active: false },
13 | { note: "A", key: "N", frequency: 440, active: false },
14 | { note: "Bb", key: "J", frequency: 466.164, active: false },
15 | { note: "B", key: "M", frequency: 493.883, active: false }
16 | ]
17 |
18 | document.addEventListener("keydown", e => {
19 | if (e.repeat) return
20 | const keyboardKey = e.code
21 | const noteDetail = getNoteDetail(keyboardKey)
22 |
23 | if (noteDetail == null) return
24 |
25 | noteDetail.active = true
26 | playNotes()
27 | })
28 |
29 | document.addEventListener("keyup", e => {
30 | const keyboardKey = e.code
31 | const noteDetail = getNoteDetail(keyboardKey)
32 |
33 | if (noteDetail == null) return
34 |
35 | noteDetail.active = false
36 | playNotes()
37 | })
38 |
39 | function getNoteDetail(keyboardKey) {
40 | return NOTE_DETAILS.find(n => `Key${n.key}` === keyboardKey)
41 | }
42 |
43 | function playNotes() {
44 | NOTE_DETAILS.forEach(n => {
45 | const keyElement = document.querySelector(`[data-note="${n.note}"]`)
46 | keyElement.classList.toggle("active", n.active)
47 | if (n.oscillator != null) {
48 | n.oscillator.stop()
49 | n.oscillator.disconnect()
50 | }
51 | })
52 |
53 | const activeNotes = NOTE_DETAILS.filter(n => n.active)
54 | const gain = 1 / activeNotes.length
55 | activeNotes.forEach(n => {
56 | startNote(n, gain)
57 | })
58 | }
59 |
60 | function startNote(noteDetail, gain) {
61 | const gainNode = audioContext.createGain()
62 | gainNode.gain.value = gain
63 | const oscillator = audioContext.createOscillator()
64 | oscillator.frequency.value = noteDetail.frequency
65 | oscillator.type = "sine"
66 | oscillator.connect(gainNode).connect(audioContext.destination)
67 | oscillator.start()
68 | noteDetail.oscillator = oscillator
69 | }
70 |
--------------------------------------------------------------------------------
/54-55-quiz/after/script.js:
--------------------------------------------------------------------------------
1 | /*
2 | TODO: 2. Select all elements needed
3 | * The form element (has the id `quiz-form`)
4 | * The answer inputs (have the class `answer`)
5 | * BONUS: The questions (have the class `question-item`)
6 | * BONUS: The alert (has the id `alert`)
7 | */
8 | const form = document.getElementById("quiz-form")
9 | const answers = Array.from(document.querySelectorAll(".answer"))
10 | const questionItems = document.querySelectorAll(".question-item")
11 | const alert = document.querySelector("#alert")
12 |
13 | // TODO: 3. Create a submit event listener for the form that does the following.
14 | form.addEventListener("submit", e => {
15 | // 1. Prevent the default behaviour
16 | e.preventDefault()
17 |
18 | // 6. BONUS: Make sure unanswered questions show up as incorrect. The easiest way to do this is to add the incorrect class and removing the correct class from all question items before checking the correct answers
19 | questionItems.forEach(questionItem => {
20 | questionItem.classList.add("incorrect")
21 | questionItem.classList.remove("correct")
22 | })
23 |
24 | // 2. Get all selected answers (use the `checked` property on the input to determine if it is selected or not)
25 | const checkedAnswers = answers.filter(answer => answer.checked)
26 | // 3. Loop through the selected answer to see if they are correct or not (Check the value of the answer to see if it is the string "true")
27 | checkedAnswers.forEach(answer => {
28 | const isCorrect = answer.value === "true"
29 | const questionItem = answer.closest(".question-item")
30 |
31 | // 4. For each correct answer add the class `correct` to the parent with the class `question-item` and remove the class `incorrect`.
32 | if (isCorrect) {
33 | questionItem.classList.add("correct")
34 | questionItem.classList.remove("incorrect")
35 | } else {
36 | // 5. For each incorrect answer add the class `incorrect` to the parent with the class `question-item` and remove the class `correct`.
37 | questionItem.classList.add("incorrect")
38 | questionItem.classList.remove("correct")
39 | }
40 | // 7. BONUS: If all answers are correct show the element with the id `alert` and hide it after one second (look into setTimeout) (use the class active to show the alert and remove the class to hide it)
41 |
42 | const allTrue = checkedAnswers.every(answer => answer.value === "true")
43 | const allAnswered = checkedAnswers.length === questionItems.length
44 |
45 | if (allTrue && allAnswered) {
46 | alert.classList.add("active")
47 | setTimeout(() => {
48 | alert.classList.remove("active")
49 | }, 1000)
50 | }
51 | })
52 | })
53 |
--------------------------------------------------------------------------------
/73-date-picker/after/script.js:
--------------------------------------------------------------------------------
1 | import {
2 | format,
3 | getUnixTime,
4 | fromUnixTime,
5 | addMonths,
6 | subMonths,
7 | startOfWeek,
8 | startOfMonth,
9 | endOfWeek,
10 | endOfMonth,
11 | eachDayOfInterval,
12 | isSameMonth,
13 | isSameDay
14 | } from "date-fns"
15 |
16 | const datePickerButton = document.querySelector(".date-picker-button")
17 | const datePicker = document.querySelector(".date-picker")
18 | const datePickerHeaderText = document.querySelector(".current-month")
19 | const previousMonthButton = document.querySelector(".prev-month-button")
20 | const nextMonthButton = document.querySelector(".next-month-button")
21 | const dateGrid = document.querySelector(".date-picker-grid-dates")
22 | let currentDate = new Date()
23 |
24 | datePickerButton.addEventListener("click", () => {
25 | datePicker.classList.toggle("show")
26 | const selectedDate = fromUnixTime(datePickerButton.dataset.selectedDate)
27 | currentDate = selectedDate
28 | setupDatePicker(selectedDate)
29 | })
30 |
31 | function setDate(date) {
32 | datePickerButton.innerText = format(date, "MMMM do, yyyy")
33 | datePickerButton.dataset.selectedDate = getUnixTime(date)
34 | }
35 |
36 | function setupDatePicker(selectedDate) {
37 | datePickerHeaderText.innerText = format(currentDate, "MMMM - yyyy")
38 | setupDates(selectedDate)
39 | }
40 |
41 | function setupDates(selectedDate) {
42 | const firstWeekStart = startOfWeek(startOfMonth(currentDate))
43 | const lastWeekEnd = endOfWeek(endOfMonth(currentDate))
44 | const dates = eachDayOfInterval({ start: firstWeekStart, end: lastWeekEnd })
45 | dateGrid.innerHTML = ""
46 |
47 | dates.forEach(date => {
48 | const dateElement = document.createElement("button")
49 | dateElement.classList.add("date")
50 | dateElement.innerText = date.getDate()
51 | if (!isSameMonth(date, currentDate)) {
52 | dateElement.classList.add("date-picker-other-month-date")
53 | }
54 | if (isSameDay(date, selectedDate)) {
55 | dateElement.classList.add("selected")
56 | }
57 | console.log(selectedDate)
58 | dateElement.addEventListener("click", () => {
59 | setDate(date)
60 | datePicker.classList.remove("show")
61 | })
62 |
63 | dateGrid.appendChild(dateElement)
64 | })
65 | }
66 |
67 | nextMonthButton.addEventListener("click", () => {
68 | const selectedDate = fromUnixTime(datePickerButton.dataset.selectedDate)
69 | currentDate = addMonths(currentDate, 1)
70 | setupDatePicker(selectedDate)
71 | })
72 |
73 | previousMonthButton.addEventListener("click", () => {
74 | const selectedDate = fromUnixTime(datePickerButton.dataset.selectedDate)
75 | currentDate = subMonths(currentDate, 1)
76 | setupDatePicker(selectedDate)
77 | })
78 |
79 | setDate(new Date())
80 |
--------------------------------------------------------------------------------
/73-date-picker/before/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Date Picker
5 |
6 |
16 |
17 |
18 |
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Reprehenderit tempore nisi dolores ipsum veritatis, rerum excepturi numquam vitae perferendis recusandae possimus deserunt quas aut perspiciatis, qui architecto fuga molestias! Dolorem veritatis quasi cum quod deserunt sit recusandae blanditiis debitis voluptatum.
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Reprehenderit tempore nisi dolores ipsum veritatis, rerum excepturi numquam vitae perferendis recusandae possimus deserunt quas aut perspiciatis, qui architecto fuga molestias! Dolorem veritatis quasi cum quod deserunt sit recusandae blanditiis debitis voluptatum.