├── .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 |
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 |
11 |
30 |
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 |
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 |
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 |
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 |
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 |
Gerar Números
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 | Escolha a dificuldade:
15 |
16 | Selecione
17 | Fácil
18 | Médio
19 | Difícil
20 |
21 |
22 |
23 |
24 |
25 | Eu estou pensando em um número entre 1 e 100. Tente adivinhar qual é!
26 |
27 |
28 |
29 |
34 | Enviar
35 |
36 |
37 |
38 |
39 |
Jogar novamente
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 | Criar
15 |
16 |
17 |
18 |
Para fazer
19 |
20 | Tarefa 1
21 | Tarefa 2
22 |
23 |
24 |
25 |
Fazendo
26 |
27 | Tarefa 3
28 | Tarefa 4
29 |
30 |
31 |
32 |
Prontas
33 |
34 | Tarefa 5
35 | Tarefa 6
36 |
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 |
Digite uma palavra:
13 |
14 |
Verificar
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 |
23 |
24 | Adicione sua tarefa
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Edite sua tarefa
38 |
39 |
40 |
41 |
42 |
43 |
44 | Cancelar
45 |
46 |
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 | Digite a quantidade:
16 |
17 |
18 |
19 | De:
20 |
21 | Metro(s)
22 | Quilômetro(s)
23 | Centímetro(s)
24 | Milímetro(s)
25 |
26 |
27 |
28 | Para:
29 |
30 | Metro(s)
31 | Quilômetro(s)
32 | Centímetro(s)
33 | Milímetro(s)
34 |
35 |
36 |
Converter
37 |
38 | Resultado:
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 |
21 |
Seja bem-vindo
22 |
23 |
24 |
25 | Crie sua conta:
26 | Registre-se para utilizar todas as funcionalidades do sistema
27 |
28 | Nome
29 |
35 |
36 |
37 | E-mail
38 |
44 |
45 |
46 | Senha
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 | Quantidade de caracteres:
62 |
63 |
64 |
65 | Letras:
66 |
67 |
68 |
69 | Números:
70 |
71 |
72 |
73 | Símbolos:
74 |
75 |
76 |
Criar senha
77 |
78 |
79 |
Aqui está a sua senha:
80 |
81 |
Copiar
82 |
83 |
84 | Confirmação de Senha
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 | Digite seu texto:
13 |
14 |
15 |
0 caractere(s)
16 |
Contar palavras
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 | Reiniciar
25 |
26 | Alternar tema
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 |
setFilter(e.target.value)}>
11 | Todas
12 | Completas
13 | Incompletas
14 |
15 |
16 |
17 |
18 |
Ordem alfabética:
19 |
setSort("Asc")}>Asc
20 |
setSort("Desc")}>Desc
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 | completeTodo(todo.id)}>
13 | Completar
14 |
15 | removeTodo(todo.id)}>
16 | x
17 |
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 | setCategory(e.target.value)}>
27 | Seleciona uma categoria
28 | Trabalho
29 | Pessoal
30 | Estudos
31 |
32 | Criar tarefa
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 |
Gerar citação
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 |
18 |
19 |
25 |
26 |
27 |
33 |
34 |
35 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
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 |
21 |
22 |
23 |
24 | Voltar
25 |
26 | Próximo
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 |
20 |
21 |
26 |
27 |
32 |
33 |
38 |
39 | Enviar
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 |
Gerar nova paleta
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 |
Gerar CPF
17 |
Copiar CPF
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 |
Valor da conta:
15 |
16 |
Qualidade do serviço:
17 |
18 | Excelente - 10%
19 | Ótimo - 8%
20 | Bom - 5%
21 | Ruim - 2%
22 |
23 |
Calcular Gorjeta
24 |
25 | Valor da gorjeta:
26 |
27 | Valor total:
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 |
--------------------------------------------------------------------------------