├── .vscode └── settings.json ├── 10_jogo_da_memoria ├── img │ └── card-back.png ├── index.html ├── scripts.js └── style.css ├── 11_dropdown ├── index.html ├── scripts.js └── style.css ├── 12_conversao_json_para_csv ├── exemplos.txt ├── index.html ├── scripts.js └── style.css ├── 13_faq ├── index.html ├── scripts.js └── style.css ├── 14_gerador_mega_sena ├── index.html ├── scripts.js └── style.css ├── 15_jogo_da_advinhacao ├── index.html ├── scripts.js └── style.css ├── 16_kanban ├── index.html ├── scripts.js └── style.css ├── 17_popup_de_saida ├── index.html ├── scripts.js └── style.css ├── 18_verificador_de_palindromo ├── index.html ├── scripts.js └── style.css ├── 19_todo_list ├── css │ └── styles.css ├── img │ └── bg.jpg ├── index.html └── js │ └── scripts.js ├── 1_conversor_de_unidades ├── index.html ├── scripts.js └── styles.css ├── 20_gerador_de_senha ├── css │ └── styles.css ├── img │ └── bg-form.jpg ├── index.html └── js │ └── scripts.js ├── 21_contador_caracteres_extra ├── index.html ├── scripts.js └── style.css ├── 22_teste_velocidade_digitacao ├── index.html ├── scripts.js └── style.css ├── 23_todo_react └── todo │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── Filter.jsx │ │ ├── Search.jsx │ │ ├── Todo.jsx │ │ └── TodoForm.jsx │ ├── img │ │ └── bg.jpg │ └── main.jsx │ └── vite.config.js ├── 2_relogio_digital ├── index.html ├── scripts.js └── styles.css ├── 3_citacoes ├── .vscode │ └── settings.json ├── index.html ├── scripts.js └── styles.css ├── 4_galera_com_lightbox ├── img │ ├── image-1.jpg │ ├── image-2.jpg │ ├── image-3.jpg │ └── image-4.jpg ├── index.html ├── scripts.js └── styles.css ├── 5_barra_de_progresso ├── index.html ├── scripts.js └── styles.css ├── 6_formulario_com_validacao ├── index.html ├── scripts.js └── styles.css ├── 7_paleta_de_cores ├── .vscode │ └── settings.json ├── index.html ├── scripts.js └── styles.css ├── 8_gerador_de_cpf ├── index.html ├── scripts.js └── styles.css └── 9_calculadora_de_gorjeta ├── index.html ├── scripts.js └── styles.css /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } 4 | -------------------------------------------------------------------------------- /10_jogo_da_memoria/img/card-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_javascript/9af0e18d4ebb02c1a6dc226b1a0ddacd65043e40/10_jogo_da_memoria/img/card-back.png -------------------------------------------------------------------------------- /10_jogo_da_memoria/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Memory Game 6 | 7 | 8 | 9 | 10 |

Jogo da Memória

11 |
12 |
13 |
14 |

Tentativas: 0

15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /10_jogo_da_memoria/scripts.js: -------------------------------------------------------------------------------- 1 | // Crie uma matriz com pares de números representando as cartas 2 | const cards = [1, 1, 2, 2, 3, 3, 4, 4]; 3 | 4 | // Crie um objeto para armazenar as imagens correspondentes para cada carta 5 | async function generateImagePairs() { 6 | const imagePairs = {}; 7 | for (let i = 0; i < cards.length; i++) { 8 | if (!imagePairs[cards[i]]) { 9 | const id = Math.floor(Math.random() * 1000) + 1; 10 | const url = `https://picsum.photos/id/${id}/200/300`; 11 | imagePairs[cards[i]] = [url, url]; 12 | } 13 | } 14 | return imagePairs; 15 | } 16 | 17 | // Embaralhe a matriz de cartas 18 | function shuffleCards(cards) { 19 | cards.sort(() => Math.random() - 0.5); 20 | } 21 | 22 | let flippedCards = 0; 23 | let firstCard, secondCard; 24 | let attempts = 0; 25 | 26 | // Selecione as cartas e atribua um número da matriz a cada carta 27 | async function createCards() { 28 | const imagePairs = await generateImagePairs(); 29 | shuffleCards(cards); 30 | const cardsList = document.querySelector(".container"); 31 | for (let i = 0; i < cards.length; i++) { 32 | const card = document.createElement("div"); 33 | const cardBack = document.createElement("div"); 34 | const cardFront = document.createElement("div"); 35 | card.classList.add("card"); 36 | cardBack.classList.add("back"); 37 | cardFront.classList.add("front"); 38 | cardBack.style.backgroundImage = `url('img/card-back.png')`; 39 | const cardNumber = cards[i]; 40 | const cardImage = imagePairs[cardNumber].pop(); 41 | cardFront.style.backgroundImage = `url(${cardImage})`; 42 | card.setAttribute("data-card", cardNumber); 43 | card.appendChild(cardBack); 44 | card.appendChild(cardFront); 45 | card.addEventListener("click", flipCard); 46 | cardsList.appendChild(card); 47 | } 48 | } 49 | 50 | // Vire a carta clicada 51 | function flipCard() { 52 | if (flippedCards < 2 && !this.classList.contains("flip")) { 53 | flippedCards++; 54 | this.classList.add("flip"); 55 | if (flippedCards === 1) { 56 | firstCard = this; 57 | } else { 58 | secondCard = this; 59 | attempts++; 60 | updateAttempts(); 61 | checkForMatch(); 62 | } 63 | } 64 | } 65 | 66 | // Verifique se as cartas viradas são iguais 67 | function checkForMatch() { 68 | const isMatch = 69 | firstCard.getAttribute("data-card") === 70 | secondCard.getAttribute("data-card"); 71 | isMatch ? disableCards() : unflipCards(); 72 | } 73 | 74 | // Desabilita as cartas viradas se forem iguais 75 | function disableCards() { 76 | firstCard.removeEventListener("click", flipCard); 77 | secondCard.removeEventListener("click", flipCard); 78 | 79 | console.log(document.querySelectorAll(".card:not(.flip)").length); 80 | if (document.querySelectorAll(".card:not(.flip)").length === 0) { 81 | showCongratulations(); 82 | } 83 | 84 | resetBoard(); 85 | } 86 | 87 | // Desvira as cartas se não forem iguais 88 | function unflipCards() { 89 | setTimeout(() => { 90 | firstCard.classList.remove("flip"); 91 | secondCard.classList.remove("flip"); 92 | resetBoard(); 93 | }, 1000); 94 | } 95 | 96 | // Reinicia o tabuleiro 97 | function resetBoard() { 98 | [flippedCards, firstCard, secondCard] = [0, null, null]; 99 | } 100 | 101 | // Atualiza o número de tentativas 102 | function updateAttempts() { 103 | const attemptsElement = document.querySelector(".attempts"); 104 | attemptsElement.textContent = `Tentativas: ${attempts}`; 105 | } 106 | 107 | function showCongratulations() { 108 | const congratulationsContainer = document.querySelector( 109 | ".congratulations-container" 110 | ); 111 | const congratulationsElement = document.createElement("p"); 112 | congratulationsElement.classList.add("congratulations"); 113 | congratulationsElement.textContent = `Parabéns! Você venceu em ${attempts} tentativas!`; 114 | congratulationsContainer.appendChild(congratulationsElement); 115 | } 116 | 117 | createCards(); 118 | -------------------------------------------------------------------------------- /10_jogo_da_memoria/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Helvetica; 3 | } 4 | 5 | h1 { 6 | text-align: center; 7 | margin-bottom: 0; 8 | } 9 | 10 | .container { 11 | display: grid; 12 | grid-template-columns: repeat(4, 1fr); 13 | grid-gap: 1rem; 14 | max-width: 800px; 15 | margin: 0 auto; 16 | } 17 | 18 | .card { 19 | position: relative; 20 | height: 200px; 21 | } 22 | 23 | .card .front, 24 | .card .back { 25 | position: absolute; 26 | width: 100%; 27 | height: 100%; 28 | backface-visibility: hidden; 29 | } 30 | 31 | .card .front { 32 | border: 1px solid black; 33 | display: flex; 34 | font-size: 48px; 35 | transform: rotateY(180deg); 36 | transition: transform 0.5s ease-in-out; 37 | } 38 | 39 | .card .back { 40 | background-size: cover; 41 | background-position: center; 42 | border: 1px solid black; 43 | box-sizing: border-box; 44 | transform: rotateY(0deg); 45 | transition: transform 0.5s ease-in-out; 46 | z-index: 1; 47 | } 48 | 49 | .card.flip .front { 50 | transform: rotateY(0deg); 51 | z-index: 2; 52 | } 53 | 54 | .card.flip .back { 55 | transform: rotateY(0deg); 56 | } 57 | 58 | .attempts-container { 59 | text-align: center; 60 | margin-top: 20px; 61 | } 62 | 63 | .attempts { 64 | font-size: 24px; 65 | } 66 | 67 | .congratulations-container { 68 | justify-content: center; 69 | margin-top: 20px; 70 | } 71 | 72 | .congratulations { 73 | font-size: 1.5rem; 74 | padding: 0.4rem; 75 | background-color: #0a790a; 76 | color: #fff; 77 | border-radius: 4px; 78 | text-align: center; 79 | max-width: 500px; 80 | margin: 1rem auto; 81 | } 82 | -------------------------------------------------------------------------------- /11_dropdown/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dropdown 6 | 7 | 8 | 9 | 10 | 31 |

Conteúdo...

32 | Voltar ao topo 33 | 34 | 35 | -------------------------------------------------------------------------------- /11_dropdown/scripts.js: -------------------------------------------------------------------------------- 1 | // Adiciona o evento de scroll na janela 2 | window.addEventListener("scroll", function () { 3 | // Obtém a posição da janela atual 4 | const scrollTop = window.pageYOffset || document.documentElement.scrollTop; 5 | 6 | // Verifica se a posição da janela é maior que 500 pixels 7 | if (scrollTop > 500) { 8 | // Exibe o botão "Voltar ao topo" 9 | document.getElementById("back-to-top").style.display = "block"; 10 | } else { 11 | // Oculta o botão "Voltar ao topo" 12 | document.getElementById("back-to-top").style.display = "none"; 13 | } 14 | }); 15 | 16 | // Adiciona o evento de clique ao botão "Voltar ao topo" 17 | document.getElementById("back-to-top").addEventListener("click", function (e) { 18 | // Previne o comportamento padrão do link 19 | e.preventDefault(); 20 | 21 | // Retorna ao topo da página 22 | window.scrollTo({ 23 | top: 0, 24 | behavior: "smooth", 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /11_dropdown/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Helvetica; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | padding-bottom: 1500px; 9 | } 10 | 11 | nav { 12 | background-color: #333; 13 | } 14 | 15 | nav ul { 16 | list-style: none; 17 | margin: 0; 18 | padding: 0; 19 | display: flex; 20 | gap: 1rem; 21 | padding: 0.2rem 1rem; 22 | } 23 | 24 | nav ul li { 25 | display: inline-block; 26 | position: relative; 27 | } 28 | 29 | nav ul li a { 30 | display: block; 31 | padding: 15px; 32 | color: #fff; 33 | text-decoration: none; 34 | } 35 | 36 | nav ul li a:hover { 37 | background-color: #222; 38 | } 39 | 40 | nav ul li:hover > ul { 41 | display: block; 42 | } 43 | 44 | nav ul ul { 45 | display: none; 46 | position: absolute; 47 | width: 100px; 48 | top: 50px; 49 | left: 0; 50 | background-color: #444; 51 | padding: 0; 52 | font-size: 0.8rem; 53 | } 54 | 55 | nav ul ul li { 56 | display: block; 57 | } 58 | 59 | nav ul ul li a { 60 | display: block; 61 | color: #fff; 62 | text-decoration: none; 63 | padding: 0.5rem; 64 | } 65 | 66 | nav ul ul ul { 67 | top: 0; 68 | left: 100px; 69 | background-color: #555; 70 | } 71 | 72 | #back-to-top { 73 | display: none; 74 | position: fixed; 75 | bottom: 20px; 76 | right: 20px; 77 | background-color: #333; 78 | color: #fff; 79 | padding: 10px 20px; 80 | text-decoration: none; 81 | } 82 | 83 | #back-to-top:hover { 84 | background-color: #666; 85 | } 86 | -------------------------------------------------------------------------------- /12_conversao_json_para_csv/exemplos.txt: -------------------------------------------------------------------------------- 1 | JSON: 2 | 3 | [ { "id": 1, "name": "Alice", "age": 25 }, { "id": 2, "name": "Bob", "age": 30 }, { "id": 3, "name": "Charlie", "age": 35 }] 4 | 5 | 6 | CSV: 7 | 8 | id,name,age 9 | 1,Alice,25 10 | 2,Bob,30 11 | 3,Charlie,35 12 | -------------------------------------------------------------------------------- /12_conversao_json_para_csv/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Conversor JSON <> CSV 6 | 7 | 8 | 9 | 10 |

Conversor JSON <> CSV

11 |
12 | 13 | 14 | 15 | 16 |
17 | 20 | 23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /12_conversao_json_para_csv/scripts.js: -------------------------------------------------------------------------------- 1 | const converterForm = document.querySelector("#converterForm"); 2 | const converterInput = document.querySelector("#converterInput"); 3 | const jsonToCsvButton = document.querySelector("#jsonToCsvButton"); 4 | const csvToJsonButton = document.querySelector("#csvToJsonButton"); 5 | 6 | function jsonToCsv(json) { 7 | const headers = Object.keys(json[0]); 8 | const csvRows = []; 9 | csvRows.push(headers.join(",")); 10 | 11 | for (const row of json) { 12 | const values = headers.map((header) => { 13 | let value = row[header]; 14 | if (value === null || value === undefined) { 15 | value = ""; 16 | } else if (typeof value === "object") { 17 | value = JSON.stringify(value); 18 | } 19 | return value; 20 | }); 21 | csvRows.push(values.join(",")); 22 | } 23 | 24 | return csvRows.join("\n"); 25 | } 26 | 27 | function csvToJson(csv) { 28 | const lines = csv.split("\n"); 29 | const headers = lines[0].split(","); 30 | const json = []; 31 | 32 | // Remover outro JSON 33 | const preTags = document.querySelectorAll("pre"); 34 | preTags.forEach((tag) => { 35 | tag.remove(); 36 | }); 37 | 38 | for (let i = 1; i < lines.length; i++) { 39 | const values = lines[i].split(","); 40 | const row = {}; 41 | 42 | for (let j = 0; j < headers.length; j++) { 43 | let value = values[j]; 44 | if (value[0] === "{" || value[0] === "[") { 45 | value = JSON.parse(value); 46 | } 47 | row[headers[j]] = value; 48 | } 49 | 50 | json.push(row); 51 | } 52 | 53 | return json; 54 | } 55 | 56 | jsonToCsvButton.addEventListener("click", function () { 57 | const json = JSON.parse(converterInput.value.trim()); 58 | const csv = jsonToCsv(json); 59 | downloadCsv(csv); 60 | }); 61 | 62 | csvToJsonButton.addEventListener("click", function () { 63 | const csv = converterInput.value.trim(); 64 | const json = csvToJson(csv); 65 | displayJson(json); 66 | }); 67 | 68 | function downloadCsv(csv) { 69 | const downloadLink = document.createElement("a"); 70 | downloadLink.setAttribute( 71 | "href", 72 | "data:text/csv;charset=utf-8," + encodeURIComponent(csv) 73 | ); 74 | downloadLink.setAttribute("download", "data.csv"); 75 | downloadLink.style.display = "none"; 76 | document.body.appendChild(downloadLink); 77 | downloadLink.click(); 78 | document.body.removeChild(downloadLink); 79 | } 80 | 81 | function displayJson(json) { 82 | const resultArea = document.createElement("pre"); 83 | resultArea.textContent = JSON.stringify(json, null, 2); 84 | document.body.appendChild(resultArea); 85 | } 86 | -------------------------------------------------------------------------------- /12_conversao_json_para_csv/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | background-color: #f8f8f8; 4 | text-align: center; 5 | box-sizing: border-box; 6 | } 7 | 8 | #converterForm { 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | gap: 1rem; 14 | } 15 | 16 | label { 17 | display: block; 18 | margin-bottom: 10px; 19 | font-weight: bold; 20 | } 21 | 22 | textarea, 23 | pre { 24 | width: 600px; 25 | height: 200px; 26 | padding: 10px; 27 | font-size: 16px; 28 | border: 2px solid #ccc; 29 | border-radius: 5px; 30 | resize: none; 31 | } 32 | 33 | pre { 34 | height: auto; 35 | margin: 1.5rem auto; 36 | text-align: left; 37 | } 38 | 39 | button { 40 | margin-top: 10px; 41 | padding: 10px; 42 | font-size: 16px; 43 | color: #fff; 44 | background-color: #007bff; 45 | border: none; 46 | border-radius: 5px; 47 | cursor: pointer; 48 | transition: 0.4s; 49 | } 50 | 51 | button:hover { 52 | background-color: #0069d9; 53 | } 54 | -------------------------------------------------------------------------------- /13_faq/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FAQ 6 | 7 | 8 | 9 | 10 |

FAQ

11 |
12 |
13 |
O que é JavaScript?
14 |
15 |

16 | JavaScript é uma linguagem de programação de alto nível, 17 | interpretada e orientada a objetos, amplamente utilizada em 18 | desenvolvimento web para criar interatividade em páginas da web. 19 |

20 |
21 |
22 |
23 |
Qual é a diferença entre let e var?
24 |
25 |

26 | A principal diferença entre let e var é o escopo. let tem escopo de 27 | bloco, o que significa que a variável só pode ser acessada dentro do 28 | bloco em que é definida, enquanto var tem escopo de função, o que 29 | significa que a variável pode ser acessada em toda a função em que é 30 | definida. 31 |

32 |
33 |
34 |
35 |
O que é uma função de callback?
36 |
37 |

38 | Uma função de callback é uma função passada como argumento para 39 | outra função. A função de callback é então chamada dentro da função 40 | que a recebeu, permitindo que ela seja executada em algum momento 41 | posterior ou em resposta a algum evento. 42 |

43 |
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /13_faq/scripts.js: -------------------------------------------------------------------------------- 1 | // obtém todos os elementos com classe accordion-header 2 | const headers = document.querySelectorAll(".accordion-header"); 3 | 4 | // adiciona o listener de clique em cada header 5 | headers.forEach(function (header) { 6 | header.addEventListener("click", function () { 7 | // obtém o elemento accordion-item correspondente 8 | const item = this.parentNode; 9 | // obtém todos os elementos accordion-item 10 | const items = document.querySelectorAll(".accordion-item"); 11 | // verifica se o item clicado já está ativo 12 | const isActive = item.classList.contains("accordion-item--active"); 13 | // fecha todos os itens 14 | items.forEach(function (item) { 15 | item.classList.remove("accordion-item--active"); 16 | item.classList.add("accordion-item--closed"); 17 | }); 18 | // se o item clicado não estava ativo, abra-o 19 | if (!isActive) { 20 | item.classList.add("accordion-item--active"); 21 | item.classList.remove("accordion-item--closed"); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /13_faq/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | background-color: #f2f2f2; 4 | } 5 | 6 | h1 { 7 | text-align: center; 8 | margin-top: 1rem; 9 | } 10 | 11 | .accordion { 12 | max-width: 600px; 13 | margin: 0 auto; 14 | } 15 | 16 | .accordion-item { 17 | background-color: #fff; 18 | border: 1px solid #ccc; 19 | border-radius: 5px; 20 | margin-bottom: 1rem; 21 | overflow: hidden; 22 | } 23 | 24 | .accordion-header { 25 | padding: 1rem; 26 | font-size: 1.3rem; 27 | font-weight: bold; 28 | cursor: pointer; 29 | } 30 | 31 | .accordion-content { 32 | padding: 0 10px; 33 | font-size: 0.9rem; 34 | line-height: 1.5; 35 | } 36 | 37 | .accordion-item--active .accordion-header { 38 | background-color: #ddd; 39 | } 40 | 41 | .accordion-item--active .accordion-content { 42 | max-height: 1000px; 43 | transition: max-height 0.5s ease-in-out; 44 | } 45 | 46 | /* esconde o conteúdo dos itens fechados */ 47 | .accordion-item--closed .accordion-content { 48 | max-height: 0; 49 | overflow: hidden; 50 | } 51 | 52 | /* adiciona transição suave para abrir e fechar itens */ 53 | .accordion-content { 54 | transition: max-height 0.3s ease-out; 55 | } 56 | -------------------------------------------------------------------------------- /14_gerador_mega_sena/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Gerador de Números da Mega-Sena 6 | 7 | 8 | 9 | 10 |
11 |

Gerador de Números da Mega-Sena

12 |

Pressione o botão para gerar 6 números aleatórios:

13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /14_gerador_mega_sena/scripts.js: -------------------------------------------------------------------------------- 1 | const numbers = document.querySelectorAll(".number"); 2 | const generateBtn = document.querySelector("#generate"); 3 | 4 | function generateNumbers() { 5 | const max = 60; // número máximo 6 | const min = 1; // número mínimo 7 | const result = []; // array para armazenar os números gerados 8 | 9 | // gera 6 números aleatórios únicos 10 | while (result.length < 6) { 11 | const number = Math.floor(Math.random() * (max - min + 1)) + min; // gera um número aleatório 12 | if (!result.includes(number)) { 13 | // verifica se o número já foi gerado 14 | result.push(number); // adiciona o número ao array 15 | } 16 | } 17 | 18 | // exibe os números gerados na tela 19 | for (let i = 0; i < numbers.length; i++) { 20 | numbers[i].textContent = result[i]; // atribui o número ao elemento span correspondente 21 | } 22 | } 23 | 24 | generateBtn.addEventListener("click", generateNumbers); 25 | -------------------------------------------------------------------------------- /14_gerador_mega_sena/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | background-color: #f8f8f8; 4 | } 5 | 6 | .container { 7 | max-width: 800px; 8 | margin: 0 auto; 9 | padding: 1.5rem; 10 | margin-top: 2rem; 11 | background-color: #fff; 12 | border-radius: 5px; 13 | box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3); 14 | } 15 | 16 | h1 { 17 | margin-top: 0; 18 | font-size: 2rem; 19 | text-align: center; 20 | } 21 | 22 | p { 23 | margin-bottom: 1.8rem; 24 | font-size: 1.5rem; 25 | text-align: center; 26 | } 27 | 28 | #numbers { 29 | display: flex; 30 | justify-content: center; 31 | align-items: center; 32 | margin-bottom: 1.5rem; 33 | } 34 | 35 | .number { 36 | display: block; 37 | width: 60px; 38 | height: 60px; 39 | margin-right: 1rem; 40 | font-size: 2rem; 41 | font-weight: bold; 42 | text-align: center; 43 | line-height: 60px; 44 | background-color: #ff5100; 45 | color: #fff; 46 | border-radius: 50%; 47 | } 48 | 49 | button { 50 | display: block; 51 | margin: 0 auto; 52 | padding: 0.8rem 1rem; 53 | font-size: 1.1rem; 54 | color: #fff; 55 | background-color: #ff5100; 56 | border: none; 57 | border-radius: 5px; 58 | cursor: pointer; 59 | } 60 | 61 | button:hover { 62 | background-color: #e4510d; 63 | } 64 | -------------------------------------------------------------------------------- /15_jogo_da_advinhacao/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jogo de Adivinhação 6 | 7 | 8 | 9 | 10 |

Jogo de Adivinhação

11 | 12 |
13 |
14 | 15 | 21 |
22 | 23 |
24 |

25 | Eu estou pensando em um número entre 1 e 100. Tente adivinhar qual é! 26 |

27 | 28 |
29 | 34 | 35 |
36 | 37 |

38 | 39 | 40 | 41 |

Tentativas restantes:

42 |
43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /15_jogo_da_advinhacao/scripts.js: -------------------------------------------------------------------------------- 1 | // Obtém os elementos HTML 2 | const guessSection = document.querySelector("#guess-section"); 3 | const guessInput = document.querySelector("#guess"); 4 | const guessButton = document.querySelector("#guess-btn"); 5 | const resultParagraph = document.querySelector("#result"); 6 | const difficultySelect = document.querySelector("#difficulty"); 7 | const difficultySection = document.querySelector("#difficulty-section"); 8 | const gameSection = document.querySelector("#game-section"); 9 | const triesLeftSpan = document.querySelector("#tries-left"); 10 | const resetButton = document.querySelector("#reset-btn"); 11 | 12 | // Define as configurações de dificuldade 13 | let maxTries; 14 | let randomNumber; 15 | let triesLeft; 16 | 17 | // Define as configurações de dificuldade quando o usuário escolhe uma opção 18 | difficultySelect.addEventListener("change", function () { 19 | const difficulty = parseInt(difficultySelect.value); 20 | 21 | switch (difficulty) { 22 | case 1: 23 | maxTries = 10; 24 | break; 25 | case 2: 26 | maxTries = 7; 27 | break; 28 | case 3: 29 | maxTries = 5; 30 | break; 31 | default: 32 | maxTries = 10; 33 | break; 34 | } 35 | 36 | triesLeft = maxTries; 37 | triesLeftSpan.textContent = triesLeft; 38 | 39 | randomNumber = Math.floor(Math.random() * 100) + 1; 40 | 41 | // Oculta a seleção de dificuldade e exibe o campo de entrada de palpite 42 | difficultySection.style.display = "none"; 43 | gameSection.style.display = "block"; 44 | guessSection.style.display = "flex"; 45 | }); 46 | 47 | // Adiciona o evento de clique ao botão "Enviar" 48 | guessButton.addEventListener("click", function () { 49 | // Obtém o valor do campo de texto 50 | const guess = parseInt(guessInput.value); 51 | 52 | // Verifica se o valor é um número válido 53 | if (isNaN(guess) || guess < 1 || guess > 100) { 54 | resultParagraph.textContent = "Por favor, insira um número entre 1 e 100."; 55 | } else { 56 | // Compara o palpite do usuário com o número aleatório 57 | if (guess === randomNumber) { 58 | resultParagraph.textContent = `Parabéns! Você acertou em ${ 59 | maxTries - triesLeft + 1 60 | } tentativa(s).`; 61 | resetButton.style.display = "block"; 62 | guessSection.style.display = "none"; 63 | } else if (guess > randomNumber) { 64 | resultParagraph.textContent = "Muito alto. Tente novamente."; 65 | triesLeft--; 66 | } else { 67 | resultParagraph.textContent = "Muito baixo. Tente novamente."; 68 | triesLeft--; 69 | } 70 | 71 | triesLeftSpan.textContent = triesLeft; 72 | 73 | // Verifica se o usuário excedeu o número máximo de tentativas 74 | if (triesLeft === 0) { 75 | resultParagraph.textContent = 76 | "Suas tentativas acabaram. O número correto era " + randomNumber + "."; 77 | resetButton.style.display = "block"; 78 | guessSection.style.display = "none"; 79 | } 80 | } 81 | 82 | // Limpa o campo de texto 83 | guessInput.value = ""; 84 | }); 85 | 86 | function resetGame() { 87 | difficultySelect.value = ""; 88 | resultParagraph.textContent = ""; 89 | 90 | difficultySection.style.display = "flex"; 91 | gameSection.style.display = "none"; 92 | guessSection.style.display = "none"; 93 | resetButton.style.display = "none"; 94 | } 95 | 96 | resetButton.addEventListener("click", resetGame); 97 | -------------------------------------------------------------------------------- /15_jogo_da_advinhacao/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | background-color: #f5f5f5; 4 | } 5 | 6 | .container { 7 | max-width: 600px; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | justify-content: center; 12 | margin: 0 auto; 13 | background-color: #fff; 14 | padding: 1.5rem; 15 | border-radius: 10px; 16 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); 17 | } 18 | 19 | #difficulty-section, 20 | #game-section { 21 | display: flex; 22 | flex-direction: column; 23 | align-items: center; 24 | justify-content: center; 25 | gap: 1rem; 26 | } 27 | 28 | #guess-section { 29 | display: flex; 30 | justify-content: center; 31 | } 32 | 33 | h1 { 34 | font-size: 2rem; 35 | font-weight: bold; 36 | text-align: center; 37 | margin-bottom: 1.5rem; 38 | color: #333; 39 | } 40 | 41 | label { 42 | font-size: 1rem; 43 | font-weight: bold; 44 | color: #333; 45 | } 46 | 47 | select { 48 | font-size: 1rem; 49 | padding: 0.8rem; 50 | border: 2px solid #333; 51 | border-radius: 5px; 52 | background-color: #fff; 53 | color: #333; 54 | margin-bottom: 1.5rem; 55 | } 56 | 57 | p { 58 | font-size: 1.3rem; 59 | text-align: center; 60 | margin-bottom: 1.5rem; 61 | color: #333; 62 | } 63 | 64 | input[type="text"] { 65 | font-size: 1rem; 66 | padding: 0.6rem; 67 | border: 2px solid #ccc; 68 | border-radius: 5px; 69 | background-color: #fff; 70 | color: #333; 71 | margin-right: 0.6rem; 72 | } 73 | 74 | button { 75 | font-size: 1rem; 76 | padding: 10px 1.5rem; 77 | background-color: #333; 78 | color: #fff; 79 | border: none; 80 | border-radius: 5px; 81 | cursor: pointer; 82 | } 83 | 84 | button:hover { 85 | background-color: #666; 86 | } 87 | 88 | #result { 89 | font-size: 1.3rem; 90 | text-align: center; 91 | margin-top: 1.5rem; 92 | color: #b30ce6; 93 | } 94 | 95 | #game-section, 96 | #reset-btn { 97 | display: none; 98 | } 99 | 100 | #reset-btn { 101 | margin: 0 auto; 102 | } 103 | -------------------------------------------------------------------------------- /16_kanban/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kanban 6 | 7 | 8 | 9 | 10 |

Kanban

11 |
12 |

Crie uma nova tarefa:

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

Para fazer

19 | 23 |
24 |
25 |

Fazendo

26 | 30 |
31 |
32 |

Prontas

33 | 37 |
38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /16_kanban/scripts.js: -------------------------------------------------------------------------------- 1 | const tasks = document.querySelectorAll(".tasks li"); 2 | let draggedTask = null; 3 | 4 | for (let i = 0; i < tasks.length; i++) { 5 | const task = tasks[i]; 6 | 7 | task.addEventListener("dragstart", function (event) { 8 | draggedTask = task; 9 | event.dataTransfer.effectAllowed = "move"; 10 | event.dataTransfer.setData("text/html", task.innerHTML); 11 | task.classList.add("dragging"); 12 | }); 13 | 14 | task.addEventListener("dragend", function () { 15 | draggedTask.classList.remove("dragging"); 16 | draggedTask = null; 17 | }); 18 | } 19 | 20 | const columns = document.querySelectorAll(".tasks"); 21 | 22 | for (let i = 0; i < columns.length; i++) { 23 | const column = columns[i]; 24 | 25 | column.addEventListener("dragover", function (event) { 26 | event.preventDefault(); 27 | event.dataTransfer.dropEffect = "move"; 28 | column.classList.add("dragover"); 29 | }); 30 | 31 | column.addEventListener("dragleave", function () { 32 | column.classList.remove("dragover"); 33 | }); 34 | 35 | column.addEventListener("drop", function (event) { 36 | event.preventDefault(); 37 | const task = document.createElement("li"); 38 | task.innerHTML = event.dataTransfer.getData("text/html"); 39 | task.setAttribute("draggable", true); 40 | task.addEventListener("dragstart", function (event) { 41 | draggedTask = task; 42 | event.dataTransfer.effectAllowed = "move"; 43 | event.dataTransfer.setData("text/html", task.innerHTML); 44 | task.classList.add("dragging"); 45 | }); 46 | column.appendChild(task); 47 | column.classList.remove("dragover"); 48 | 49 | // Remove task from previous column 50 | const previousColumn = draggedTask.parentNode; 51 | previousColumn.removeChild(draggedTask); 52 | }); 53 | } 54 | 55 | const addTaskForm = document.querySelector("#add-task-form"); 56 | const addTaskInput = addTaskForm.querySelector("input"); 57 | 58 | addTaskForm.addEventListener("submit", function (event) { 59 | event.preventDefault(); 60 | const newTaskText = addTaskInput.value.trim(); 61 | if (newTaskText !== "") { 62 | const newTask = document.createElement("li"); 63 | newTask.innerHTML = newTaskText; 64 | newTask.setAttribute("draggable", true); 65 | newTask.addEventListener("dragstart", function (event) { 66 | draggedTask = newTask; 67 | event.dataTransfer.effectAllowed = "move"; 68 | event.dataTransfer.setData("text/html", newTask.innerHTML); 69 | newTask.classList.add("dragging"); 70 | }); 71 | document.querySelector("#todo").appendChild(newTask); 72 | addTaskInput.value = ""; 73 | } 74 | }); 75 | -------------------------------------------------------------------------------- /16_kanban/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Helvetica; 3 | } 4 | 5 | h1 { 6 | text-align: center; 7 | } 8 | 9 | .kanban { 10 | display: flex; 11 | justify-content: space-around; 12 | margin-top: 1.5rem; 13 | } 14 | 15 | .column { 16 | width: 30%; 17 | background-color: #eee; 18 | border-radius: 10px; 19 | padding: 0.5rem; 20 | } 21 | 22 | .tasks { 23 | list-style: none; 24 | margin: 0; 25 | padding: 1rem; 26 | border: 2px dashed #ccc; 27 | border-radius: 5px; 28 | min-height: 100px; 29 | } 30 | 31 | .tasks li { 32 | background-color: #fff; 33 | padding: 10px; 34 | margin: 10px 0; 35 | border-radius: 5px; 36 | cursor: move; 37 | } 38 | 39 | .tasks li.dragging { 40 | opacity: 0.5; 41 | } 42 | 43 | .tasks li:hover { 44 | background-color: #f5f5f5; 45 | } 46 | 47 | h2 { 48 | text-align: center; 49 | } 50 | 51 | #add-task-form { 52 | margin-bottom: 1.5rem; 53 | margin-left: 1rem; 54 | } 55 | 56 | #add-task-form input[type="text"] { 57 | padding: 0.3rem; 58 | margin-right: 0.5rem; 59 | border-radius: 3px; 60 | border: 1px solid #ccc; 61 | } 62 | 63 | #add-task-form button[type="submit"] { 64 | padding: 0.3rem 1rem; 65 | background-color: #2196f3; 66 | color: #fff; 67 | border: none; 68 | border-radius: 3px; 69 | cursor: pointer; 70 | } 71 | -------------------------------------------------------------------------------- /17_popup_de_saida/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Pop up de saída 6 | 7 | 8 | 9 | 10 |

Bem-vindo ao nosso site!

11 |

Continue navegando...

12 | 13 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /17_popup_de_saida/scripts.js: -------------------------------------------------------------------------------- 1 | // obtém o pop-up 2 | const popup = document.querySelector("#popup"); 3 | 4 | // obtém o botão de cancelar 5 | const cancelButton = document.querySelector("#cancel-button"); 6 | 7 | // remove exibição de pop up ao entrar na pagina 8 | localStorage.removeItem("popupDisplayed"); 9 | 10 | // adiciona listener de clique no botão de cancelar 11 | cancelButton.addEventListener("click", () => { 12 | // fecha o pop-up 13 | popup.style.display = "none"; 14 | // armazena o valor indicando que o pop-up já foi exibido 15 | localStorage.setItem("popupDisplayed", "true"); 16 | }); 17 | 18 | // adiciona listener de saída do cursor da página 19 | 20 | document.addEventListener("mouseout", (event) => { 21 | // verifica se o pop-up já foi exibido nesta visita 22 | const popupDisplayed = localStorage.getItem("popupDisplayed"); 23 | 24 | if (!popupDisplayed) { 25 | // verifica se o cursor do mouse saiu da página 26 | if (event.relatedTarget === null) { 27 | // exibe o pop-up 28 | popup.style.display = "block"; 29 | } 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /17_popup_de_saida/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Cambria; 3 | } 4 | 5 | .popup { 6 | position: fixed; 7 | top: 0; 8 | left: 0; 9 | right: 0; 10 | bottom: 0; 11 | background-color: rgba(0, 0, 0, 0.5); 12 | display: none; 13 | } 14 | 15 | .popup-content { 16 | position: absolute; 17 | top: 50%; 18 | left: 50%; 19 | transform: translate(-50%, -50%); 20 | background-color: #fff; 21 | padding: 1.5rem; 22 | border-radius: 5px; 23 | text-align: center; 24 | } 25 | 26 | .popup-buttons { 27 | margin-top: 1.5rem; 28 | } 29 | 30 | .popup-buttons button, 31 | .popup-buttons a { 32 | background-color: #007bff; 33 | color: #fff; 34 | padding: 0.6rem 1.5rem; 35 | border-radius: 5px; 36 | margin: 0 1.5rem; 37 | border: none; 38 | text-decoration: none; 39 | cursor: pointer; 40 | } 41 | -------------------------------------------------------------------------------- /18_verificador_de_palindromo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Verificador de Palíndromo 6 | 7 | 8 | 9 | 10 |

Verificador de Palíndromo

11 |
12 | 13 | 14 | 15 |

16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /18_verificador_de_palindromo/scripts.js: -------------------------------------------------------------------------------- 1 | const botaoVerificar = document.querySelector("#botao-verificar"); 2 | const palavraInput = document.querySelector("#palavra"); 3 | const resultado = document.querySelector("#resultado"); 4 | 5 | botaoVerificar.addEventListener("click", () => { 6 | verificarPalindromo(); 7 | }); 8 | 9 | palavraInput.addEventListener("keyup", (event) => { 10 | if (event.key === "Enter") { 11 | event.preventDefault(); 12 | verificarPalindromo(); 13 | } 14 | }); 15 | 16 | function verificarPalindromo() { 17 | const palavra = palavraInput.value; 18 | const palavraInvertida = palavra.split("").reverse().join(""); 19 | if (palavra.toLowerCase() === palavraInvertida.toLowerCase()) { 20 | resultado.innerHTML = `A palavra ${palavra} é um palíndromo!`; 21 | } else { 22 | resultado.innerHTML = `A palavra ${palavra} não é um palíndromo.`; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /18_verificador_de_palindromo/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Arial; 3 | } 4 | 5 | h1 { 6 | text-align: center; 7 | } 8 | 9 | .container { 10 | max-width: 400px; 11 | margin: 0 auto; 12 | } 13 | 14 | label { 15 | display: block; 16 | margin-bottom: 10px; 17 | } 18 | 19 | input { 20 | padding: 0.4rem; 21 | font-size: 1rem; 22 | width: 100%; 23 | box-sizing: border-box; 24 | margin-bottom: 0.6rem; 25 | } 26 | 27 | button { 28 | padding: 0.8rem; 29 | font-size: 1rem; 30 | background-color: #4caf50; 31 | color: #fff; 32 | border: none; 33 | border-radius: 5px; 34 | transition: 0.4s; 35 | cursor: pointer; 36 | } 37 | 38 | button:hover { 39 | background-color: #3e8e41; 40 | } 41 | 42 | #resultado { 43 | font-size: 1.5rem; 44 | margin-top: 2rem; 45 | } 46 | -------------------------------------------------------------------------------- /19_todo_list/css/styles.css: -------------------------------------------------------------------------------- 1 | /* Geral */ 2 | * { 3 | padding: 0; 4 | margin: 0; 5 | box-sizing: border-box; 6 | font-family: Verdana; 7 | color: #333; 8 | } 9 | 10 | body { 11 | background-image: url("../img/bg.jpg"); 12 | background-position: center; 13 | background-size: cover; 14 | } 15 | 16 | button { 17 | background-color: #fdfdfd; 18 | color: #102f5e; 19 | border: 2px solid #102f5e; 20 | padding: 0.3rem 0.6rem; 21 | font-size: 1rem; 22 | cursor: pointer; 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | transition: 0.4s; 27 | } 28 | 29 | button:hover { 30 | background-color: #102f5e; 31 | } 32 | 33 | button:hover > i { 34 | color: #fff; 35 | } 36 | 37 | button i { 38 | pointer-events: none; 39 | color: #102f5e; 40 | font-weight: bold; 41 | } 42 | 43 | input, 44 | select { 45 | padding: 0.25rem 0.5rem; 46 | } 47 | 48 | .hide { 49 | display: none; 50 | } 51 | 52 | /* Todo Header */ 53 | .todo-container { 54 | max-width: 450px; 55 | margin: 3rem auto 0; 56 | background-color: #fdfdfd; 57 | padding: 1.5rem; 58 | border-radius: 15px; 59 | } 60 | 61 | .todo-container header { 62 | text-align: center; 63 | padding: 0 1rem 1rem; 64 | border-bottom: 1px solid #ccc; 65 | } 66 | 67 | /* Todo Form */ 68 | #todo-form, 69 | #edit-form { 70 | padding: 1rem; 71 | border-bottom: 1px solid #ccc; 72 | } 73 | 74 | #todo-form p, 75 | #edit-form p { 76 | margin-bottom: 0.5rem; 77 | font-weight: bold; 78 | } 79 | 80 | .form-control { 81 | display: flex; 82 | } 83 | 84 | .form-control input { 85 | margin-right: 0.3rem; 86 | } 87 | 88 | #cancel-edit-btn { 89 | margin-top: 1rem; 90 | color: #444; 91 | } 92 | 93 | /* Todo Toolbar */ 94 | #toolbar { 95 | padding: 1rem; 96 | border-bottom: 1px solid #ccc; 97 | display: flex; 98 | } 99 | 100 | #toolbar h4 { 101 | margin-bottom: 0.5rem; 102 | } 103 | 104 | #search { 105 | border-right: 1px solid #ddd; 106 | padding-right: 1rem; 107 | margin-right: 1rem; 108 | width: 65%; 109 | display: flex; 110 | flex-direction: column; 111 | } 112 | 113 | #search form { 114 | display: flex; 115 | } 116 | 117 | #search input { 118 | width: 100%; 119 | margin-right: 0.3rem; 120 | } 121 | 122 | #filter { 123 | width: 35%; 124 | display: flex; 125 | flex-direction: column; 126 | } 127 | 128 | #filter select { 129 | flex: 1; 130 | } 131 | 132 | /* Todo List */ 133 | .todo { 134 | display: flex; 135 | justify-content: space-around; 136 | align-items: center; 137 | padding: 1rem 1rem; 138 | border-bottom: 1px solid #ddd; 139 | transition: 0.3s; 140 | } 141 | 142 | .todo h3 { 143 | flex: 1; 144 | font-size: 0.9rem; 145 | } 146 | 147 | .todo button { 148 | margin-left: 0.4rem; 149 | } 150 | 151 | .done { 152 | background: #395169; 153 | } 154 | 155 | .done h3 { 156 | color: #fff; 157 | text-decoration: line-through; 158 | font-style: italic; 159 | } 160 | -------------------------------------------------------------------------------- /19_todo_list/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_javascript/9af0e18d4ebb02c1a6dc226b1a0ddacd65043e40/19_todo_list/img/bg.jpg -------------------------------------------------------------------------------- /19_todo_list/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Todo Avançado 8 | 9 | 16 | 17 | 18 | 19 |
20 |
21 |

Todo Avançado

22 |
23 |
24 |

Adicione sua tarefa

25 |
26 | 31 | 34 |
35 |
36 |
37 |

Edite sua tarefa

38 |
39 | 40 | 43 |
44 | 45 |
46 |
47 | 56 |
57 |

Filtrar:

58 | 63 |
64 |
65 |
66 | 81 |
82 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /19_todo_list/js/scripts.js: -------------------------------------------------------------------------------- 1 | // Seleção de elementos 2 | const todoForm = document.querySelector("#todo-form"); 3 | const todoInput = document.querySelector("#todo-input"); 4 | const todoList = document.querySelector("#todo-list"); 5 | const editForm = document.querySelector("#edit-form"); 6 | const editInput = document.querySelector("#edit-input"); 7 | const cancelEditBtn = document.querySelector("#cancel-edit-btn"); 8 | const searchInput = document.querySelector("#search-input"); 9 | const eraseBtn = document.querySelector("#erase-button"); 10 | const filterBtn = document.querySelector("#filter-select"); 11 | 12 | let oldInputValue; 13 | 14 | // Funções 15 | const saveTodo = (text, done = 0, save = 1) => { 16 | const todo = document.createElement("div"); 17 | todo.classList.add("todo"); 18 | 19 | const todoTitle = document.createElement("h3"); 20 | todoTitle.innerText = text; 21 | todo.appendChild(todoTitle); 22 | 23 | const doneBtn = document.createElement("button"); 24 | doneBtn.classList.add("finish-todo"); 25 | doneBtn.innerHTML = ''; 26 | todo.appendChild(doneBtn); 27 | 28 | const editBtn = document.createElement("button"); 29 | editBtn.classList.add("edit-todo"); 30 | editBtn.innerHTML = ''; 31 | todo.appendChild(editBtn); 32 | 33 | const deleteBtn = document.createElement("button"); 34 | deleteBtn.classList.add("remove-todo"); 35 | deleteBtn.innerHTML = ''; 36 | todo.appendChild(deleteBtn); 37 | 38 | // Utilizando dados da localStorage 39 | if (done) { 40 | todo.classList.add("done"); 41 | } 42 | 43 | if (save) { 44 | saveTodoLocalStorage({ text, done: 0 }); 45 | } 46 | 47 | todoList.appendChild(todo); 48 | 49 | todoInput.value = ""; 50 | }; 51 | 52 | const toggleForms = () => { 53 | editForm.classList.toggle("hide"); 54 | todoForm.classList.toggle("hide"); 55 | todoList.classList.toggle("hide"); 56 | }; 57 | 58 | const updateTodo = (text) => { 59 | const todos = document.querySelectorAll(".todo"); 60 | 61 | todos.forEach((todo) => { 62 | let todoTitle = todo.querySelector("h3"); 63 | 64 | if (todoTitle.innerText === oldInputValue) { 65 | todoTitle.innerText = text; 66 | 67 | // Utilizando dados da localStorage 68 | updateTodoLocalStorage(oldInputValue, text); 69 | } 70 | }); 71 | }; 72 | 73 | const getSearchedTodos = (search) => { 74 | const todos = document.querySelectorAll(".todo"); 75 | 76 | todos.forEach((todo) => { 77 | const todoTitle = todo.querySelector("h3").innerText.toLowerCase(); 78 | 79 | todo.style.display = "flex"; 80 | 81 | console.log(todoTitle); 82 | 83 | if (!todoTitle.includes(search)) { 84 | todo.style.display = "none"; 85 | } 86 | }); 87 | }; 88 | 89 | const filterTodos = (filterValue) => { 90 | const todos = document.querySelectorAll(".todo"); 91 | 92 | switch (filterValue) { 93 | case "all": 94 | todos.forEach((todo) => (todo.style.display = "flex")); 95 | 96 | break; 97 | 98 | case "done": 99 | todos.forEach((todo) => 100 | todo.classList.contains("done") 101 | ? (todo.style.display = "flex") 102 | : (todo.style.display = "none") 103 | ); 104 | 105 | break; 106 | 107 | case "todo": 108 | todos.forEach((todo) => 109 | !todo.classList.contains("done") 110 | ? (todo.style.display = "flex") 111 | : (todo.style.display = "none") 112 | ); 113 | 114 | break; 115 | 116 | default: 117 | break; 118 | } 119 | }; 120 | 121 | // Eventos 122 | todoForm.addEventListener("submit", (e) => { 123 | e.preventDefault(); 124 | 125 | const inputValue = todoInput.value; 126 | 127 | if (inputValue) { 128 | saveTodo(inputValue); 129 | } 130 | }); 131 | 132 | document.addEventListener("click", (e) => { 133 | const targetEl = e.target; 134 | const parentEl = targetEl.closest("div"); 135 | let todoTitle; 136 | 137 | if (parentEl && parentEl.querySelector("h3")) { 138 | todoTitle = parentEl.querySelector("h3").innerText || ""; 139 | } 140 | 141 | if (targetEl.classList.contains("finish-todo")) { 142 | parentEl.classList.toggle("done"); 143 | 144 | updateTodoStatusLocalStorage(todoTitle); 145 | } 146 | 147 | if (targetEl.classList.contains("remove-todo")) { 148 | parentEl.remove(); 149 | 150 | // Utilizando dados da localStorage 151 | removeTodoLocalStorage(todoTitle); 152 | } 153 | 154 | if (targetEl.classList.contains("edit-todo")) { 155 | toggleForms(); 156 | 157 | editInput.value = todoTitle; 158 | oldInputValue = todoTitle; 159 | } 160 | }); 161 | 162 | cancelEditBtn.addEventListener("click", (e) => { 163 | e.preventDefault(); 164 | toggleForms(); 165 | }); 166 | 167 | editForm.addEventListener("submit", (e) => { 168 | e.preventDefault(); 169 | 170 | const editInputValue = editInput.value; 171 | 172 | if (editInputValue) { 173 | updateTodo(editInputValue); 174 | } 175 | 176 | toggleForms(); 177 | }); 178 | 179 | searchInput.addEventListener("keyup", (e) => { 180 | const search = e.target.value; 181 | 182 | getSearchedTodos(search); 183 | }); 184 | 185 | eraseBtn.addEventListener("click", (e) => { 186 | e.preventDefault(); 187 | 188 | searchInput.value = ""; 189 | 190 | searchInput.dispatchEvent(new Event("keyup")); 191 | }); 192 | 193 | filterBtn.addEventListener("change", (e) => { 194 | const filterValue = e.target.value; 195 | 196 | filterTodos(filterValue); 197 | }); 198 | 199 | // Local Storage 200 | const getTodosLocalStorage = () => { 201 | const todos = JSON.parse(localStorage.getItem("todos")) || []; 202 | 203 | return todos; 204 | }; 205 | 206 | const loadTodos = () => { 207 | const todos = getTodosLocalStorage(); 208 | 209 | todos.forEach((todo) => { 210 | saveTodo(todo.text, todo.done, 0); 211 | }); 212 | }; 213 | 214 | const saveTodoLocalStorage = (todo) => { 215 | const todos = getTodosLocalStorage(); 216 | 217 | todos.push(todo); 218 | 219 | localStorage.setItem("todos", JSON.stringify(todos)); 220 | }; 221 | 222 | const removeTodoLocalStorage = (todoText) => { 223 | const todos = getTodosLocalStorage(); 224 | 225 | const filteredTodos = todos.filter((todo) => todo.text != todoText); 226 | 227 | localStorage.setItem("todos", JSON.stringify(filteredTodos)); 228 | }; 229 | 230 | const updateTodoStatusLocalStorage = (todoText) => { 231 | const todos = getTodosLocalStorage(); 232 | 233 | todos.map((todo) => 234 | todo.text === todoText ? (todo.done = !todo.done) : null 235 | ); 236 | 237 | localStorage.setItem("todos", JSON.stringify(todos)); 238 | }; 239 | 240 | const updateTodoLocalStorage = (todoOldText, todoNewText) => { 241 | const todos = getTodosLocalStorage(); 242 | 243 | todos.map((todo) => 244 | todo.text === todoOldText ? (todo.text = todoNewText) : null 245 | ); 246 | 247 | localStorage.setItem("todos", JSON.stringify(todos)); 248 | }; 249 | 250 | loadTodos(); 251 | -------------------------------------------------------------------------------- /1_conversor_de_unidades/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Conversor de Unidades 8 | 9 | 10 | 11 | 12 |
13 |

Conversor de Unidades

14 |
15 | 16 | 17 |
18 |
19 | 20 | 26 |
27 |
28 | 29 | 35 |
36 | 37 |
38 | 39 | 40 |
41 |
42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /1_conversor_de_unidades/scripts.js: -------------------------------------------------------------------------------- 1 | // Obter referências aos elementos HTML 2 | const inputElement = document.querySelector("#input"); 3 | const fromElement = document.querySelector("#from"); 4 | const toElement = document.querySelector("#to"); 5 | const outputElement = document.querySelector("#output"); 6 | const convertButton = document.querySelector("#convert-btn"); 7 | const messageElement = document.querySelector("#message"); 8 | 9 | // Função para converter as unidades 10 | function convert() { 11 | // Obter os valores das unidades de entrada e saída 12 | const fromValue = fromElement.value; 13 | const toValue = toElement.value; 14 | 15 | // Verificar se as unidades de entrada e saída são iguais 16 | if (fromValue === toValue) { 17 | outputElement.value = inputElement.value; 18 | messageElement.textContent = ""; 19 | return; 20 | } 21 | 22 | // Converter o valor de entrada para metros 23 | let meters; 24 | switch (fromValue) { 25 | case "m": 26 | meters = inputElement.value; 27 | break; 28 | case "km": 29 | meters = inputElement.value * 1000; 30 | break; 31 | case "cm": 32 | meters = inputElement.value / 100; 33 | break; 34 | case "mm": 35 | meters = inputElement.value / 1000; 36 | break; 37 | } 38 | 39 | // Converter os metros para a unidade de saída 40 | let result; 41 | switch (toValue) { 42 | case "m": 43 | result = meters; 44 | break; 45 | case "km": 46 | result = meters / 1000; 47 | break; 48 | case "cm": 49 | result = meters * 100; 50 | break; 51 | case "mm": 52 | result = meters * 1000; 53 | break; 54 | } 55 | 56 | // Exibir o resultado na caixa de saída 57 | output.value = result.toFixed(2); 58 | 59 | // Exibir a mensagem de conversão 60 | const fromLabel = fromElement.options[fromElement.selectedIndex].text; 61 | const toLabel = toElement.options[toElement.selectedIndex].text; 62 | const message = `${ 63 | inputElement.value 64 | } ${fromLabel} equivalem a ${result.toFixed(2)} ${toLabel}`; 65 | messageElement.textContent = message; 66 | } 67 | 68 | // Adicionar um ouvinte de eventos ao botão de conversão 69 | convertButton.addEventListener("click", convert); 70 | -------------------------------------------------------------------------------- /1_conversor_de_unidades/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | font-family: Arial, sans-serif; 9 | background-color: #f4f4f4; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | height: 100vh; 14 | } 15 | 16 | .container { 17 | background-color: #fff; 18 | border-radius: 10px; 19 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); 20 | padding: 20px; 21 | width: 400px; 22 | } 23 | 24 | h1 { 25 | font-size: 24px; 26 | margin-bottom: 20px; 27 | text-align: center; 28 | } 29 | 30 | .input-group { 31 | display: flex; 32 | flex-direction: column; 33 | margin-bottom: 15px; 34 | } 35 | 36 | .input-group label { 37 | margin-bottom: 5px; 38 | font-weight: bold; 39 | } 40 | 41 | select, 42 | input[type="number"], 43 | input[type="text"] { 44 | padding: 10px; 45 | border-radius: 5px; 46 | border: none; 47 | font-size: 16px; 48 | background-color: #eee; 49 | } 50 | 51 | button { 52 | background-color: #4caf50; 53 | border-radius: 5px; 54 | border: none; 55 | color: #fff; 56 | cursor: pointer; 57 | font-size: 16px; 58 | padding: 10px 20px; 59 | opacity: 0.7; 60 | transition: 0.4s; 61 | } 62 | 63 | button:hover { 64 | opacity: 1; 65 | } 66 | 67 | .output-group { 68 | display: flex; 69 | flex-direction: column; 70 | margin-top: 20px; 71 | } 72 | 73 | .output-group label { 74 | margin-bottom: 5px; 75 | font-weight: bold; 76 | } 77 | 78 | #message { 79 | font-size: 14px; 80 | color: #4caf50; 81 | margin-top: 10px; 82 | text-align: center; 83 | } 84 | -------------------------------------------------------------------------------- /20_gerador_de_senha/css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: "Montserrat", sans-serif; 3 | padding: 0; 4 | margin: 0; 5 | box-sizing: border-box; 6 | color: #333; 7 | } 8 | 9 | html, 10 | body { 11 | height: auto; 12 | min-height: 100vh; 13 | } 14 | 15 | html { 16 | background: linear-gradient(180deg, rgba(234, 2, 255, 1) 0%, #0097ff 100%); 17 | } 18 | 19 | input:focus { 20 | outline: none; 21 | } 22 | 23 | #register-container { 24 | max-width: 900px; 25 | min-height: 80vh; 26 | margin: 2rem auto; 27 | display: flex; 28 | box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, 29 | rgba(0, 0, 0, 0.3) 0px 8px 16px -8px; 30 | } 31 | 32 | #register-banner { 33 | background: url("../img/bg-form.jpg"); 34 | background-size: cover; 35 | background-position: center; 36 | background-blend-mode: saturation; 37 | width: 50%; 38 | } 39 | 40 | #banner-layer { 41 | background-color: rgba(25, 11, 122, 0.7); 42 | height: 100%; 43 | padding: 2rem; 44 | display: flex; 45 | flex-direction: column; 46 | justify-content: center; 47 | } 48 | 49 | #register-banner h1 { 50 | font-size: 4rem; 51 | word-spacing: 50rem; 52 | margin-bottom: 1.5rem; 53 | color: #fff; 54 | } 55 | 56 | #register-form { 57 | background-color: #fdfdfd; 58 | padding: 2rem; 59 | width: 50%; 60 | display: flex; 61 | flex-direction: column; 62 | } 63 | 64 | #register-form h2 { 65 | text-align: center; 66 | margin-bottom: 1rem; 67 | font-size: 2.2rem; 68 | } 69 | 70 | #register-form p { 71 | text-align: center; 72 | margin-bottom: 1.5rem; 73 | color: #aaa; 74 | font-size: 0.8rem; 75 | } 76 | 77 | .form-control { 78 | margin-bottom: 1rem; 79 | display: flex; 80 | flex-direction: column; 81 | } 82 | 83 | .form-control label { 84 | font-weight: bold; 85 | margin-bottom: 0.6rem; 86 | } 87 | 88 | .form-control input { 89 | border: none; 90 | border-bottom: 1px solid #aaa; 91 | padding: 0.6rem 0; 92 | } 93 | 94 | #open-generate-password { 95 | font-weight: bold; 96 | cursor: pointer; 97 | transition: 0.4s; 98 | } 99 | 100 | #open-generate-password:hover { 101 | color: #0099ff; 102 | } 103 | 104 | #register-form #generated-password { 105 | border: 1px solid #0097ff; 106 | border-radius: 4px; 107 | font-weight: bold; 108 | font-size: 1.2rem; 109 | padding: 0.4rem; 110 | color: #333; 111 | text-align: center; 112 | margin-bottom: 1rem; 113 | display: none; 114 | } 115 | 116 | #register-form #generated-password p { 117 | color: #81d340; 118 | margin-bottom: 0.5rem; 119 | } 120 | 121 | #register-form input[type="submit"], 122 | #generate-options button, 123 | #copy-password { 124 | background-color: #81d340; 125 | color: #fff; 126 | opacity: 0.8; 127 | border: none; 128 | border-radius: 2rem; 129 | padding: 1rem 1.4rem; 130 | max-width: 150px; 131 | cursor: pointer; 132 | align-self: flex-end; 133 | transition: 0.4s; 134 | } 135 | 136 | #register-form input[type="submit"]:hover { 137 | opacity: 1; 138 | } 139 | 140 | /* Comu */ 141 | #generate-options { 142 | margin-bottom: 1rem; 143 | padding: 1em; 144 | border: 1px solid #ddd; 145 | } 146 | 147 | #generate-options .form-control { 148 | flex-direction: row; 149 | justify-content: space-between; 150 | align-items: center; 151 | } 152 | 153 | #generate-options label { 154 | font-size: 0.8rem; 155 | } 156 | 157 | #generate-options input[type="text"] { 158 | max-width: 30px; 159 | text-align: center; 160 | border: 1px solid #ccc; 161 | } 162 | 163 | #generate-options button { 164 | background-color: #0097ff; 165 | display: block; 166 | margin: 0 auto; 167 | } 168 | 169 | #copy-password { 170 | margin: 1rem auto; 171 | background-color: transparent; 172 | color: #81d340; 173 | border: 2px solid #81d340; 174 | padding: 0.5rem 1rem; 175 | } 176 | 177 | .hide { 178 | display: none; 179 | } 180 | -------------------------------------------------------------------------------- /20_gerador_de_senha/img/bg-form.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_javascript/9af0e18d4ebb02c1a6dc226b1a0ddacd65043e40/20_gerador_de_senha/img/bg-form.jpg -------------------------------------------------------------------------------- /20_gerador_de_senha/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Gerador de Senha 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 |
19 |
20 | 23 |
24 |
25 |

Crie sua conta:

26 |

Registre-se para utilizar todas as funcionalidades do sistema

27 |
28 | 29 | 35 |
36 |
37 | 38 | 44 |
45 |
46 | 47 | 53 |
54 |

55 | Quer ajuda para criar uma senha segura? 56 | Clique aqui. 57 |

58 |
59 |

Selecione as opções que você deseja:

60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 |
69 | 70 | 71 |
72 |
73 | 74 | 75 |
76 | 77 |
78 |
79 |

Aqui está a sua senha:

80 |

81 | 82 |
83 |
84 | 85 | 91 |
92 | 93 |
94 |
95 | 96 | 97 | -------------------------------------------------------------------------------- /20_gerador_de_senha/js/scripts.js: -------------------------------------------------------------------------------- 1 | // Seleção de elementos 2 | const generatePasswordButton = document.querySelector("#generate-password"); 3 | const generatedPasswordElement = document.querySelector("#generated-password"); 4 | 5 | // Após refatoração 6 | const openCloseGeneratorButton = document.querySelector( 7 | "#open-generate-password" 8 | ); 9 | const generatePasswordContainer = document.querySelector("#generate-options"); 10 | const lengthInput = document.querySelector("#length"); 11 | const lettersInput = document.querySelector("#letters"); 12 | const numbersInput = document.querySelector("#numbers"); 13 | const symbolsInput = document.querySelector("#symbols"); 14 | const copyPasswordButton = document.querySelector("#copy-password"); 15 | 16 | // Funções 17 | const getLetterLowerCase = () => { 18 | return String.fromCharCode(Math.floor(Math.random() * 26) + 97); 19 | }; 20 | 21 | const getLetterUpperCase = () => { 22 | return String.fromCharCode(Math.floor(Math.random() * 26) + 65); 23 | }; 24 | 25 | const getNumber = () => { 26 | return Math.floor(Math.random() * 11).toString(); 27 | }; 28 | 29 | const getSymbol = () => { 30 | const symbols = "(){}[]=<>/,.!@#$%^&*"; 31 | return symbols[Math.floor(Math.random() * symbols.length)]; 32 | }; 33 | 34 | const generatePassword = ( 35 | getLetterLowerCase, 36 | getLetterUpperCase, 37 | getNumber, 38 | getSymbol 39 | ) => { 40 | let password = ""; 41 | 42 | const passwordLength = lengthInput.value; 43 | 44 | // Após refatoração 45 | const generators = []; 46 | 47 | if (lettersInput.checked) { 48 | generators.push(getLetterLowerCase, getLetterUpperCase); 49 | } 50 | 51 | if (numbersInput.checked) { 52 | generators.push(getNumber); 53 | } 54 | 55 | if (symbolsInput.checked) { 56 | generators.push(getSymbol); 57 | } 58 | 59 | if (generators.length === 0) { 60 | return; 61 | } 62 | 63 | for (i = 0; i < passwordLength; i = i + generators.length) { 64 | generators.forEach(() => { 65 | const randomValue = 66 | generators[Math.floor(Math.random() * generators.length)](); 67 | 68 | password += randomValue; 69 | }); 70 | } 71 | 72 | password = password.slice(0, passwordLength); 73 | 74 | generatedPasswordElement.style.display = "block"; 75 | generatedPasswordElement.querySelector("h4").innerText = password; 76 | }; 77 | 78 | // Eventos 79 | generatePasswordButton.addEventListener("click", (e) => { 80 | e.preventDefault(); 81 | 82 | generatePassword( 83 | getLetterLowerCase, 84 | getLetterUpperCase, 85 | getNumber, 86 | getSymbol 87 | ); 88 | }); 89 | 90 | openCloseGeneratorButton.addEventListener("click", () => { 91 | generatePasswordContainer.classList.toggle("hide"); 92 | }); 93 | 94 | copyPasswordButton.addEventListener("click", (e) => { 95 | e.preventDefault(); 96 | 97 | const password = generatedPasswordElement.querySelector("h4").innerText; 98 | 99 | navigator.clipboard.writeText(password).then(function () { 100 | copyPasswordButton.innerText = "Senha copiada com sucesso!"; 101 | 102 | setTimeout(() => { 103 | copyPasswordButton.innerText = "Copiar"; 104 | }, 1000); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /21_contador_caracteres_extra/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Contador de caracteres e palavras 5 | 6 | 7 | 8 | 9 |
10 |

Contador de caracteres e palavras

11 |
12 | 13 | 14 |
15 |
0 caractere(s)
16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /21_contador_caracteres_extra/scripts.js: -------------------------------------------------------------------------------- 1 | const input = document.getElementById("input"); 2 | const counter = document.querySelector(".counter"); 3 | const toggleButton = document.getElementById("toggle"); 4 | 5 | let mode = "characters"; 6 | 7 | toggleButton.addEventListener("click", () => { 8 | if (mode === "characters") { 9 | mode = "words"; 10 | toggleButton.textContent = "Contar caracteres"; 11 | } else { 12 | mode = "characters"; 13 | toggleButton.textContent = "Contar palavras"; 14 | } 15 | input.dispatchEvent(new Event("input")); 16 | }); 17 | 18 | input.addEventListener("input", () => { 19 | let count = 0; 20 | if (mode === "characters") { 21 | count = input.value.length; 22 | counter.textContent = `${count} caractere(s)`; 23 | } else { 24 | const words = input.value.trim().split(/\s+/); 25 | count = input.value.trim() === "" ? 0 : words.length; 26 | counter.textContent = `${count} palavra(s)`; 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /21_contador_caracteres_extra/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Helvetica; 3 | } 4 | 5 | .container { 6 | display: flex; 7 | flex-direction: column; 8 | margin-top: 3rem; 9 | align-items: center; 10 | height: 100vh; 11 | } 12 | 13 | .field { 14 | width: 500px; 15 | margin-bottom: 1rem; 16 | } 17 | 18 | label { 19 | display: block; 20 | font-size: 0.9rem; 21 | margin-bottom: 0.5rem; 22 | } 23 | 24 | textarea { 25 | width: 100%; 26 | font-size: 0.9rem; 27 | padding: 0.5rem; 28 | border-radius: 5px; 29 | border: 1px solid #ccc; 30 | } 31 | 32 | .counter { 33 | font-size: 0.8rem; 34 | color: #666; 35 | margin-bottom: 1rem; 36 | } 37 | 38 | #toggle { 39 | background-color: #4285f4; 40 | color: #fff; 41 | font-size: 1rem; 42 | padding: 0.4rem 1rem; 43 | border-radius: 5px; 44 | border: none; 45 | cursor: pointer; 46 | transition: 0.3s; 47 | } 48 | 49 | #toggle:hover { 50 | background-color: #0069d9; 51 | } 52 | -------------------------------------------------------------------------------- /22_teste_velocidade_digitacao/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Teste de Velocidade de Digitação 5 | 6 | 7 | 8 | 9 |

Teste de Velocidade de Digitação

10 |

11 | Para começar o teste, clique na caixa de texto abaixo e comece a digitar o 12 | texto exibido acima dela. O cronômetro começará automaticamente assim que 13 | você começar a digitar. Quando você terminar de digitar o texto 14 | corretamente, o tempo que você levou será exibido. 15 |

16 |
17 | 23 |
24 | 25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /22_teste_velocidade_digitacao/scripts.js: -------------------------------------------------------------------------------- 1 | const texto = document.querySelector("#texto"); 2 | const entrada = document.querySelector("#entrada"); 3 | const reiniciar = document.querySelector("#reiniciar"); 4 | const resultado = document.querySelector("#resultado"); 5 | const historico = document.querySelector("#historico"); 6 | const alternarTemaBtn = document.querySelector("#alternarTema"); 7 | 8 | const textos = [ 9 | "Exemplo de texto para digitar.", 10 | "Outro exemplo de texto para digitar.", 11 | "Mais um exemplo de texto para digitar.", 12 | "Digite isso.", 13 | "Você pode digitar isso aqui.", 14 | ]; 15 | 16 | function novoTexto() { 17 | const index = Math.floor(Math.random() * textos.length); 18 | texto.textContent = textos[index]; 19 | } 20 | 21 | function iniciar() { 22 | const statusDoTeste = JSON.parse(localStorage.getItem("testeEmAndamento")); 23 | 24 | if (!statusDoTeste) { 25 | localStorage.setItem("tempoInicial", new Date().getTime()); 26 | localStorage.setItem("testeEmAndamento", true); 27 | } 28 | } 29 | 30 | function verificar() { 31 | const tempoFinal = new Date().getTime(); 32 | const tempoGasto = 33 | (tempoFinal - parseInt(localStorage.getItem("tempoInicial"))) / 1000; 34 | resultado.textContent = `Parabéns! Você levou ${tempoGasto} segundos.`; 35 | // Incrementar histórico de pontuações 36 | adicionarAoHistorico(texto.textContent, tempoGasto); 37 | 38 | localStorage.setItem("testeEmAndamento", false); 39 | entrada.value = ""; 40 | novoTexto(); 41 | } 42 | 43 | function adicionarAoHistorico(textoDigitado, tempoGasto) { 44 | const itemHistorico = document.createElement("p"); 45 | itemHistorico.textContent = `Texto: "${textoDigitado}" - Tempo: ${tempoGasto} segundos`; 46 | historico.appendChild(itemHistorico); 47 | } 48 | 49 | function atualizarTeste() { 50 | iniciar(); 51 | if (entrada.value === texto.textContent) { 52 | verificar(); 53 | } 54 | } 55 | 56 | function reiniciarTeste() { 57 | entrada.value = ""; 58 | resultado.textContent = ""; 59 | novoTexto(); 60 | localStorage.setItem("testeEmAndamento", false); 61 | historico.innerHTML = ""; 62 | } 63 | 64 | function alternarTema() { 65 | const body = document.body; 66 | 67 | if (body.classList.contains("escuro")) { 68 | body.classList.remove("escuro"); 69 | body.classList.add("claro"); 70 | } else { 71 | body.classList.remove("claro"); 72 | body.classList.add("escuro"); 73 | } 74 | } 75 | 76 | entrada.addEventListener("keyup", atualizarTeste); 77 | reiniciar.addEventListener("click", reiniciarTeste); 78 | 79 | alternarTemaBtn.addEventListener("click", alternarTema); 80 | 81 | novoTexto(); 82 | -------------------------------------------------------------------------------- /22_teste_velocidade_digitacao/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | max-width: 800px; 4 | margin: 0 auto; 5 | padding: 1.5rem; 6 | text-align: center; 7 | } 8 | 9 | h1 { 10 | font-size: 2rem; 11 | margin-bottom: 0.5rem; 12 | } 13 | 14 | .instrucoes { 15 | font-size: 1rem; 16 | margin-bottom: 1rem; 17 | text-align: justify; 18 | line-height: 1.5; 19 | } 20 | 21 | #texto { 22 | font-size: 1.2rem; 23 | background-color: #f3f3f3; 24 | padding: 1.2rem; 25 | margin-bottom: 1rem; 26 | } 27 | 28 | #entrada { 29 | width: 100%; 30 | padding: .8rem; 31 | margin-bottom: 1rem; 32 | } 33 | 34 | #resultado { 35 | font-size: 1.2rem; 36 | margin-bottom: 1rem; 37 | } 38 | 39 | button { 40 | font-size: 1rem; 41 | padding: .6rem 1.2rem; 42 | background-color: #007bff; 43 | color: #fff; 44 | border: none; 45 | cursor: pointer; 46 | transition: background-color 0.3s; 47 | } 48 | 49 | button:hover { 50 | background-color: #0056b3; 51 | } 52 | 53 | #alternarTema { 54 | position: fixed; 55 | top: 10px; 56 | right: 10px; 57 | } 58 | 59 | /* Tema claro */ 60 | .claro { 61 | background-color: #ffffff; 62 | color: #333333; 63 | } 64 | 65 | /* Tema escuro */ 66 | .escuro { 67 | background-color: #333333; 68 | color: #ffffff; 69 | } 70 | 71 | .escuro #texto { 72 | color: #333; 73 | } 74 | -------------------------------------------------------------------------------- /23_todo_react/todo/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { browser: true, es2020: true }, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:react/recommended', 6 | 'plugin:react/jsx-runtime', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 10 | settings: { react: { version: '18.2' } }, 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': 'warn', 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /23_todo_react/todo/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /23_todo_react/todo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /23_todo_react/todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.28", 18 | "@types/react-dom": "^18.0.11", 19 | "@vitejs/plugin-react": "^4.0.0", 20 | "eslint": "^8.38.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.3.4", 24 | "vite": "^4.3.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /23_todo_react/todo/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /23_todo_react/todo/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | background-color: #f4f4f4; 4 | padding: 30px; 5 | color: #333; 6 | background-image: url("./img/bg.jpg"); 7 | background-size: cover; 8 | background-position: center; 9 | } 10 | 11 | .app { 12 | max-width: 600px; 13 | margin: 0 auto; 14 | padding: 0.5rem 2rem; 15 | border-radius: 10px; 16 | background-color: #efefef; 17 | } 18 | 19 | h1 { 20 | text-align: center; 21 | } 22 | 23 | h2 { 24 | margin-bottom: 10px; 25 | } 26 | 27 | p { 28 | margin: 10px 0; 29 | } 30 | 31 | input { 32 | box-sizing: border-box; 33 | } 34 | 35 | .todo-list { 36 | margin-bottom: 1rem; 37 | padding-bottom: 1rem; 38 | border-bottom: 1px solid #555; 39 | } 40 | 41 | .todo { 42 | background-color: #fff; 43 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 44 | padding: 10px; 45 | margin-bottom: 10px; 46 | border-radius: 5px; 47 | display: flex; 48 | justify-content: space-between; 49 | align-items: center; 50 | } 51 | 52 | .todo .category { 53 | font-size: 0.8em; 54 | color: #888; 55 | } 56 | 57 | button { 58 | background-color: #333974; 59 | color: #fff; 60 | padding: 5px 10px; 61 | border: none; 62 | border-radius: 3px; 63 | margin-left: 5px; 64 | cursor: pointer; 65 | opacity: 0.8; 66 | transition: 0.3s; 67 | } 68 | 69 | button:hover { 70 | opacity: 1; 71 | } 72 | 73 | button.complete { 74 | background-color: #5cb85c; 75 | color: #fff; 76 | } 77 | 78 | button.remove { 79 | background-color: #d9534f; 80 | color: #fff; 81 | } 82 | 83 | input[type="text"], 84 | select { 85 | padding: 5px; 86 | margin-bottom: 10px; 87 | border: 1px solid #ddd; 88 | border-radius: 3px; 89 | width: 100%; 90 | } 91 | 92 | .todo-form { 93 | padding-bottom: 1.5rem; 94 | } 95 | 96 | .filter, 97 | .search { 98 | border-bottom: 1px solid #555; 99 | margin-bottom: 1rem; 100 | padding-bottom: 1rem; 101 | } 102 | 103 | .filter-options { 104 | display: flex; 105 | justify-content: space-between; 106 | gap: 3rem; 107 | } 108 | 109 | .filter-options div { 110 | flex: 1 1 0; 111 | } 112 | -------------------------------------------------------------------------------- /23_todo_react/todo/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | import Todo from "./components/Todo"; 4 | 5 | import Search from "./components/Search"; 6 | 7 | import Filter from "./components/Filter"; 8 | 9 | import "./App.css"; 10 | import TodoForm from "./components/TodoForm"; 11 | 12 | const App = () => { 13 | const [todos, setTodos] = useState([ 14 | { 15 | id: 1, 16 | text: "Criar funcionalidade X no sistema", 17 | category: "Trabalho", 18 | isCompleted: false, 19 | }, 20 | { id: 2,text: "Ir para a academia", category: "Pessoal", isCompleted: false }, 21 | { 22 | id: 3, 23 | text: "Estudar React", 24 | category: "Estudos", 25 | isCompleted: false, 26 | }, 27 | ]); 28 | 29 | const [filter, setFilter] = useState("All"); 30 | const [sort, setSort] = useState("Asc"); 31 | 32 | const [search, setSearch] = useState(""); 33 | 34 | const addTodo = (text, category) => { 35 | const newTodos = [...todos, 36 | { id: Math.floor(Math.random() * 1000), text, category, isCompleted: false } 37 | ]; 38 | setTodos(newTodos); 39 | }; 40 | 41 | const removeTodo = (id) => { 42 | const newTodos = [...todos]; 43 | const filteredTodos = newTodos.filter((todo) => todo.id !== id ? todo : null) 44 | setTodos(filteredTodos); 45 | }; 46 | 47 | const completeTodo = (id) => { 48 | const newTodos = [...todos]; 49 | newTodos.map((todo) => todo.id === id ? todo.isCompleted = !todo.isCompleted : todo) 50 | setTodos(newTodos); 51 | }; 52 | 53 | return ( 54 |
55 |

Lista de Tarefas

56 | 57 | 58 |
59 | {todos 60 | .filter((todo) => 61 | filter === "All" 62 | ? true 63 | : filter === "Completed" 64 | ? todo.isCompleted 65 | : !todo.isCompleted 66 | ) 67 | .filter((todo) => 68 | todo.text.toLowerCase().includes(search.toLowerCase()) 69 | ) 70 | .sort((a, b) => 71 | sort === "Asc" 72 | ? a.text.localeCompare(b.text) 73 | : b.text.localeCompare(a.text) 74 | ) 75 | .map((todo, index) => ( 76 | 83 | ))} 84 |
85 | 86 |
87 | ); 88 | }; 89 | 90 | export default App; 91 | -------------------------------------------------------------------------------- /23_todo_react/todo/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /23_todo_react/todo/src/components/Filter.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Filter = ({ filter, setFilter, setSort }) => { 4 | return ( 5 |
6 |

Filtrar:

7 |
8 |
9 |

Status:

10 | 15 |
16 | 17 |
18 |

Ordem alfabética:

19 | 20 | 21 |
22 |
23 |
24 | ); 25 | }; 26 | 27 | export default Filter; 28 | -------------------------------------------------------------------------------- /23_todo_react/todo/src/components/Search.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Search = ({ search, setSearch }) => ( 4 |
5 |

Pesquisar:

6 | setSearch(e.target.value)} 10 | placeholder="Digite para pesquisar..." 11 | /> 12 |
13 | ); 14 | 15 | export default Search; 16 | -------------------------------------------------------------------------------- /23_todo_react/todo/src/components/Todo.jsx: -------------------------------------------------------------------------------- 1 | const Todo = ({ todo, completeTodo, removeTodo }) => { 2 | return ( 3 |
7 |
8 |

{todo.text}

9 |

({todo.category})

10 |
11 |
12 | 15 | 18 |
19 |
20 | ); 21 | }; 22 | 23 | export default Todo; 24 | -------------------------------------------------------------------------------- /23_todo_react/todo/src/components/TodoForm.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const TodoForm = ({ addTodo }) => { 4 | const [value, setValue] = useState(""); 5 | const [category, setCategory] = useState(""); 6 | 7 | const handleSubmit = (e) => { 8 | e.preventDefault(); 9 | if (!value) return; 10 | addTodo(value, category); 11 | setValue(""); 12 | setCategory(""); 13 | }; 14 | 15 | return ( 16 |
17 |

Criar tarefa:

18 |
19 | setValue(e.target.value)} 25 | /> 26 | 32 | 33 |
34 |
35 | ); 36 | }; 37 | 38 | export default TodoForm; 39 | -------------------------------------------------------------------------------- /23_todo_react/todo/src/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_javascript/9af0e18d4ebb02c1a6dc226b1a0ddacd65043e40/23_todo_react/todo/src/img/bg.jpg -------------------------------------------------------------------------------- /23_todo_react/todo/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /23_todo_react/todo/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /2_relogio_digital/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Relógio Digital 8 | 9 | 10 | 11 | 12 |
13 |
14 | 00 15 | : 16 | 00 17 | : 18 | 00 19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /2_relogio_digital/scripts.js: -------------------------------------------------------------------------------- 1 | function updateClock() { 2 | const clockElement = document.querySelector(".clock"); 3 | const hoursElement = clockElement.querySelector(".hours"); 4 | const minutesElement = clockElement.querySelector(".minutes"); 5 | const secondsElement = clockElement.querySelector(".seconds"); 6 | 7 | const now = new Date(); 8 | const hours = now.getHours().toString().padStart(2, "0"); 9 | const minutes = now.getMinutes().toString().padStart(2, "0"); 10 | const seconds = now.getSeconds().toString().padStart(2, "0"); 11 | 12 | hoursElement.textContent = hours; 13 | minutesElement.textContent = minutes; 14 | secondsElement.textContent = seconds; 15 | } 16 | 17 | updateClock(); 18 | setInterval(updateClock, 1000); 19 | -------------------------------------------------------------------------------- /2_relogio_digital/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #333; 3 | font-family: Helvetica; 4 | } 5 | 6 | .container { 7 | height: 100vh; 8 | width: 100%; 9 | padding-top: 5rem; 10 | display: flex; 11 | justify-content: center; 12 | } 13 | 14 | .clock { 15 | font-size: 3rem; 16 | font-weight: bold; 17 | color: #fff; 18 | text-align: center; 19 | border: 2px solid #fff; 20 | border-radius: 1rem; 21 | padding: 1rem 2rem; 22 | height: 80px; 23 | display: flex; 24 | align-items: center; 25 | } 26 | 27 | .clock span { 28 | margin-right: 0.5rem; 29 | } 30 | -------------------------------------------------------------------------------- /3_citacoes/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } 4 | -------------------------------------------------------------------------------- /3_citacoes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Citações 8 | 9 | 10 | 11 | 12 |
13 |

Clique no botão para gerar uma fala interessante...

14 |

15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /3_citacoes/scripts.js: -------------------------------------------------------------------------------- 1 | const quotes = [ 2 | { 3 | quote: "A persistência é o caminho do êxito.", 4 | author: "Charles Chaplin", 5 | }, 6 | { 7 | quote: "A única forma de fazer um grande trabalho é amar o que se faz.", 8 | author: "Steve Jobs", 9 | }, 10 | { 11 | quote: "O sucesso é a soma de pequenos esforços repetidos dia após dia.", 12 | author: "Robert Collier", 13 | }, 14 | { 15 | quote: 16 | "O fracasso é a oportunidade de começar de novo, com mais experiência.", 17 | author: "Henry Ford", 18 | }, 19 | { 20 | quote: "Não espere por oportunidades, crie você mesmo as suas.", 21 | author: "Autor desconhecido", 22 | }, 23 | { 24 | quote: 25 | "O verdadeiro sucesso não é o sucesso financeiro, mas sim o sucesso em ajudar outras pessoas.", 26 | author: "Zig Ziglar", 27 | }, 28 | { 29 | quote: 30 | "O sucesso não é final, o fracasso não é fatal: o que importa é ter coragem para continuar.", 31 | author: "Winston Churchill", 32 | }, 33 | { 34 | quote: 35 | "O sucesso consiste em ir de fracasso em fracasso sem perder o entusiasmo.", 36 | author: "Winston Churchill", 37 | }, 38 | { 39 | quote: "Acredite em si próprio e todo o resto virá naturalmente.", 40 | author: "Elon Musk", 41 | }, 42 | { 43 | quote: 44 | "O fracasso não significa que você não seja bom o suficiente, significa que você precisa se esforçar mais.", 45 | author: "Autor desconhecido", 46 | }, 47 | ]; 48 | 49 | const quoteBtn = document.getElementById("quoteBtn"); 50 | const quoteText = document.querySelector(".quote .text"); 51 | const quoteAuthor = document.querySelector(".quote .author"); 52 | 53 | function getQuote() { 54 | const index = Math.floor(Math.random() * quotes.length); 55 | quoteText.textContent = quotes[index].quote; 56 | quoteAuthor.textContent = "- " + quotes[index].author; 57 | } 58 | 59 | quoteBtn.addEventListener("click", getQuote); 60 | -------------------------------------------------------------------------------- /3_citacoes/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #178d3e; 3 | padding-top: 2rem; 4 | } 5 | 6 | .quote { 7 | font-family: sans-serif; 8 | font-size: 1.6rem; 9 | text-align: center; 10 | max-width: 600px; 11 | margin: 0 auto; 12 | padding: 20px; 13 | border: 2px solid #fff; 14 | color: #fff; 15 | border-radius: 0.4rem; 16 | } 17 | 18 | .quote .text { 19 | font-style: italic; 20 | margin-bottom: 10px; 21 | } 22 | 23 | .quote .author { 24 | font-weight: bold; 25 | color: #133110; 26 | } 27 | 28 | .quote .button { 29 | font-size: 20px; 30 | background-color: #0a2c07; 31 | color: #fff; 32 | border: none; 33 | border-radius: 5px; 34 | padding: 10px 20px; 35 | cursor: pointer; 36 | transition: background-color 0.3s ease; 37 | } 38 | 39 | .quote .button:hover { 40 | background-color: #12520c; 41 | } 42 | -------------------------------------------------------------------------------- /4_galera_com_lightbox/img/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_javascript/9af0e18d4ebb02c1a6dc226b1a0ddacd65043e40/4_galera_com_lightbox/img/image-1.jpg -------------------------------------------------------------------------------- /4_galera_com_lightbox/img/image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_javascript/9af0e18d4ebb02c1a6dc226b1a0ddacd65043e40/4_galera_com_lightbox/img/image-2.jpg -------------------------------------------------------------------------------- /4_galera_com_lightbox/img/image-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_javascript/9af0e18d4ebb02c1a6dc226b1a0ddacd65043e40/4_galera_com_lightbox/img/image-3.jpg -------------------------------------------------------------------------------- /4_galera_com_lightbox/img/image-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_javascript/9af0e18d4ebb02c1a6dc226b1a0ddacd65043e40/4_galera_com_lightbox/img/image-4.jpg -------------------------------------------------------------------------------- /4_galera_com_lightbox/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Galera com Lightbox 8 | 12 | 13 | 14 | 15 | 16 |

Galeria

17 | 51 | 52 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /4_galera_com_lightbox/scripts.js: -------------------------------------------------------------------------------- 1 | const galleryItems = document.querySelectorAll(".gallery-item"); 2 | const lightbox = document.querySelector(".lightbox"); 3 | const lightboxImage = document.querySelector(".lightbox-image"); 4 | const lightboxClose = document.querySelector(".lightbox-close"); 5 | 6 | galleryItems.forEach((item) => { 7 | item.addEventListener("click", () => { 8 | const imageUrl = item 9 | .querySelector(".gallery-image") 10 | .getAttribute("data-src"); 11 | lightboxImage.setAttribute("src", imageUrl); 12 | lightbox.style.display = "flex"; 13 | }); 14 | }); 15 | 16 | lightboxClose.addEventListener("click", () => { 17 | lightbox.style.display = "none"; 18 | }); 19 | -------------------------------------------------------------------------------- /4_galera_com_lightbox/styles.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-family: Helvetica; 3 | text-align: center; 4 | } 5 | 6 | .gallery { 7 | display: flex; 8 | flex-wrap: wrap; 9 | } 10 | 11 | .gallery-item { 12 | flex: 0 0 calc(33.333% - 10px); 13 | margin: 5px; 14 | } 15 | 16 | .gallery-link { 17 | display: block; 18 | } 19 | 20 | .gallery-image { 21 | width: 100%; 22 | height: auto; 23 | transition: transform 0.2s ease-in-out; 24 | height: 300px; 25 | object-fit: cover; 26 | cursor: pointer; 27 | } 28 | 29 | .gallery-image:hover { 30 | transform: scale(1.1); 31 | } 32 | 33 | .lightbox { 34 | display: none; 35 | position: fixed; 36 | top: 0; 37 | left: 0; 38 | width: 100%; 39 | height: 100%; 40 | background-color: rgba(0, 0, 0, 0.8); 41 | z-index: 100; 42 | } 43 | 44 | .lightbox-content { 45 | display: flex; 46 | justify-content: center; 47 | align-items: center; 48 | height: 100%; 49 | width: 100%; 50 | } 51 | 52 | .lightbox-image { 53 | max-width: 800px; 54 | object-fit: cover; 55 | } 56 | 57 | .lightbox-close { 58 | position: absolute; 59 | top: 1rem; 60 | right: 2rem; 61 | font-size: 2rem; 62 | color: #fff; 63 | border: 1px solid #fff; 64 | padding: 0.1rem 0.3rem; 65 | text-decoration: none; 66 | cursor: pointer; 67 | transition: 0.5s; 68 | } 69 | 70 | .lightbox-close:hover { 71 | background-color: #fff; 72 | color: #000; 73 | } 74 | -------------------------------------------------------------------------------- /5_barra_de_progresso/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Galera com Lightbox 8 | 12 | 13 | 14 | 15 | 16 |

Barra de Progresso

17 |
18 |
19 |
20 |
21 | 22 |
23 | 26 | 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /5_barra_de_progresso/scripts.js: -------------------------------------------------------------------------------- 1 | const progressBar = document.querySelector(".progress"); 2 | const previousBtn = document.querySelector("#previous-btn"); 3 | const nextBtn = document.querySelector("#next-btn"); 4 | 5 | let progress = 0; 6 | 7 | function updateProgressBar() { 8 | progressBar.style.width = progress + "%"; 9 | } 10 | 11 | function previousStep() { 12 | progress -= 10; 13 | if (progress < 0) progress = 0; 14 | updateProgressBar(); 15 | } 16 | 17 | function nextStep() { 18 | progress += 10; 19 | if (progress > 100) progress = 100; 20 | updateProgressBar(); 21 | } 22 | 23 | previousBtn.addEventListener("click", previousStep); 24 | nextBtn.addEventListener("click", nextStep); 25 | -------------------------------------------------------------------------------- /5_barra_de_progresso/styles.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-family: Helvetica; 3 | text-align: center; 4 | } 5 | 6 | .container { 7 | max-width: 50%; 8 | margin: 0 auto; 9 | } 10 | 11 | .progress-bar { 12 | width: 100%; 13 | height: 20px; 14 | background-color: #ddd; 15 | border-radius: 10px; 16 | } 17 | 18 | .progress { 19 | width: 0%; 20 | height: 100%; 21 | background-color: #4caf50; 22 | border-radius: 10px; 23 | transition: width 1s ease-in-out; 24 | } 25 | 26 | .controls { 27 | display: flex; 28 | justify-content: space-between; 29 | margin-top: 10px; 30 | } 31 | 32 | button { 33 | padding: 5px 10px; 34 | font-size: 16px; 35 | font-weight: bold; 36 | border-radius: 5px; 37 | background-color: #4caf50; 38 | border: 2px solid #4caf50; 39 | color: #fff; 40 | cursor: pointer; 41 | transition: 0.4s; 42 | } 43 | 44 | button i { 45 | margin-left: 0.2rem; 46 | margin-right: 0.2rem; 47 | } 48 | 49 | #previous-btn { 50 | background-color: transparent; 51 | color: #4caf50; 52 | } 53 | 54 | button:hover { 55 | background-color: #2e8b57; 56 | } 57 | -------------------------------------------------------------------------------- /6_formulario_com_validacao/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Formulário com Validação 8 | 9 | 10 | 11 | 12 |
13 |

Formulário de Contato

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 | -------------------------------------------------------------------------------- /6_formulario_com_validacao/scripts.js: -------------------------------------------------------------------------------- 1 | const form = document.querySelector("form"); 2 | const nome = document.querySelector("#nome"); 3 | const email = document.querySelector("#email"); 4 | const assunto = document.querySelector("#assunto"); 5 | const mensagem = document.querySelector("#mensagem"); 6 | const errorMessages = document.querySelectorAll(".error-message"); 7 | 8 | form.addEventListener("submit", function (event) { 9 | event.preventDefault(); 10 | resetErrors(); 11 | validateInputs(); 12 | }); 13 | 14 | function resetErrors() { 15 | errorMessages.forEach((errorMessage) => { 16 | errorMessage.innerText = ""; 17 | }); 18 | nome.parentElement.classList.remove("error"); 19 | email.parentElement.classList.remove("error"); 20 | assunto.parentElement.classList.remove("error"); 21 | mensagem.parentElement.classList.remove("error"); 22 | } 23 | 24 | function validateInputs() { 25 | const nomeValue = nome.value.trim(); 26 | const emailValue = email.value.trim(); 27 | const assuntoValue = assunto.value.trim(); 28 | const mensagemValue = mensagem.value.trim(); 29 | 30 | if (nomeValue === "") { 31 | setError(nome, "Nome não pode ficar em branco"); 32 | } 33 | 34 | if (emailValue === "") { 35 | setError(email, "E-mail não pode ficar em branco"); 36 | } else if (!isValidEmail(emailValue)) { 37 | setError(email, "E-mail inválido"); 38 | } 39 | 40 | if (assuntoValue === "") { 41 | setError(assunto, "Assunto não pode ficar em branco"); 42 | } 43 | 44 | if (mensagemValue === "") { 45 | setError(mensagem, "Mensagem não pode ficar em branco"); 46 | } 47 | } 48 | 49 | function setError(input, errorMessage) { 50 | const errorMessageElement = input.nextElementSibling; 51 | errorMessageElement.innerText = errorMessage; 52 | input.parentElement.classList.add("error"); 53 | } 54 | 55 | function isValidEmail(email) { 56 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); 57 | } 58 | -------------------------------------------------------------------------------- /6_formulario_com_validacao/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | font-family: Arial, Helvetica, sans-serif; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | padding: 0; 9 | } 10 | 11 | .container { 12 | max-width: 500px; 13 | margin: 0 auto; 14 | padding: 1rem; 15 | } 16 | 17 | h1 { 18 | text-align: center; 19 | } 20 | 21 | form { 22 | display: flex; 23 | flex-direction: column; 24 | } 25 | 26 | .form-control { 27 | display: flex; 28 | flex-direction: column; 29 | margin-bottom: 1.5rem; 30 | } 31 | 32 | label { 33 | margin-bottom: 0.3rem; 34 | font-weight: bold; 35 | } 36 | 37 | input[type="text"], 38 | input[type="email"], 39 | textarea { 40 | padding: 10px; 41 | border: 1px solid #ccc; 42 | border-radius: 5px; 43 | font-size: 16px; 44 | } 45 | 46 | .error-message { 47 | color: #e21111; 48 | margin-top: 5px; 49 | } 50 | 51 | button[type="submit"] { 52 | background-color: #2756af; 53 | color: white; 54 | padding: 0.7rem; 55 | border: none; 56 | border-radius: 5px; 57 | font-size: 1.2rem; 58 | cursor: pointer; 59 | transition: 0.4s; 60 | } 61 | 62 | button[type="submit"]:hover { 63 | background-color: #132f63; 64 | } 65 | 66 | /* Estilo para inputs com erro */ 67 | .form-control.error input, 68 | .form-control.error textarea { 69 | border-color: #e21111; 70 | } 71 | -------------------------------------------------------------------------------- /7_paleta_de_cores/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } 4 | -------------------------------------------------------------------------------- /7_paleta_de_cores/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Paleta de Cores 8 | 9 | 10 | 11 | 12 |
13 |

Paleta de Cores

14 |
15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /7_paleta_de_cores/scripts.js: -------------------------------------------------------------------------------- 1 | const generateButton = document.querySelector("#generate-button"); 2 | const colorsDiv = document.querySelector(".colors"); 3 | 4 | generateButton.addEventListener("click", generateColors); 5 | 6 | function generateColors() { 7 | colorsDiv.innerHTML = ""; 8 | for (let i = 0; i < 5; i++) { 9 | const color = generateRandomColor(); 10 | const colorDiv = document.createElement("div"); 11 | colorDiv.style.backgroundColor = color; 12 | const colorName = document.createElement("p"); 13 | colorName.innerText = color; 14 | colorName.style.color = color; 15 | colorDiv.appendChild(colorName); 16 | colorsDiv.appendChild(colorDiv); 17 | } 18 | } 19 | 20 | function generateRandomColor() { 21 | const letters = "0123456789ABCDEF"; 22 | let color = "#"; 23 | for (let i = 0; i < 6; i++) { 24 | color += letters[Math.floor(Math.random() * 16)]; 25 | } 26 | return color; 27 | } 28 | -------------------------------------------------------------------------------- /7_paleta_de_cores/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | height: 100vh; 7 | } 8 | 9 | h1 { 10 | margin-bottom: 2rem; 11 | text-align: center; 12 | } 13 | 14 | .container { 15 | display: flex; 16 | flex-direction: column; 17 | justify-content: center; 18 | } 19 | 20 | .colors { 21 | display: flex; 22 | justify-content: center; 23 | align-items: center; 24 | } 25 | 26 | .colors div { 27 | width: 100px; 28 | height: 100px; 29 | margin: 0 10px; 30 | border-radius: 5px; 31 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2); 32 | transition: transform 0.2s ease-in-out; 33 | position: relative; 34 | } 35 | 36 | .colors div p { 37 | position: absolute; 38 | top: 100px; 39 | } 40 | 41 | .colors div:hover { 42 | transform: scale(1.1); 43 | } 44 | 45 | #generate-button { 46 | background-color: #774caf; 47 | border: none; 48 | color: #fff; 49 | text-align: center; 50 | font-size: 1.2rem; 51 | padding: 0.8rem 1.2rem; 52 | cursor: pointer; 53 | border-radius: 8px; 54 | transition: all 0.3s; 55 | margin: 3.5rem auto; 56 | } 57 | 58 | #generate-button:hover { 59 | background-color: #6b30b8; 60 | box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 61 | } 62 | -------------------------------------------------------------------------------- /8_gerador_de_cpf/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Gerador de CPF 8 | 9 | 10 | 11 | 12 |
13 |

Gerador de CPF

14 |
15 |

16 | 17 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /8_gerador_de_cpf/scripts.js: -------------------------------------------------------------------------------- 1 | const cpfEl = document.getElementById("cpf"); 2 | const gerarCpfBtn = document.getElementById("gerar-cpf"); 3 | const copiarCpfBtn = document.getElementById("copiar-cpf"); 4 | 5 | // Função para gerar CPF aleatório 6 | function gerarCPF() { 7 | let n = Math.floor(Math.random() * 999999999) + 1; 8 | let nStr = n.toString().padStart(9, "0"); 9 | let dv1 = calcularDV(nStr, 10); 10 | let dv2 = calcularDV(nStr + dv1, 11); 11 | 12 | cpfEl.innerText = formatarCPF(nStr + dv1 + dv2); 13 | } 14 | 15 | function calcularDV(numero, peso) { 16 | let total = 0; 17 | for (let i = 0; i < numero.length; i++) { 18 | total += parseInt(numero.charAt(i)) * peso--; 19 | } 20 | let resto = total % 11; 21 | return resto < 2 ? 0 : 11 - resto; 22 | } 23 | 24 | function formatarCPF(cpf) { 25 | const cpfRegex = /^(\d{3})(\d{3})(\d{3})(\d{2})$/; 26 | return cpf.replace(cpfRegex, "$1.$2.$3-$4"); 27 | } 28 | 29 | // Função para copiar CPF para área de transferência 30 | function copiarCPF() { 31 | const cpf = cpfEl.innerText; 32 | navigator.clipboard.writeText(cpf).then( 33 | () => { 34 | alert(`CPF ${cpf} copiado para a área de transferência!`); 35 | }, 36 | (err) => { 37 | console.error("Erro ao copiar CPF: ", err); 38 | } 39 | ); 40 | } 41 | 42 | gerarCpfBtn.addEventListener("click", gerarCPF); 43 | copiarCpfBtn.addEventListener("click", copiarCPF); 44 | -------------------------------------------------------------------------------- /8_gerador_de_cpf/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Helvetica; 3 | } 4 | 5 | .container { 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | text-align: center; 10 | } 11 | 12 | button { 13 | background-color: #008cba; 14 | color: #fff; 15 | border: none; 16 | border-radius: 5px; 17 | padding: 0.8rem 1rem; 18 | font-size: 1.2rem; 19 | cursor: pointer; 20 | margin: 0 0.5rem; 21 | } 22 | -------------------------------------------------------------------------------- /9_calculadora_de_gorjeta/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Calculadora de Gorjeta 8 | 9 | 10 | 11 | 12 |
13 |

Calculadora de Gorjeta

14 | 15 | 16 | 17 | 23 | 24 |
25 | 26 | 27 | 28 | 29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /9_calculadora_de_gorjeta/scripts.js: -------------------------------------------------------------------------------- 1 | function calculateTip() { 2 | // Obter valor da conta e qualidade do serviço 3 | const billAmount = document.getElementById("billAmount").value; 4 | const serviceQuality = document.getElementById("serviceQuality").value; 5 | 6 | // Validar entrada 7 | if (billAmount === "" || serviceQuality == 0) { 8 | alert("Por favor, preencha os campos!"); 9 | return; 10 | } 11 | 12 | // Calcular gorjeta e valor total 13 | const tipAmount = billAmount * serviceQuality; 14 | const totalAmount = parseFloat(billAmount) + parseFloat(tipAmount); 15 | 16 | // Exibir resultados 17 | document.getElementById("tipAmount").value = "R$" + tipAmount.toFixed(2); 18 | document.getElementById("totalAmount").value = "R$" + totalAmount.toFixed(2); 19 | } 20 | -------------------------------------------------------------------------------- /9_calculadora_de_gorjeta/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f2f2f2; 3 | font-family: Arial, sans-serif; 4 | } 5 | 6 | .calculator { 7 | background-color: #fff; 8 | border-radius: 10px; 9 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); 10 | margin: 3rem auto; 11 | max-width: 500px; 12 | padding: 1.6rem; 13 | } 14 | 15 | h2 { 16 | text-align: center; 17 | } 18 | 19 | label { 20 | display: block; 21 | font-weight: bold; 22 | margin: 0.5rem 0; 23 | } 24 | 25 | input[type="number"], 26 | select { 27 | border: 1px solid #ccc; 28 | border-radius: 4px; 29 | box-sizing: border-box; 30 | margin-bottom: 10px; 31 | padding: 10px; 32 | width: 100%; 33 | } 34 | 35 | button { 36 | background-color: #4caf50; 37 | border: none; 38 | border-radius: 4px; 39 | color: #fff; 40 | cursor: pointer; 41 | font-size: 16px; 42 | margin-bottom: 10px; 43 | padding: 10px; 44 | width: 100%; 45 | } 46 | 47 | button:hover { 48 | background-color: #3e8e41; 49 | } 50 | 51 | .result { 52 | display: flex; 53 | flex-direction: column; 54 | } 55 | 56 | input[type="text"] { 57 | border: none; 58 | border-bottom: 2px solid #ccc; 59 | font-size: 16px; 60 | margin-bottom: 10px; 61 | padding: 5px; 62 | text-align: right; 63 | } 64 | 65 | input[type="text"]:disabled { 66 | background-color: #f2f2f2; 67 | color: #777; 68 | } 69 | --------------------------------------------------------------------------------