├── 01 - BackgroundChanger
├── css
│ └── style.css
├── index.html
└── js
│ └── app.js
├── 02 - Countdown
├── css
│ └── style.css
├── img
│ └── macbook.jpg
├── index.html
└── js
│ └── app.js
├── 03 - RandomCat
├── css
│ └── style.css
├── img
│ └── cat-default.jpg
├── index.html
└── js
│ └── app.js
├── 04 - MovieApp
├── css
│ └── style.css
├── index.html
└── js
│ └── app.js
├── 05 - MovieApp + Modal
├── css
│ └── style.css
├── index.html
└── js
│ └── app.js
├── 06 - Pagination
├── css
│ └── style.css
├── index.html
└── js
│ └── app.js
├── 07 - PasswordGenerator
├── css
│ └── style.css
├── index.html
└── js
│ └── app.js
├── 08 - GithubProfiles
├── css
│ └── style.css
├── index.html
└── js
│ └── app.js
└── 09 - NotesApp
├── css
└── style.css
├── index.html
└── js
└── app.js
/01 - BackgroundChanger/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | height: 100vh;
9 | display: flex;
10 | justify-content: center;
11 | align-items: center;
12 | background-color: #f1f5f8;
13 | font-family: 'Roboto', sans-serif;
14 | }
15 |
16 | .container {
17 | text-align: center;
18 | }
19 |
20 | .container h2 {
21 | background-color: #222;
22 | padding: 20px;
23 | border-radius: 10px;
24 | color: #ffffff;
25 | font-size: 32px;
26 | margin-bottom: 24px;
27 | }
28 |
29 | .color {
30 | color: #49a6e9;
31 | }
32 |
33 | .btn {
34 | background-color: transparent;
35 | padding: 8px;
36 | border: 2px solid #222;
37 | border-radius: 5px;
38 | font-size: 16px;
39 | letter-spacing: 1.5px;
40 | color: #222;
41 | outline: none;
42 | cursor: pointer;
43 | }
44 |
45 | .btn:hover {
46 | background-color: #222;
47 | color: #ffffff;
48 | transition: all 0.275s ease-in;
49 | }
--------------------------------------------------------------------------------
/01 - BackgroundChanger/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
Macbook Pro 16 M1
14 |
Старт продаж
15 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
16 |
17 |
18 |
10
19 | дни
20 |
21 |
22 |
06
23 | часы
24 |
25 |
26 |
11
27 | минуты
28 |
29 |
30 |
49
31 | секунды
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/02 - Countdown/js/app.js:
--------------------------------------------------------------------------------
1 | const countdownElement = document.querySelector(".countdown");
2 | const items = document.querySelectorAll(".countdown-item > h4");
3 |
4 | // Назначаем дату отсчета
5 | let countdownDate = new Date(2022, 11, 18, 10, 0, 0).getTime();
6 |
7 | function getCountTime() {
8 | // Получаем текущее время
9 | const now = new Date().getTime();
10 |
11 | // Находим разницу времени
12 | const distance = countdownDate - now;
13 |
14 | // 1c = 1000мс
15 | // 1м = 60с
16 | // 1ч = 60м
17 | // 1д = 24ч
18 |
19 | // Создаем переменные в милисекундах
20 | const oneDay = 24 * 60 * 60 * 1000;
21 | const oneHour = 60 * 60 * 1000;
22 | const oneMinute = 60 * 1000;
23 |
24 | // Подсчет для дней, часов, минут и секунд
25 | let days = Math.floor(distance / oneDay);
26 | let hours = Math.floor((distance % oneDay) / oneHour);
27 | let minutes = Math.floor((distance % oneHour) / oneMinute);
28 | let seconds = Math.floor((distance % oneMinute) / 1000);
29 |
30 | // Создаем массив для переменных
31 | const values = [days, hours, minutes, seconds];
32 |
33 | // Добавляем значения переменных на страницу
34 | items.forEach(function (item, index) {
35 | item.textContent = values[index];
36 | });
37 |
38 | // Если время истекло
39 | if (distance < 0) {
40 | clearInterval(countdown);
41 | countdownElement.innerHTML = "
Время вышло! ";
42 | }
43 | }
44 |
45 | // Обновление счетчика каждую секунду
46 | let countdown = setInterval(getCountTime, 1000);
47 |
48 | // Инициализация текущего времени
49 | getCountTime();
50 |
--------------------------------------------------------------------------------
/03 - RandomCat/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #EDEEF0;
9 | }
10 |
11 | .container {
12 | display: flex;
13 | justify-content: center;
14 | flex-direction: column;
15 | align-items: center;
16 | }
17 |
18 | .img {
19 | height: 100%;
20 | width: 100%;
21 | }
22 |
23 | .card {
24 | max-width: 600px;
25 | padding: 16px;
26 | background-color: #ffffff;
27 | margin: 16px;
28 | border-radius: 10px;
29 | box-shadow: 5px 5px 15px grey;
30 | }
31 |
32 | .btn {
33 | padding: 8px;
34 | }
--------------------------------------------------------------------------------
/03 - RandomCat/img/cat-default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/balkoev/10-javascript-vanilla-projects/d842413b0ce29cbe55b9c04a43c944c0ced10dc5/03 - RandomCat/img/cat-default.jpg
--------------------------------------------------------------------------------
/03 - RandomCat/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
RandomCat
8 |
9 |
10 |
11 |
Cats API
12 |
13 |
14 |
15 |
Random Cat
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/03 - RandomCat/js/app.js:
--------------------------------------------------------------------------------
1 | const button = document.querySelector(".btn");
2 | const image = document.querySelector(".img");
3 | const url = "http://aws.random.cat/meow";
4 |
5 | async function fetchHandler() {
6 | try {
7 | const response = await fetch(url);
8 | const data = await response.json();
9 | image.src = data.file;
10 | } catch (error) {
11 | console.log(error);
12 | }
13 | }
14 |
15 | button.addEventListener("click", () => {
16 | let isLoaded = image.complete;
17 |
18 | if (isLoaded) {
19 | fetchHandler();
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/04 - MovieApp/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0px;
3 | padding: 0px;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #1a191f;
9 | font-family: "Open Sans", sans-serif;
10 | }
11 |
12 | .container {
13 | max-width: 1200px;
14 | margin: 0 auto;
15 | }
16 |
17 | .header__content {
18 | display: flex;
19 | align-items: center;
20 | justify-content: space-between;
21 | padding: 16px;
22 | height: 60px;
23 | }
24 |
25 | .header__logo {
26 | font-size: 32px;
27 | text-decoration: none;
28 | color: #ffffff;
29 | }
30 |
31 | .header__search {
32 | padding: 8px;
33 | border: 2px solid #1a191f;
34 | border-radius: 5px;
35 | outline: none;
36 | }
37 |
38 | .movies {
39 | display: flex;
40 | flex-wrap: wrap;
41 | justify-content: space-around;
42 | }
43 |
44 | .movie {
45 | width: 240px;
46 | margin: 10px;
47 | position: relative;
48 | }
49 |
50 | .movie__cover-inner {
51 | position: relative;
52 | height: 360px;
53 | }
54 |
55 | .movie__cover {
56 | max-width: 100%;
57 | height: 100%;
58 | }
59 |
60 | .movie__cover--darkened {
61 | background-color: #000000;
62 | opacity: 0.1;
63 | position: absolute;
64 | top: 0;
65 | left: 0;
66 | right: 0;
67 | max-width: 100%;
68 | height: 100%;
69 | z-index: 1;
70 | }
71 |
72 | .movie__cover--darkened:hover {
73 | background-color: grey;
74 | cursor: pointer;
75 | }
76 |
77 | .movie__info {
78 | padding: 10px 0px;
79 | }
80 |
81 | .movie__title {
82 | font-size: 16px;
83 | color: #ffffff;
84 | }
85 |
86 | .movie__category {
87 | font-size: 14px;
88 | color: #ffd80e;
89 | }
90 |
91 | .movie__average {
92 | position: absolute;
93 | top: 10px;
94 | left: 10px;
95 | right: 0;
96 | border-radius: 50%;
97 | width: 36px;
98 | height: 36px;
99 | display: flex;
100 | justify-content: center;
101 | align-items: center;
102 | background-color: #1a191f;
103 | color: #ffffff;
104 | font-size: 14px;
105 | }
106 |
107 | .movie__average--green {
108 | border: 1px solid green;
109 | }
110 |
111 | .movie__average--orange {
112 | border: 1px solid orange;
113 | }
114 |
115 | .movie__average--red {
116 | border: 1px solid red;
117 | }
118 |
--------------------------------------------------------------------------------
/04 - MovieApp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
Movie App
13 |
14 |
15 |
23 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/04 - MovieApp/js/app.js:
--------------------------------------------------------------------------------
1 | const API_KEY = "8c8e1a50-6322-4135-8875-5d40a5420d86";
2 | const API_URL_POPULAR =
3 | "https://kinopoiskapiunofficial.tech/api/v2.2/films/top?type=TOP_100_POPULAR_FILMS&page=1";
4 | const API_URL_SEARCH =
5 | "https://kinopoiskapiunofficial.tech/api/v2.1/films/search-by-keyword?keyword=";
6 |
7 | getMovies(API_URL_POPULAR);
8 |
9 | async function getMovies(url) {
10 | const resp = await fetch(url, {
11 | headers: {
12 | "Content-Type": "application/json",
13 | "X-API-KEY": API_KEY,
14 | },
15 | });
16 | const respData = await resp.json();
17 | showMovies(respData);
18 | }
19 |
20 | function getClassByRate(vote) {
21 | if (vote >= 7) {
22 | return "green";
23 | } else if (vote > 5) {
24 | return "orange";
25 | } else {
26 | return "red";
27 | }
28 | }
29 |
30 | function showMovies(data) {
31 | const moviesEl = document.querySelector(".movies");
32 |
33 | // Очищаем предыдущие фильмы
34 | document.querySelector(".movies").innerHTML = "";
35 |
36 | data.films.forEach((movie) => {
37 | const movieEl = document.createElement("div");
38 | movieEl.classList.add("movie");
39 | movieEl.innerHTML = `
40 |
41 |
46 |
47 |
48 |
49 |
${movie.nameRu}
50 |
${movie.genres.map(
51 | (genre) => ` ${genre.genre}`
52 | )}
53 | ${
54 | movie.rating &&
55 | `
56 |
${movie.rating}
59 | `
60 | }
61 |
62 | `;
63 | moviesEl.appendChild(movieEl);
64 | });
65 | }
66 |
67 | const form = document.querySelector("form");
68 | const search = document.querySelector(".header__search");
69 |
70 | form.addEventListener("submit", (e) => {
71 | e.preventDefault();
72 |
73 | const apiSearchUrl = `${API_URL_SEARCH}${search.value}`;
74 | if (search.value) {
75 | getMovies(apiSearchUrl);
76 |
77 | search.value = "";
78 | }
79 | });
80 |
--------------------------------------------------------------------------------
/05 - MovieApp + Modal/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0px;
3 | padding: 0px;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #1a191f;
9 | font-family: "Open Sans", sans-serif;
10 | }
11 |
12 | .container {
13 | max-width: 1200px;
14 | margin: 0 auto;
15 | }
16 |
17 | .header__content {
18 | display: flex;
19 | align-items: center;
20 | justify-content: space-between;
21 | padding: 16px;
22 | height: 60px;
23 | }
24 |
25 | .header__logo {
26 | font-size: 32px;
27 | text-decoration: none;
28 | color: #ffffff;
29 | }
30 |
31 | .header__search {
32 | padding: 8px;
33 | border: 2px solid #1a191f;
34 | border-radius: 5px;
35 | outline: none;
36 | }
37 |
38 | .movies {
39 | display: flex;
40 | flex-wrap: wrap;
41 | justify-content: space-around;
42 | }
43 |
44 | .movie {
45 | width: 240px;
46 | margin: 10px;
47 | position: relative;
48 | }
49 |
50 | .movie__cover-inner {
51 | position: relative;
52 | height: 360px;
53 | }
54 |
55 | .movie__cover {
56 | max-width: 100%;
57 | height: 100%;
58 | }
59 |
60 | .movie__cover--darkened {
61 | background-color: #000000;
62 | opacity: 0.1;
63 | position: absolute;
64 | top: 0;
65 | left: 0;
66 | right: 0;
67 | max-width: 100%;
68 | height: 100%;
69 | z-index: 1;
70 | }
71 |
72 | .movie__cover--darkened:hover {
73 | background-color: grey;
74 | cursor: pointer;
75 | }
76 |
77 | .movie__info {
78 | padding: 10px 0px;
79 | }
80 |
81 | .movie__title {
82 | font-size: 16px;
83 | color: #ffffff;
84 | }
85 |
86 | .movie__category {
87 | font-size: 14px;
88 | color: #ffd80e;
89 | }
90 |
91 | .movie__average {
92 | position: absolute;
93 | top: 10px;
94 | left: 10px;
95 | right: 0;
96 | border-radius: 50%;
97 | width: 36px;
98 | height: 36px;
99 | display: flex;
100 | justify-content: center;
101 | align-items: center;
102 | background-color: #1a191f;
103 | color: #ffffff;
104 | font-size: 14px;
105 | }
106 |
107 | .movie__average--green {
108 | border: 1px solid green;
109 | }
110 |
111 | .movie__average--orange {
112 | border: 1px solid orange;
113 | }
114 |
115 | .movie__average--red {
116 | border: 1px solid red;
117 | }
118 |
119 | .modal {
120 | display: none;
121 | position: fixed;
122 | z-index: 100;
123 | left: 0;
124 | top: 0;
125 | width: 100%;
126 | height: 100%;
127 | overflow: auto;
128 | background-color: rgba(0, 0, 0, 0.5);
129 | transition: width 2s;
130 | }
131 |
132 | .modal--show {
133 | display: flex;
134 | justify-content: center;
135 | align-items: center;
136 | }
137 |
138 | .modal__card {
139 | background-color: #ffffff;
140 | max-width: 600px;
141 | padding: 16px;
142 | box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.5);
143 | background-color: #1a191f;
144 | color: #ffffff;
145 | display: flex;
146 | flex-direction: column;
147 | }
148 |
149 | .modal__movie-backdrop {
150 | width: 200px;
151 | margin: 0 auto
152 | }
153 |
154 | .modal__movie-info {
155 | margin-left: 20px;
156 | }
157 |
158 | .modal__button-close {
159 | padding: 5px;
160 | margin: 10px;
161 | align-self: center;
162 | }
163 |
164 | .modal__movie-site {
165 | color: #ffd80e
166 | }
167 |
168 | ul li {
169 | padding: 5px;
170 | }
171 |
172 | .stop-scrolling {
173 | height: 100%;
174 | overflow: hidden;
175 | }
176 |
177 |
--------------------------------------------------------------------------------
/05 - MovieApp + Modal/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
Movie App
13 |
14 |
15 |
23 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/05 - MovieApp + Modal/js/app.js:
--------------------------------------------------------------------------------
1 | const API_KEY = "8c8e1a50-6322-4135-8875-5d40a5420d86";
2 | const API_URL_POPULAR =
3 | "https://kinopoiskapiunofficial.tech/api/v2.2/films/top?type=TOP_100_POPULAR_FILMS&page=1";
4 | const API_URL_SEARCH =
5 | "https://kinopoiskapiunofficial.tech/api/v2.1/films/search-by-keyword?keyword=";
6 | const API_URL_MOVIE_DETAILS = "https://kinopoiskapiunofficial.tech/api/v2.2/films/"
7 |
8 | getMovies(API_URL_POPULAR);
9 |
10 | async function getMovies(url) {
11 | const resp = await fetch(url, {
12 | headers: {
13 | "Content-Type": "application/json",
14 | "X-API-KEY": API_KEY,
15 | },
16 | });
17 | const respData = await resp.json();
18 | showMovies(respData);
19 | }
20 |
21 | function getClassByRate(vote) {
22 | if (vote >= 7) {
23 | return "green";
24 | } else if (vote > 5) {
25 | return "orange";
26 | } else {
27 | return "red";
28 | }
29 | }
30 |
31 | function showMovies(data) {
32 | const moviesEl = document.querySelector(".movies");
33 |
34 | // Очищаем предыдущие фильмы
35 | document.querySelector(".movies").innerHTML = "";
36 |
37 | data.films.forEach((movie) => {
38 | const movieEl = document.createElement("div");
39 | movieEl.classList.add("movie");
40 | movieEl.innerHTML = `
41 |
42 |
47 |
48 |
49 |
50 |
${movie.nameRu}
51 |
${movie.genres.map(
52 | (genre) => ` ${genre.genre}`
53 | )}
54 | ${
55 | movie.rating &&
56 | `
57 |
${movie.rating}
60 | `
61 | }
62 |
63 | `;
64 | movieEl.addEventListener("click", () => openModal(movie.filmId))
65 | moviesEl.appendChild(movieEl);
66 | });
67 | }
68 |
69 | const form = document.querySelector("form");
70 | const search = document.querySelector(".header__search");
71 |
72 | form.addEventListener("submit", (e) => {
73 | e.preventDefault();
74 |
75 | const apiSearchUrl = `${API_URL_SEARCH}${search.value}`;
76 | if (search.value) {
77 | getMovies(apiSearchUrl);
78 |
79 | search.value = "";
80 | }
81 | });
82 |
83 | // Modal
84 | const modalEl = document.querySelector(".modal");
85 |
86 | async function openModal(id) {
87 | const resp = await fetch(API_URL_MOVIE_DETAILS + id, {
88 | headers: {
89 | "Content-Type": "application/json",
90 | "X-API-KEY": API_KEY,
91 | },
92 | });
93 | const respData = await resp.json();
94 |
95 | modalEl.classList.add("modal--show");
96 | document.body.classList.add("stop-scrolling");
97 |
98 | modalEl.innerHTML = `
99 |
100 |
101 |
102 | ${respData.nameRu}
103 | - ${respData.year}
104 |
105 |
106 |
107 | Жанр - ${respData.genres.map((el) => `${el.genre} `)}
108 | ${respData.filmLength ? `Время - ${respData.filmLength} минут ` : ''}
109 | Сайт: ${respData.webUrl}
110 | Описание - ${respData.description}
111 |
112 |
Закрыть
113 |
114 | `
115 | const btnClose = document.querySelector(".modal__button-close");
116 | btnClose.addEventListener("click", () => closeModal());
117 | }
118 |
119 | function closeModal() {
120 | modalEl.classList.remove("modal--show");
121 | document.body.classList.remove("stop-scrolling");
122 | }
123 |
124 | window.addEventListener("click", (e) => {
125 | if (e.target === modalEl) {
126 | closeModal();
127 | }
128 | })
129 |
130 | window.addEventListener("keydown", (e) => {
131 | if (e.keyCode === 27) {
132 | closeModal();
133 | }
134 | })
--------------------------------------------------------------------------------
/06 - Pagination/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0px;
3 | padding: 0px;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #f1f5f8;
9 | font-family: "Open Sans", sans-serif;
10 | }
11 |
12 | .container {
13 | max-width: 1200px;
14 | margin: 0 auto;
15 | }
16 |
17 | .title {
18 | display: flex;
19 | justify-content: center;
20 | }
21 |
22 | .posts {
23 | display: flex;
24 | flex-direction: column;
25 | align-items: center;
26 | }
27 |
28 | .post {
29 | padding: 8px;
30 | border-color: bisque;
31 | border: 1px;
32 | }
33 |
34 | .pagination {
35 | display: flex;
36 | justify-content: center;
37 | }
38 |
39 | .pagination__list {
40 | display: flex;
41 | list-style-type: none;
42 | }
43 |
44 | .pagination__item {
45 | padding: 8px;
46 | margin: 8px;
47 | cursor: pointer;
48 | }
49 |
50 | .pagination__item--active {
51 | font-weight: 800;
52 | color: gold;
53 | }
54 |
--------------------------------------------------------------------------------
/06 - Pagination/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
Pagination
13 |
14 |
15 |
16 |
Pagination
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/06 - Pagination/js/app.js:
--------------------------------------------------------------------------------
1 | async function getData() {
2 | const response = await fetch('https://jsonplaceholder.typicode.com/posts');
3 | const data = await response.json();
4 | return data;
5 | }
6 |
7 | async function main() {
8 | const postsData = await getData();
9 | let currentPage = 1;
10 | let rows = 10;
11 |
12 | function displayList(arrData, rowPerPage, page) {
13 | const postsEl = document.querySelector('.posts');
14 | postsEl.innerHTML = "";
15 | page--;
16 |
17 | const start = rowPerPage * page;
18 | const end = start + rowPerPage;
19 | const paginatedData = arrData.slice(start, end);
20 |
21 | paginatedData.forEach((el) => {
22 | const postEl = document.createElement("div");
23 | postEl.classList.add("post");
24 | postEl.innerText = `${el.title}`;
25 | postsEl.appendChild(postEl);
26 | })
27 | }
28 |
29 | function displayPagination(arrData, rowPerPage) {
30 | const paginationEl = document.querySelector('.pagination');
31 | const pagesCount = Math.ceil(arrData.length / rowPerPage);
32 | const ulEl = document.createElement("ul");
33 | ulEl.classList.add('pagination__list');
34 |
35 | for (let i = 0; i < pagesCount; i++) {
36 | const liEl = displayPaginationBtn(i + 1);
37 | ulEl.appendChild(liEl)
38 | }
39 | paginationEl.appendChild(ulEl)
40 | }
41 |
42 | function displayPaginationBtn(page) {
43 | const liEl = document.createElement("li");
44 | liEl.classList.add('pagination__item')
45 | liEl.innerText = page
46 |
47 | if (currentPage == page) liEl.classList.add('pagination__item--active');
48 |
49 | liEl.addEventListener('click', () => {
50 | currentPage = page
51 | displayList(postsData, rows, currentPage)
52 |
53 | let currentItemLi = document.querySelector('li.pagination__item--active');
54 | currentItemLi.classList.remove('pagination__item--active');
55 |
56 | liEl.classList.add('pagination__item--active');
57 | })
58 |
59 | return liEl;
60 | }
61 |
62 | displayList(postsData, rows, currentPage);
63 | displayPagination(postsData, rows);
64 | }
65 |
66 | main();
--------------------------------------------------------------------------------
/07 - PasswordGenerator/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0px;
3 | padding: 0px;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #f1f5f8;
9 | font-family: "Open Sans", sans-serif;
10 | }
11 |
12 | .container {
13 | max-width: 1200px;
14 | margin: 0 auto;
15 | }
16 |
17 | .main {
18 | display: flex;
19 | flex-direction: column;
20 | justify-content: center;
21 | align-items: center;
22 | height: 100vh;
23 | }
24 |
25 | .password {
26 | padding: 8px;
27 | margin: 8px;
28 | caret-color: transparent;
29 | cursor: pointer;
30 | }
31 |
32 | .password-button {
33 | padding: 8px;
34 | background-color: blueviolet;
35 | color: #f1f5f8;
36 | cursor: pointer;
37 | border: none;
38 | border-radius: 2px;
39 | margin: 2px;
40 | }
--------------------------------------------------------------------------------
/07 - PasswordGenerator/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
Password Generator
13 |
14 |
15 |
16 |
17 |
Password Generator
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/07 - PasswordGenerator/js/app.js:
--------------------------------------------------------------------------------
1 | const mainEl = document.querySelector('.main');
2 |
3 | const passwordEl = document.createElement('input');
4 | passwordEl.classList.add('password');
5 | passwordEl.setAttribute('placeholder', 'Сгенерировать пароль');
6 | passwordEl.addEventListener('keypress', (e) => {
7 | e.preventDefault();
8 | })
9 | passwordEl.addEventListener('focus', (e) => {
10 | navigator.clipboard.writeText(passwordEl.value);
11 | });
12 |
13 | const copyBtn = document.createElement('button');
14 | copyBtn.classList.add('password-button');
15 | copyBtn.innerText = 'Скопировать';
16 | copyBtn.addEventListener('click', (e) => {
17 | passwordEl.select();
18 | passwordEl.setSelectionRange(0, 99999);
19 | navigator.clipboard.writeText(passwordEl.value);
20 | })
21 |
22 | const generateBtn = document.createElement('button');
23 | generateBtn.classList.add('password-button');
24 | generateBtn.innerText = 'Сгенерировать';
25 | generateBtn.addEventListener('click', (e) => {
26 | let password = generatePassword(12);
27 | passwordEl.value = password;
28 | });
29 |
30 | function generatePassword(passwordLength) {
31 | const numberChars = "0123456789";
32 | const upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
33 | const lowerChars = "abcdefghijklmnopqrstuvwxyz";
34 | const symbolChars = "!@#$%^&*()_+";
35 | const allChars = numberChars + upperChars + lowerChars + symbolChars;
36 |
37 | let randomString = '';
38 |
39 | for (let i = 0; i < passwordLength; i++) {
40 | let randomNumber = Math.floor(Math.random() * allChars.length);
41 | randomString += allChars[randomNumber];
42 | }
43 |
44 | return randomString;
45 | }
46 |
47 | mainEl.appendChild(passwordEl);
48 | mainEl.appendChild(copyBtn);
49 | mainEl.appendChild(generateBtn);
--------------------------------------------------------------------------------
/08 - GithubProfiles/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0px;
3 | padding: 0px;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #f1f5f8;
9 | font-family: "Open Sans", sans-serif;
10 | }
11 |
12 | .container {
13 | max-width: 1200px;
14 | margin: 0 auto;
15 | }
16 |
17 | .main {
18 | display: flex;
19 | flex-direction: column;
20 | justify-content: center;
21 | align-items: center;
22 | height: 100vh;
23 | }
24 |
25 | .search-image {
26 | width: 200px;
27 | height: 200px;
28 | display: block;
29 | margin: 0 auto;
30 | }
31 |
32 | .search-text {
33 | padding: 4px;
34 | }
35 |
36 | .search-input {
37 | padding: 8px;
38 | margin: 8px;
39 | }
40 |
41 | .search-button {
42 | padding: 8px;
43 | background-color: blueviolet;
44 | color: #f1f5f8;
45 | cursor: pointer;
46 | border: none;
47 | }
48 |
49 | .profile {
50 | border: 1px solid white;
51 | border-radius: 20px;
52 | padding: 20px;
53 | background-color: white;
54 | width: 300px;
55 | box-shadow: 8px 8px 2px 1px rgb(0, 0, 255, .2);
56 | }
57 |
58 | .delete-button {
59 | padding: 8px;
60 | background-color: rgb(237, 68, 68);
61 | border: none;
62 | color: #f1f5f8;
63 | border-radius: 2px;
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/08 - GithubProfiles/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
Github search profiles
13 |
14 |
15 |
16 |
17 |
Github search profiles
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/08 - GithubProfiles/js/app.js:
--------------------------------------------------------------------------------
1 | const mainEl = document.querySelector('.main');
2 | const wrapper = document.createElement('div')
3 |
4 | const formEl = document.createElement('form');
5 | formEl.addEventListener('submit', async (e) => {
6 | e.preventDefault();
7 | const inputsValue = Object.fromEntries(new FormData(e.target));
8 | const response = await fetch(`
9 | https://api.github.com/users/${inputsValue.name}
10 | `);
11 |
12 | if (response.ok) {
13 | const data = await response.json();
14 | wrapper.appendChild(createProfileEl(data))
15 | mainEl.appendChild(wrapper);
16 | inputEl.value = '';
17 | } else {
18 | alert("Пользователь не найден")
19 | }
20 | })
21 |
22 | const inputEl = document.createElement('input');
23 | inputEl.classList.add('search-input');
24 | inputEl.setAttribute('name', 'name')
25 |
26 | const searchButtonEl = document.createElement('button')
27 | searchButtonEl.classList.add('search-button');
28 | searchButtonEl.setAttribute('type', 'submit');
29 | searchButtonEl.innerHTML = "Поиск";
30 |
31 | formEl.appendChild(inputEl);
32 | formEl.appendChild(searchButtonEl);
33 | mainEl.appendChild(formEl);
34 |
35 | function createProfileEl(profileData) {
36 | const element = document.createElement('div');
37 | element.classList.add('profile');
38 | element.innerHTML = `
39 |
40 |
Имя: ${profileData.name}
41 |
Город: ${profileData.location}
42 |
О себе: ${profileData.bio}
43 | `
44 | element.appendChild(createDeleteBtnEl())
45 | return element;
46 | }
47 |
48 | function createDeleteBtnEl() {
49 | const element = document.createElement('button');
50 | element.classList.add('delete-button');
51 | element.innerText = "Удалить";
52 | element.addEventListener('click', (e) => {
53 | wrapper.innerHTML = ''
54 | })
55 |
56 | return element
57 | }
--------------------------------------------------------------------------------
/09 - NotesApp/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0px;
3 | padding: 0px;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #f1f5f8;
9 | font-family: "Open Sans", sans-serif;
10 | }
11 |
12 | .container {
13 | max-width: 1200px;
14 | margin: 0 auto;
15 | }
16 |
17 | .header {
18 | display: flex;
19 | align-items: center;
20 | }
21 |
22 | .note-header {
23 | display: flex;
24 | justify-content: space-between;
25 | margin-bottom: 8px;
26 | }
27 |
28 | .notes {
29 | display: flex;
30 | flex-direction: row;
31 | flex-wrap: wrap;
32 | }
33 |
34 | .note {
35 | background-color: lightyellow;
36 | padding: 16px;
37 | margin: 8px;
38 | border-radius: 10px;
39 | height: 300px;
40 | width: 240px;
41 | box-shadow: 2px 2px 5px 2px #cacaca;
42 | }
43 |
44 | .note-add {
45 | padding: 8px;
46 | margin: 8px;
47 | cursor: pointer;
48 | }
49 |
50 | .note-edit {
51 | padding: 4px;
52 | cursor: pointer;
53 | }
54 |
55 | .note-delete {
56 | padding: 4px;
57 | cursor: pointer;
58 | }
59 |
60 | .hidden {
61 | display: none;
62 | }
--------------------------------------------------------------------------------
/09 - NotesApp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
Notes App
17 |
18 |
19 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/09 - NotesApp/js/app.js:
--------------------------------------------------------------------------------
1 | const notesEl = document.querySelector('.notes');
2 | const addBtn = document.querySelector('.note-add')
3 |
4 | function createNote(title, text) {
5 | const noteEl = document.createElement('div');
6 | noteEl.classList.add('note');
7 | noteEl.innerHTML = `
8 |
16 |
${text}
17 |
18 |
19 | `
20 |
21 | const editBtn = noteEl.querySelector('.note-edit');
22 | const deleteBtn = noteEl.querySelector('.note-delete');
23 | const titleEl = noteEl.querySelector('#note-title');
24 | const textEl = noteEl.querySelector('#note-text');
25 | const titleInputEl = noteEl.querySelector('#note-title-input');
26 | const textInputEl = noteEl.querySelector('#note-textarea');
27 |
28 | editBtn.addEventListener('click', (e) => {
29 | titleEl.classList.toggle('hidden');
30 | textEl.classList.toggle('hidden');
31 |
32 | titleInputEl.classList.toggle('hidden');
33 | textInputEl.classList.toggle('hidden');
34 | });
35 |
36 | deleteBtn.addEventListener('click', (e) => {
37 | noteEl.remove();
38 | });
39 |
40 | titleInputEl.addEventListener('input', (e) => {
41 | titleEl.innerText = e.target.value;
42 | });
43 |
44 | textInputEl.addEventListener('input', (e) => {
45 | textEl.innerText = e.target.value;
46 | });
47 |
48 | return noteEl;
49 | }
50 |
51 | addBtn.addEventListener('click', (e) => {
52 | const el = createNote("Заголовок", "Ваш текст");
53 | notesEl.appendChild(el);
54 | });
55 |
--------------------------------------------------------------------------------