├── .DS_Store ├── .gitignore ├── Counter Up ├── Counter Up.zip ├── app.js ├── index.html └── style.css ├── Create Modal ├── Create Modal.zip ├── app.js ├── index.html └── style.css ├── Create Range Slider ├── Create Range Slider.zip ├── app.js ├── index.html └── style.css ├── Custom Video Player ├── Custom Video Player.zip ├── app.js ├── index.html └── style.css ├── Dark And Light Mode ├── Dark And Light Mode.zip ├── app.js ├── index.html └── style.css ├── Detect Pressed Key ├── Detect Pressed Key.zip ├── app.js ├── index.html └── style.css ├── Drag And Drop ├── Drag And Drop.zip ├── app.js ├── bg.jpeg ├── index.html └── style.css ├── Drawing App ├── Drawing App.zip ├── app.js ├── index.html └── style.css ├── Filter Elements ├── Filter Elements.zip ├── app.js ├── dessert1.jpg ├── drinks1.jpg ├── drinks2.jpg ├── drinks3.jpg ├── index.html ├── meat1.jpg ├── meat2.jpg ├── salad1.jpg ├── salad2.jpg └── style.css ├── Hover Board ├── Hover Board.zip ├── app.js ├── index.html └── style.css ├── Image Gallery ├── Image Gallery.zip ├── app.js ├── img1.jpeg ├── img2.jpeg ├── img3.jpeg ├── img4.jpeg ├── img5.jpeg ├── img6.jpeg ├── img7.jpeg ├── img8.jpeg ├── index.html └── style.css ├── Live Product Filter ├── Live Product Filter.zip ├── app.js ├── index.html └── style.css ├── Play Piano ├── E.mp3 ├── I.mp3 ├── O.mp3 ├── P.mp3 ├── Play Piano.zip ├── Q.mp3 ├── R.mp3 ├── T.mp3 ├── U.mp3 ├── W.mp3 ├── Y.mp3 ├── app.js ├── index.html └── style.css ├── Preview Image ├── Preview Image.zip ├── app.js ├── index.html └── style.css ├── Product Card ├── Nike Zoom KD 12.png ├── Product Card.zip ├── index.html └── style.css ├── Profile Card ├── Cuong.jpg ├── Profile Card.zip ├── aNam.jpg ├── aThai.jpg ├── index.html └── style.css ├── README.md ├── Register Form Validator ├── Register Form Validator.zip ├── app.js ├── index.html └── style.css ├── Search Box ├── Search Box.zip ├── app.js ├── index.html └── style.css ├── Search Tags ├── Search Tags.zip ├── app.js ├── index.html └── style.css ├── Show On Scroll ├── Show On Scroll.zip ├── app.js ├── index.html └── style.css ├── Simple Carousel ├── Simple Carousel.zip ├── app.js ├── index.html └── style.css ├── Skeleton Loading Effect ├── Skeleton Loading Effect.zip ├── app.js ├── index.html └── style.css ├── Slideshow ├── Slideshow.zip ├── app.js ├── img1.jpeg ├── img2.jpeg ├── img3.jpeg ├── img4.jpeg ├── img5.jpeg ├── img6.jpeg ├── img7.jpeg ├── img8.jpeg ├── img9.jpeg ├── index.html └── style.css ├── Toast Notification ├── Toast Notification.zip ├── app.js ├── index.html └── style.css ├── Todo List ├── Todo List.zip ├── app.js ├── index.html └── style.css ├── Tooltip ├── Tooltip.zip ├── index.html └── style.css ├── Typing Animation Effect ├── Typing Animation Effect.zip ├── index.html └── style.css ├── Vaidator Password ├── Vaidator Password.zip ├── app.js ├── index.html └── style.css ├── Weather App ├── Weather App.zip ├── app.js ├── cold.png ├── cool.jpeg ├── hot.png ├── index.html ├── style.css └── warm.jpeg └── Zoom Image ├── Zoom Image.zip ├── app.js ├── bike1.jpg ├── bike2.jpg ├── index.html └── style.css /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 30.md -------------------------------------------------------------------------------- /Counter Up/Counter Up.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Counter Up/Counter Up.zip -------------------------------------------------------------------------------- /Counter Up/app.js: -------------------------------------------------------------------------------- 1 | const face = document.querySelector('.counter.face h2') 2 | const youtube = document.querySelector('.counter.youtube h2') 3 | const tiktok = document.querySelector('.counter.tiktok h2') 4 | 5 | function counterUp(el, to) { 6 | let speed = 200 7 | let from = 0 8 | let step = to / speed 9 | const counter = setInterval(function () { 10 | from += step 11 | if (from > to) { 12 | clearInterval(counter) 13 | el.innerText = to 14 | } else { 15 | el.innerText = Math.ceil(from) 16 | } 17 | }, 1) 18 | } 19 | 20 | counterUp(face, 3300) 21 | counterUp(youtube, 1000) 22 | counterUp(tiktok, 9900) 23 | -------------------------------------------------------------------------------- /Counter Up/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Counter Up 8 | 9 | 10 | 11 |
12 |

Facebook

13 |

0

14 |
15 |
16 |

Youtube

17 |

0

18 |
19 |
20 |

Tiktok

21 |

0

22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Counter Up/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | height: 100vh; 9 | overflow: hidden; 10 | background-color: #8a66b4; 11 | display: flex; 12 | font-family: sans-serif; 13 | align-items: center; 14 | justify-content: center; 15 | } 16 | 17 | .counter { 18 | background-color: #6b38b6; 19 | margin: 30px; 20 | color: white; 21 | padding: 20px 40px; 22 | text-align: center; 23 | border-radius: 10px; 24 | font-size: 40px; 25 | box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15); 26 | } 27 | 28 | .counter p { 29 | font-size: 18px; 30 | margin-bottom: 15px; 31 | } 32 | -------------------------------------------------------------------------------- /Create Modal/Create Modal.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Create Modal/Create Modal.zip -------------------------------------------------------------------------------- /Create Modal/app.js: -------------------------------------------------------------------------------- 1 | const modal = document.querySelector('.modal') 2 | const openModalBtn = document.querySelector('.open-modal-btn') 3 | const iconCloseModal = document.querySelector('.modal__header i') 4 | const buttonCloseModal = document.querySelector('.modal__footer button') 5 | 6 | function toggleModal() { 7 | modal.classList.toggle('hide') 8 | } 9 | 10 | openModalBtn.addEventListener('click', toggleModal) 11 | iconCloseModal.addEventListener('click', toggleModal) 12 | buttonCloseModal.addEventListener('click', toggleModal) 13 | 14 | modal.addEventListener('click', (e) => { 15 | if (e.target == e.currentTarget) toggleModal() 16 | }) 17 | -------------------------------------------------------------------------------- /Create Modal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Create Modal 8 | 15 | 16 | 17 | 18 | 19 | 20 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Create Modal/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --pink-color: #e26e70; 3 | } 4 | 5 | * { 6 | padding: 0; 7 | margin: 0; 8 | font-family: sans-serif; 9 | box-sizing: border-box; 10 | } 11 | 12 | body { 13 | background-image: linear-gradient( 14 | 45deg, 15 | rgb(65, 88, 208) 0%, 16 | rgb(200, 80, 192) 50%, 17 | rgb(255, 204, 112) 100% 18 | ); 19 | height: 100vh; 20 | overflow: hidden; 21 | } 22 | 23 | .open-modal-btn { 24 | position: fixed; 25 | top: 50%; 26 | left: 50%; 27 | transform: translate(-50%, -50%); 28 | outline: none; 29 | border: none; 30 | padding: 20px 40px; 31 | border-radius: 6px; 32 | font-size: 20px; 33 | cursor: pointer; 34 | box-shadow: 0 0 5px #aba4a4; 35 | background-color: #fff; 36 | transition: 0.25s; 37 | } 38 | 39 | .open-modal-btn:hover { 40 | background-color: rgba(255, 255, 255, 0.8); 41 | } 42 | 43 | .modal { 44 | position: absolute; 45 | top: 0; 46 | left: 0; 47 | width: 100vw; 48 | height: 100vh; 49 | background-color: rgba(0, 0, 0, 0.6); 50 | } 51 | 52 | .hide { 53 | display: none; 54 | } 55 | 56 | .modal__inner { 57 | width: 450px; 58 | background-color: #fff; 59 | border-radius: 5px; 60 | overflow: hidden; 61 | animation: showModal linear 0.2s; 62 | position: relative; 63 | margin: 200px auto; 64 | } 65 | 66 | @keyframes showModal { 67 | from { 68 | top: -200px; 69 | opacity: 0; 70 | } 71 | to { 72 | top: 0; 73 | opacity: 1; 74 | } 75 | } 76 | 77 | .modal__header { 78 | background-color: var(--pink-color); 79 | color: white; 80 | display: flex; 81 | align-items: center; 82 | justify-content: space-between; 83 | padding: 15px; 84 | font-size: 17px; 85 | } 86 | 87 | .modal__body { 88 | padding: 15px; 89 | } 90 | 91 | .modal__body h2 { 92 | color: var(--pink-color); 93 | margin-bottom: 10px; 94 | } 95 | 96 | .modal__footer { 97 | padding: 15px; 98 | text-align: right; 99 | } 100 | 101 | .modal__footer button { 102 | outline: none; 103 | border: none; 104 | padding: 10px 20px; 105 | background-color: var(--pink-color); 106 | color: white; 107 | border-radius: 6px; 108 | cursor: pointer; 109 | } 110 | 111 | i { 112 | cursor: pointer; 113 | font-size: 18px; 114 | } 115 | -------------------------------------------------------------------------------- /Create Range Slider/Create Range Slider.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Create Range Slider/Create Range Slider.zip -------------------------------------------------------------------------------- /Create Range Slider/app.js: -------------------------------------------------------------------------------- 1 | const body = document.querySelector('body') 2 | const range = document.querySelector('.range') 3 | const rangeBar = document.querySelector('.range-bar') 4 | 5 | function setRangeBar(percent) { 6 | rangeBar.style.width = `${percent}%` 7 | rangeBar.querySelector('span').innerText = `${percent}%` 8 | body.style.backgroundColor = `rgba(0, 0, 0, ${percent / 100})` 9 | } 10 | 11 | setRangeBar(40) 12 | 13 | range.addEventListener('mousemove', function (e) { 14 | const process = e.pageX - this.offsetLeft 15 | let percent = (process / this.offsetWidth) * 100 16 | 17 | percent = Math.ceil(percent) 18 | setRangeBar(percent) 19 | }) 20 | -------------------------------------------------------------------------------- /Create Range Slider/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Create Range Slider 8 | 9 | 10 | 11 |

Custom Range Slider

12 | 13 |
14 |
15 | 16 |
17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Create Range Slider/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 100%; 3 | height: 100vh; 4 | display: flex; 5 | align-items: center; 6 | flex-direction: column; 7 | overflow: hidden; 8 | font-family: sans-serif; 9 | padding-top: 150px; 10 | } 11 | 12 | .range { 13 | margin-top: 40px; 14 | width: 600px; 15 | height: 50px; 16 | background-color: white; 17 | border: 1px solid #aaa; 18 | border-radius: 100px; 19 | position: relative; 20 | overflow: hidden; 21 | cursor: e-resize; 22 | } 23 | 24 | .range-bar { 25 | background-image: linear-gradient(to right, #bb69cc, #246bac); 26 | position: absolute; 27 | top: 0; 28 | left: 0; 29 | height: 100%; 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | } 34 | 35 | .range-bar span { 36 | color: white; 37 | font-size: 26px; 38 | } 39 | -------------------------------------------------------------------------------- /Custom Video Player/Custom Video Player.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Custom Video Player/Custom Video Player.zip -------------------------------------------------------------------------------- /Custom Video Player/app.js: -------------------------------------------------------------------------------- 1 | const player = document.querySelector('.player') 2 | const video = player.querySelector('.player__video') 3 | const progress = player.querySelector('.player__progress') 4 | const progressBar = player.querySelector('.player__progress__filled') 5 | 6 | const toggle = player.querySelector('.toggle') 7 | const skipButtons = player.querySelectorAll('[data-skip]') 8 | const volume = player.querySelector('.player__volume input') 9 | const time = player.querySelector('.player__time') 10 | 11 | function togglePlay() { 12 | if (video.paused) { 13 | video.play() 14 | toggle.innerHTML = "" 15 | } else { 16 | video.pause() 17 | toggle.innerHTML = "" 18 | } 19 | } 20 | 21 | function handleProgress() { 22 | const percent = (video.currentTime / video.duration) * 100 23 | progressBar.style.width = `${percent}%` 24 | 25 | time.innerHTML = `${formatTime(video.currentTime)}/ ${formatTime( 26 | video.duration 27 | )}` 28 | } 29 | 30 | function scrub(e) { 31 | const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration 32 | video.currentTime = scrubTime 33 | } 34 | 35 | video.addEventListener('click', togglePlay) 36 | toggle.addEventListener('click', togglePlay) 37 | 38 | video.addEventListener('timeupdate', handleProgress) 39 | 40 | skipButtons.forEach((button) => 41 | button.addEventListener('click', function () { 42 | video.currentTime += +this.dataset.skip 43 | }) 44 | ) 45 | 46 | volume.addEventListener('change', function () { 47 | video.volume = this.value 48 | }) 49 | 50 | progress.addEventListener('click', scrub) 51 | 52 | function formatTime(time) { 53 | let minutes = Math.floor(time / 60) 54 | let seconds = Math.floor(time - minutes * 60) 55 | let minuteValue, secondValue 56 | 57 | minuteValue = minutes < 10 ? '0' + minutes : minutes 58 | secondValue = seconds < 10 ? '0' + seconds : seconds 59 | 60 | let mediaTime = minuteValue + ':' + secondValue 61 | return mediaTime 62 | } 63 | -------------------------------------------------------------------------------- /Custom Video Player/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Custom Video Player 8 | 9 | 13 | 14 | 15 | 16 |
17 | 21 |
22 | 25 |

26 |
27 |
28 |
29 | 30 | 31 | 32 |
33 | 34 | 35 |
36 |
37 |
38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Custom Video Player/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary-color: #0075ff; 3 | } 4 | 5 | * { 6 | padding: 0; 7 | margin: 0; 8 | box-sizing: border-box; 9 | font-family: sans-serif; 10 | } 11 | 12 | body { 13 | background: linear-gradient(145deg, #7c1599, #7e4ae8); 14 | height: 100vh; 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | } 19 | 20 | .player__video { 21 | width: 100%; 22 | } 23 | 24 | .player { 25 | width: 800px; 26 | position: relative; 27 | overflow: hidden; 28 | } 29 | 30 | .player__controls { 31 | width: 100%; 32 | position: absolute; 33 | display: flex; 34 | align-items: center; 35 | bottom: 0; 36 | left: 0; 37 | transform: translateY(100%); 38 | transition: 0.25s; 39 | background-image: linear-gradient(to bottom, transparent, rgba(0,0,0,0.9)); 40 | color: white; 41 | } 42 | 43 | .player:hover .player__controls { 44 | transform: unset; 45 | } 46 | 47 | .player__controls > * { 48 | margin: 5px; 49 | } 50 | 51 | .player__progress { 52 | flex: 1; 53 | height: 10px; 54 | background-color: #fff; 55 | position: relative; 56 | } 57 | 58 | .player__progress__filled { 59 | position: absolute; 60 | top: 0; 61 | left: 0; 62 | height: 100%; 63 | background-color: var(--primary-color); 64 | } 65 | 66 | .player__button { 67 | outline: none; 68 | border: none; 69 | background-color: transparent; 70 | color: white; 71 | cursor: pointer; 72 | } 73 | 74 | .player__button:hover { 75 | color: var(--primary-color); 76 | } 77 | 78 | .toggle { 79 | font-size: 30px; 80 | } 81 | 82 | .player__volume input { 83 | position: relative; 84 | top: 1px; 85 | } -------------------------------------------------------------------------------- /Dark And Light Mode/Dark And Light Mode.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Dark And Light Mode/Dark And Light Mode.zip -------------------------------------------------------------------------------- /Dark And Light Mode/app.js: -------------------------------------------------------------------------------- 1 | const inputToggle = document.querySelector('#toggleMode') 2 | 3 | inputToggle.addEventListener('change', () => { 4 | document.body.classList.toggle('dark') 5 | }) -------------------------------------------------------------------------------- /Dark And Light Mode/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dark And Light Mode 8 | 9 | 10 | 11 |
12 |

Nodemy

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

Light/Dark Mode

20 |
21 |

22 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Ratione quis 23 | quos minima ullam aliquam veritatis ad animi quisquam ipsum voluptatem 24 | voluptatibus incidunt delectus fuga explicabo, soluta illum est ab. 25 | Perferendis? 26 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Illo obcaecati quae iusto optio nam eum error, voluptatibus molestiae nihil quo rem dolorem explicabo temporibus veritatis assumenda porro, atque suscipit. Amet! 27 |

28 |
29 | Nodemy 30 | Youtube 31 | Github 32 |
33 |
34 |
35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Dark And Light Mode/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | font-family: sans-serif; 6 | } 7 | 8 | body { 9 | --primary-color: #0a6cf1; 10 | --background-color: #eee; 11 | --box-color: #dadada; 12 | --text-color: #333; 13 | 14 | background-color: var(--background-color); 15 | } 16 | 17 | body.dark { 18 | --primary-color: #4daf54; 19 | --background-color: #2d2c2d; 20 | --box-color: #3b3a3b; 21 | --text-color: #fff; 22 | 23 | background-color: var(--background-color); 24 | } 25 | 26 | header { 27 | display: flex; 28 | justify-content: space-between; 29 | align-items: center; 30 | padding: 20px 50px; 31 | } 32 | 33 | header h1 { 34 | color: var(--primary-color); 35 | } 36 | 37 | .toggle label { 38 | width: 80px; 39 | height: 30px; 40 | background-color: #aaa; 41 | border-radius: 20px; 42 | cursor: pointer; 43 | position: relative; 44 | display: flex; 45 | align-items: center; 46 | } 47 | 48 | .toggle label::after { 49 | content: ''; 50 | position: absolute; 51 | width: 25px; 52 | height: 25px; 53 | border-radius: 50%; 54 | background-color: #fff; 55 | transition: 0.25s; 56 | } 57 | 58 | input:checked + label { 59 | background-color: var(--primary-color); 60 | } 61 | 62 | input:checked + label::after { 63 | margin-left: 80px; 64 | transform: translateX(-100%); 65 | } 66 | 67 | main { 68 | width: 1200px; 69 | margin: 100px auto; 70 | } 71 | 72 | h2 { 73 | color: var(--primary-color); 74 | font-size: 50px; 75 | text-align: center; 76 | margin-bottom: 30px; 77 | } 78 | 79 | .box { 80 | background-color: var(--box-color); 81 | padding: 40px; 82 | border-radius: 10px; 83 | font-size: 20px; 84 | } 85 | 86 | .box p { 87 | color: var(--text-color); 88 | } 89 | 90 | .box-social { 91 | margin-top: 40px; 92 | display: flex; 93 | align-items: center; 94 | justify-content: space-evenly; 95 | } 96 | 97 | a { 98 | text-decoration: none; 99 | padding: 10px 20px; 100 | background-color: var(--primary-color); 101 | color: #dadada; 102 | border-radius: 10px; 103 | } 104 | -------------------------------------------------------------------------------- /Detect Pressed Key/Detect Pressed Key.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Detect Pressed Key/Detect Pressed Key.zip -------------------------------------------------------------------------------- /Detect Pressed Key/app.js: -------------------------------------------------------------------------------- 1 | const box = document.querySelector('.box') 2 | 3 | const eKey = document.querySelector('.card.key p:last-child') 4 | const eLocation = document.querySelector('.card.location p:last-child') 5 | const eWhich = document.querySelector('.card.which p:last-child') 6 | const eCode = document.querySelector('.card.code p:last-child') 7 | 8 | document.addEventListener('keydown', (e) => { 9 | let keyName = e.keyCode === 32 ? 'Space' : e.key 10 | 11 | document.querySelector('.result').innerText = e.which 12 | 13 | 14 | eKey.innerText = keyName 15 | eLocation.innerText = e.location 16 | eWhich.innerText = e.which 17 | eCode.innerText = e.code 18 | 19 | 20 | document.querySelector('.alert').classList.add('hide') 21 | box.classList.remove('hide') 22 | }) 23 | -------------------------------------------------------------------------------- /Detect Pressed Key/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Detect Pressed Key 8 | 9 | 10 | 11 |

Press any key

12 |
13 |

14 |
15 |
16 |

Key

17 |

18 |
19 |
20 |

Location

21 |

22 |
23 |
24 |

Which

25 |

26 |
27 |
28 |

Code

29 |

30 |
31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Detect Pressed Key/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | 10 | body { 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | background: #17a2b8; 15 | width: 100%; 16 | height: 100vh; 17 | } 18 | 19 | .alert { 20 | font-size: 40px; 21 | color: #17a2b8; 22 | font-weight: 500; 23 | background-color: #fff; 24 | padding: 10px 40px; 25 | border-radius: 12px; 26 | } 27 | 28 | .result { 29 | color: white; 30 | font-size: 70px; 31 | width: 250px; 32 | height: 250px; 33 | border: 7px solid white; 34 | border-radius: 50%; 35 | font-weight: 600; 36 | margin: auto; 37 | display: flex; 38 | align-items: center; 39 | justify-content: center; 40 | margin-bottom: 60px; 41 | } 42 | 43 | .detail { 44 | display: flex; 45 | } 46 | 47 | .detail .card { 48 | background-color: #fff; 49 | margin: 20px; 50 | width: 300px; 51 | text-align: center; 52 | border-radius: 10px; 53 | overflow: hidden; 54 | } 55 | 56 | .card p { 57 | padding: 10px; 58 | font-size: 20px; 59 | } 60 | 61 | .card p:first-child { 62 | background-color: #78c0c9; 63 | color: white; 64 | } 65 | 66 | .card p:last-child { 67 | text-transform: capitalize; 68 | } 69 | 70 | .hide { 71 | display: none; 72 | } -------------------------------------------------------------------------------- /Drag And Drop/Drag And Drop.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Drag And Drop/Drag And Drop.zip -------------------------------------------------------------------------------- /Drag And Drop/app.js: -------------------------------------------------------------------------------- 1 | const draggable = document.querySelector('.draggable') 2 | const boxes = document.querySelectorAll('.box') 3 | 4 | draggable.addEventListener('dragstart', dragStart) 5 | draggable.addEventListener('dragend', dragEnd) 6 | 7 | boxes.forEach((box) => { 8 | box.addEventListener('dragover', dragOver) 9 | box.addEventListener('drop', dragDrop) 10 | }) 11 | 12 | function dragStart() { 13 | this.classList.add('dragging') 14 | } 15 | 16 | function dragEnd() { 17 | this.classList.remove('dragging') 18 | } 19 | 20 | function dragOver(e) { 21 | // thêm preventDefault vì theo mặc định thả vào bên trong 1 phần tử sẽ bị vô hiệu hóa => xem con trỏ chuột khi over để rõ hơn 22 | e.preventDefault() 23 | this.appendChild(draggable) 24 | } 25 | 26 | function dragDrop() { 27 | this.appendChild(draggable) 28 | } 29 | -------------------------------------------------------------------------------- /Drag And Drop/bg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Drag And Drop/bg.jpeg -------------------------------------------------------------------------------- /Drag And Drop/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Drag And Drop/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | background-image: linear-gradient(145deg, #a4508b, #5f0a87); 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | height: 100vh; 13 | overflow: hidden; 14 | } 15 | 16 | .box { 17 | height: 180px; 18 | width: 180px; 19 | margin: 10px; 20 | border: solid 3px black; 21 | background: white; 22 | overflow: hidden; 23 | } 24 | 25 | .draggable { 26 | background-image: url('bg.jpeg'); 27 | object-fit: cover; 28 | object-position: center; 29 | width: 100%; 30 | height: 100%; 31 | } 32 | 33 | .dragging { 34 | border: solid 5px #ccc; 35 | } 36 | -------------------------------------------------------------------------------- /Drawing App/Drawing App.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Drawing App/Drawing App.zip -------------------------------------------------------------------------------- /Drawing App/app.js: -------------------------------------------------------------------------------- 1 | const canvas = document.getElementById('canvas') 2 | const colorEl = document.getElementById('color') 3 | const eraserEl = document.getElementById('eraser') 4 | const decreaseBtn = document.getElementById('decrease') 5 | const increaseBtn = document.getElementById('increase') 6 | const sizeEL = document.getElementById('size') 7 | const saveEl = document.getElementById('save') 8 | const clearEl = document.getElementById('clear') 9 | 10 | const context = canvas.getContext('2d') 11 | 12 | let size = 10 13 | let isPressed = false 14 | colorEl.value = 'black' 15 | let color = colorEl.value 16 | let x, y 17 | 18 | canvas.addEventListener('mousedown', (e) => { 19 | isPressed = true 20 | 21 | x = e.offsetX 22 | y = e.offsetY 23 | }) 24 | 25 | document.addEventListener('mouseup', (e) => { 26 | isPressed = false 27 | 28 | x = undefined 29 | y = undefined 30 | }) 31 | 32 | canvas.addEventListener('mousemove', (e) => { 33 | if (isPressed) { 34 | const x2 = e.offsetX 35 | const y2 = e.offsetY 36 | 37 | drawCircle(x2, y2) 38 | drawLine(x, y, x2, y2) 39 | 40 | x = x2 41 | y = y2 42 | } 43 | }) 44 | 45 | function drawCircle(x, y) { 46 | context.beginPath() 47 | context.arc(x, y, size, 0, Math.PI * 2) 48 | context.fillStyle = color 49 | context.fill() 50 | } 51 | 52 | function drawLine(x1, y1, x2, y2) { 53 | context.beginPath() 54 | context.moveTo(x1, y1) 55 | context.lineTo(x2, y2) 56 | context.strokeStyle = color 57 | context.lineWidth = size * 2 58 | context.stroke() 59 | } 60 | 61 | function updateSizeOnScreen() { 62 | sizeEL.innerText = size 63 | } 64 | 65 | increaseBtn.addEventListener('click', () => { 66 | size += 5 67 | 68 | if (size > 50) { 69 | size = 50 70 | } 71 | 72 | updateSizeOnScreen() 73 | }) 74 | 75 | decreaseBtn.addEventListener('click', () => { 76 | size -= 5 77 | 78 | if (size < 5) { 79 | size = 5 80 | } 81 | 82 | updateSizeOnScreen() 83 | }) 84 | 85 | colorEl.addEventListener('change', (e) => (color = e.target.value)) 86 | 87 | clearEl.addEventListener('click', () => 88 | context.clearRect(0, 0, canvas.width, canvas.height) 89 | ) 90 | 91 | eraserEl.addEventListener('click', () => { 92 | color = '#fff' 93 | }) 94 | 95 | saveEl.addEventListener('click', (e) => { 96 | const imageURI = canvas.toDataURL('image/png') 97 | e.currentTarget.href = imageURI 98 | }) 99 | -------------------------------------------------------------------------------- /Drawing App/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Drawing App 8 | 12 | 13 | 14 | 15 |
16 |
17 | 18 | 21 | 22 | 10 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Drawing App/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | background-color: #fff; 9 | } 10 | 11 | canvas { 12 | display: block; 13 | margin: 0 auto; 14 | border: 1px solid #333; 15 | } 16 | 17 | .tools { 18 | background-color: steelblue; 19 | display: flex; 20 | align-items: center; 21 | } 22 | 23 | .tools > * { 24 | background-color: #fff; 25 | border: none; 26 | font-size: 25px; 27 | height: 40px; 28 | width: 40px; 29 | padding: 5px; 30 | margin: 10px; 31 | display: flex; 32 | align-items: center; 33 | justify-content: center; 34 | cursor: pointer; 35 | } 36 | 37 | #save { 38 | font-size: 16px; 39 | margin-left: auto; 40 | text-decoration: none; 41 | } 42 | -------------------------------------------------------------------------------- /Filter Elements/Filter Elements.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/Filter Elements.zip -------------------------------------------------------------------------------- /Filter Elements/app.js: -------------------------------------------------------------------------------- 1 | const foodBtns = document.querySelectorAll('.food-menu button') 2 | const foodList = document.querySelectorAll('.food-item') 3 | 4 | foodBtns.forEach((btn) => { 5 | btn.addEventListener('click', (e) => { 6 | const type = e.target.getAttribute('type-food') 7 | 8 | // remove and set active fpr button 9 | document 10 | .querySelector('.food-menu button.active') 11 | .classList.remove('active') 12 | e.target.classList.add('active') 13 | 14 | // filter elements 15 | foodList.forEach((item) => { 16 | if (type == 'all' || item.getAttribute('type-food') == type) 17 | item.classList.remove('hide') 18 | else item.classList.add('hide') 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /Filter Elements/dessert1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/dessert1.jpg -------------------------------------------------------------------------------- /Filter Elements/drinks1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/drinks1.jpg -------------------------------------------------------------------------------- /Filter Elements/drinks2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/drinks2.jpg -------------------------------------------------------------------------------- /Filter Elements/drinks3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/drinks3.jpg -------------------------------------------------------------------------------- /Filter Elements/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Filter Elements 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 |
46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Filter Elements/meat1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/meat1.jpg -------------------------------------------------------------------------------- /Filter Elements/meat2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/meat2.jpg -------------------------------------------------------------------------------- /Filter Elements/salad1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/salad1.jpg -------------------------------------------------------------------------------- /Filter Elements/salad2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Filter Elements/salad2.jpg -------------------------------------------------------------------------------- /Filter Elements/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary-color: #0f9d58; 3 | } 4 | 5 | * { 6 | padding: 0; 7 | margin: 0; 8 | box-sizing: border-box; 9 | font-family: sans-serif; 10 | } 11 | 12 | img { 13 | width: 100%; 14 | height: 100%; 15 | object-fit: cover; 16 | object-position: center; 17 | transition: 0.5s; 18 | } 19 | 20 | body { 21 | display: flex; 22 | } 23 | 24 | .container { 25 | border-radius: 20px; 26 | padding: 20px; 27 | background-color: #e2f3e2; 28 | margin: 100px auto; 29 | margin-bottom: auto; 30 | width: 1200px; 31 | text-align: center; 32 | } 33 | 34 | .food-menu { 35 | margin-bottom: 40px; 36 | } 37 | 38 | .food-menu button { 39 | color: var(--primary-color); 40 | border: 1px solid var(--primary-color); 41 | padding: 10px 40px; 42 | border-radius: 50px; 43 | font-weight: 600; 44 | transition: 0.25s; 45 | cursor: pointer; 46 | background-color: #fff; 47 | margin: 5px; 48 | } 49 | 50 | .food-menu button:hover, 51 | .food-menu button.active { 52 | color: #fff; 53 | background-color: var(--primary-color); 54 | } 55 | 56 | .food-list { 57 | display: flex; 58 | align-items: center; 59 | justify-content: center; 60 | flex-wrap: wrap; 61 | } 62 | 63 | .food-item { 64 | width: 20%; 65 | height: 200px; 66 | margin: 10px; 67 | border-radius: 20px; 68 | overflow: hidden; 69 | } 70 | 71 | .food-item:hover img{ 72 | transform: scale(2) rotate(90deg); 73 | } 74 | 75 | .hide { 76 | display: none; 77 | } 78 | -------------------------------------------------------------------------------- /Hover Board/Hover Board.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Hover Board/Hover Board.zip -------------------------------------------------------------------------------- /Hover Board/app.js: -------------------------------------------------------------------------------- 1 | const container = document.querySelector('.container') 2 | const SQUARES = 200 3 | 4 | for (let i = 0; i < SQUARES; i++) { 5 | const square = document.createElement('div') 6 | square.classList.add('square') 7 | 8 | square.addEventListener('mouseover', () => setColor(square)) 9 | 10 | square.addEventListener('mouseout', () => removeColor(square)) 11 | 12 | container.appendChild(square) 13 | } 14 | 15 | function setColor(element) { 16 | const color = generateRandomColor() 17 | element.style.background = color 18 | element.style.boxShadow = `0 0 10px ${color}, 0 0 100px ${color}` 19 | } 20 | 21 | function removeColor(element) { 22 | element.style.background = '#1d1d1d' 23 | element.style.boxShadow = '0 0 2px #000' 24 | } 25 | 26 | function generateRandomColor() { 27 | let letters = '0123456789ABCDEF' 28 | let color = '#' 29 | for (let i = 0; i < 6; i++) { 30 | color += letters[Math.floor(Math.random() * 16)] 31 | } 32 | return color 33 | } 34 | -------------------------------------------------------------------------------- /Hover Board/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hover Board 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /Hover Board/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | background-color: #111; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | height: 100vh; 13 | } 14 | 15 | .container { 16 | display: flex; 17 | align-items: center; 18 | justify-content: center; 19 | flex-wrap: wrap; 20 | width: 900px; 21 | } 22 | 23 | .square { 24 | background-color: #1d1d1d; 25 | box-shadow: 0 0 2px #000; 26 | height: 40px; 27 | width: 40px; 28 | margin: 2px; 29 | transition: 2s ease; 30 | } 31 | 32 | .square:hover { 33 | transition: 0s; 34 | } 35 | -------------------------------------------------------------------------------- /Image Gallery/Image Gallery.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/Image Gallery.zip -------------------------------------------------------------------------------- /Image Gallery/app.js: -------------------------------------------------------------------------------- 1 | const images = document.querySelectorAll('.wrapper .image img') 2 | const gallery = document.querySelector('.gallery') 3 | const galleryImg = document.querySelector('.gallery-inner img') 4 | const close = document.querySelector('.gallery .close') 5 | 6 | const next = document.querySelector('.control.next') 7 | const prev = document.querySelector('.control.prev') 8 | 9 | let currentIndex = 0 10 | 11 | images.forEach((img, index) => { 12 | img.addEventListener('click', () => { 13 | currentIndex = index 14 | showGallery() 15 | }) 16 | }) 17 | 18 | function showGallery() { 19 | currentIndex == images.length - 1 20 | ? next.classList.add('hide') 21 | : next.classList.remove('hide') 22 | 23 | currentIndex == 0 ? prev.classList.add('hide') : prev.classList.remove('hide') 24 | 25 | gallery.classList.add('show') 26 | galleryImg.src = images[currentIndex].src 27 | } 28 | 29 | close.addEventListener('click', () => { 30 | gallery.classList.remove('show') 31 | }) 32 | 33 | next.addEventListener('click', () => { 34 | currentIndex != images.length - 1 ? currentIndex++ : undefined 35 | showGallery() 36 | }) 37 | prev.addEventListener('click', () => { 38 | currentIndex != 0 ? currentIndex-- : undefined 39 | showGallery() 40 | }) 41 | 42 | // esc click 43 | document.addEventListener('keydown', (e) => { 44 | if (e.keyCode == 27) gallery.classList.remove('show') 45 | }) 46 | -------------------------------------------------------------------------------- /Image Gallery/img1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/img1.jpeg -------------------------------------------------------------------------------- /Image Gallery/img2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/img2.jpeg -------------------------------------------------------------------------------- /Image Gallery/img3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/img3.jpeg -------------------------------------------------------------------------------- /Image Gallery/img4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/img4.jpeg -------------------------------------------------------------------------------- /Image Gallery/img5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/img5.jpeg -------------------------------------------------------------------------------- /Image Gallery/img6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/img6.jpeg -------------------------------------------------------------------------------- /Image Gallery/img7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/img7.jpeg -------------------------------------------------------------------------------- /Image Gallery/img8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Image Gallery/img8.jpeg -------------------------------------------------------------------------------- /Image Gallery/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Image Gallery 8 | 9 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 | 46 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Image Gallery/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | height: 100vh; 9 | background-color: #dadada; 10 | display: flex; 11 | } 12 | 13 | .wrapper { 14 | width: 1200px; 15 | display: flex; 16 | flex-wrap: wrap; 17 | align-items: center; 18 | justify-content: space-between; 19 | margin: auto; 20 | } 21 | 22 | .image { 23 | width: 22%; 24 | height: 200px; 25 | overflow: hidden; 26 | margin: 10px; 27 | cursor: pointer; 28 | border-radius: 4px; 29 | } 30 | 31 | img { 32 | width: 100%; 33 | height: 100%; 34 | object-fit: cover; 35 | object-position: center; 36 | transition: 0.25s; 37 | } 38 | 39 | .image:hover img{ 40 | transform: scale(1.2); 41 | } 42 | 43 | .gallery { 44 | position: fixed; 45 | top: 0; 46 | left: 0; 47 | width: 100%; 48 | height: 100%; 49 | background-color: rgba(0, 0, 0, 0.8); 50 | z-index: 1; 51 | display: flex; 52 | align-items: center; 53 | opacity: 0; 54 | pointer-events: none; 55 | transform: scale(0.8); 56 | transition: 0.25s; 57 | } 58 | 59 | .gallery.show { 60 | opacity: 1; 61 | pointer-events: auto; 62 | transform: scale(1); 63 | } 64 | 65 | .gallery-inner { 66 | width: 70%; 67 | height: 70%; 68 | margin: auto; 69 | } 70 | 71 | .control { 72 | position: absolute; 73 | color: rgba(255, 255, 255, 0.8); 74 | font-size: 50px; 75 | cursor: pointer; 76 | } 77 | 78 | .prev { 79 | left: 15px; 80 | } 81 | 82 | .next { 83 | right: 15px; 84 | } 85 | 86 | .close { 87 | color: white; 88 | font-size: 35px; 89 | position: fixed; 90 | top: 15px; 91 | right: 25px; 92 | cursor: pointer; 93 | } 94 | 95 | .hide { 96 | display: none; 97 | } 98 | -------------------------------------------------------------------------------- /Live Product Filter/Live Product Filter.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Live Product Filter/Live Product Filter.zip -------------------------------------------------------------------------------- /Live Product Filter/app.js: -------------------------------------------------------------------------------- 1 | const products = document.querySelector('.products') 2 | const filter = document.getElementById('filter') 3 | const listItems = [] 4 | 5 | getData() 6 | 7 | filter.addEventListener('input', (e) => filterData(e.target.value)) 8 | 9 | async function getData() { 10 | const res = await fetch('https://fakestoreapi.com/products') 11 | 12 | const results = await res.json() 13 | 14 | // Clear products 15 | products.innerHTML = '' 16 | 17 | results.forEach((product) => { 18 | const div = document.createElement('div') 19 | div.setAttribute('class', 'product') 20 | listItems.push(div) 21 | 22 | div.innerHTML = ` 23 | 24 |
25 |

${product.title.slice(0, 30)}

26 |

$${product.price}

27 |
28 | ` 29 | 30 | products.appendChild(div) 31 | }) 32 | } 33 | 34 | function filterData(search) { 35 | listItems.forEach((item) => { 36 | if (item.innerText.toLowerCase().includes(search.toLowerCase())) { 37 | item.classList.remove('hide') 38 | } else { 39 | item.classList.add('hide') 40 | } 41 | }) 42 | } -------------------------------------------------------------------------------- /Live Product Filter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Live Product Filter 11 | 12 | 13 |
14 |
15 |

Live Product Filter

16 |
17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 |

Loading...

25 |
26 |
27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Live Product Filter/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); 2 | 3 | * { 4 | padding: 0; 5 | margin: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | body { 10 | background-color: #dadada; 11 | font-family: 'Roboto', sans-serif; 12 | height: 100vh; 13 | width: 100%; 14 | } 15 | 16 | .container { 17 | border-radius: 10px; 18 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2); 19 | width: 400px; 20 | margin: 80px auto; 21 | background-color: #fff; 22 | overflow: hidden; 23 | } 24 | 25 | header { 26 | padding: 20px 15px; 27 | } 28 | 29 | h4 { 30 | margin-bottom: 20px; 31 | font-size: 20px; 32 | color: #333; 33 | } 34 | 35 | .form-input { 36 | display: flex; 37 | align-items: center; 38 | border: 1px solid #333; 39 | border-radius: 5px; 40 | } 41 | 42 | .form-input input { 43 | outline: none; 44 | border: none; 45 | background-color: transparent; 46 | font-size: 16px; 47 | padding: 8px 10px; 48 | width: 100%; 49 | } 50 | 51 | .form-input i { 52 | color: #555; 53 | padding-left: 10px; 54 | } 55 | 56 | /* Products */ 57 | .products { 58 | margin-top: 15px; 59 | width: 100%; 60 | overflow-y: auto; 61 | max-height: 400px; 62 | overflow-y: auto; 63 | } 64 | 65 | h3 { 66 | padding: 8px 20px; 67 | } 68 | 69 | .product { 70 | display: flex; 71 | align-items: center; 72 | cursor: pointer; 73 | margin-bottom: 10px; 74 | padding: 8px 20px; 75 | height: 100px; 76 | } 77 | 78 | .product:hover { 79 | background-color: #ddd; 80 | } 81 | 82 | .product img { 83 | width: 50px; 84 | object-fit: cover; 85 | object-position: center; 86 | margin-right: 20px; 87 | } 88 | 89 | .product h4 { 90 | font-size: 15px; 91 | color: #333; 92 | margin-bottom: 15px; 93 | font-weight: 600; 94 | } 95 | 96 | .hide { 97 | display: none; 98 | } 99 | -------------------------------------------------------------------------------- /Play Piano/E.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/E.mp3 -------------------------------------------------------------------------------- /Play Piano/I.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/I.mp3 -------------------------------------------------------------------------------- /Play Piano/O.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/O.mp3 -------------------------------------------------------------------------------- /Play Piano/P.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/P.mp3 -------------------------------------------------------------------------------- /Play Piano/Play Piano.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/Play Piano.zip -------------------------------------------------------------------------------- /Play Piano/Q.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/Q.mp3 -------------------------------------------------------------------------------- /Play Piano/R.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/R.mp3 -------------------------------------------------------------------------------- /Play Piano/T.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/T.mp3 -------------------------------------------------------------------------------- /Play Piano/U.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/U.mp3 -------------------------------------------------------------------------------- /Play Piano/W.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/W.mp3 -------------------------------------------------------------------------------- /Play Piano/Y.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Play Piano/Y.mp3 -------------------------------------------------------------------------------- /Play Piano/app.js: -------------------------------------------------------------------------------- 1 | const buttons = document.querySelectorAll('button') 2 | 3 | buttons.forEach((btn) => { 4 | btn.addEventListener('click', (e) => { 5 | const audio = e.target.querySelector('audio') 6 | playSound(audio) 7 | }) 8 | }) 9 | 10 | const playSound = (audio) => { 11 | const clone = audio.cloneNode() 12 | clone.play() 13 | setTimeout(() => (clone.volume = 0.8), 400) 14 | setTimeout(() => (clone.volume = 0.6), 800) 15 | setTimeout(() => (clone.volume = 0.4), 1200) 16 | setTimeout(() => (clone.volume = 0.2), 1600) 17 | setTimeout(() => (clone.volume = 0), 2000) 18 | } 19 | 20 | document.addEventListener('keydown', (e) => { 21 | const btn = document.querySelector(`.key-${e.key}`) 22 | btn && btn.click() 23 | }) 24 | -------------------------------------------------------------------------------- /Play Piano/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Play Piano 8 | 9 | 10 | 11 |
12 |

Type And Enjoy

13 | 17 | 21 | 25 | 29 | 33 | 37 | 41 | 45 | 49 | 53 |
54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Play Piano/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;400&display=swap'); 2 | 3 | * { 4 | box-sizing: border-box; 5 | padding: 0; 6 | margin: 0; 7 | } 8 | 9 | body { 10 | background-image: linear-gradient(100deg, #e74d89, #febaa6); 11 | font-family: 'Poppins', sans-serif; 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | height: 100vh; 16 | overflow: hidden; 17 | } 18 | 19 | h2 { 20 | color: white; 21 | text-align: center; 22 | margin-bottom: 60px; 23 | } 24 | 25 | button { 26 | outline: none; 27 | border: none; 28 | background-color: #e43075; 29 | border-radius: 6px; 30 | color: #fff; 31 | margin: 10px; 32 | padding: 20px 40px; 33 | font-size: 26px; 34 | cursor: pointer; 35 | } 36 | -------------------------------------------------------------------------------- /Preview Image/Preview Image.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Preview Image/Preview Image.zip -------------------------------------------------------------------------------- /Preview Image/app.js: -------------------------------------------------------------------------------- 1 | const inputImg = document.querySelector('#input-img') 2 | 3 | inputImg.addEventListener('change', (e) => { 4 | let file = e.target.files[0] 5 | 6 | if (!file) return 7 | 8 | let img = document.createElement('img') 9 | img.src = URL.createObjectURL(file) 10 | 11 | document.querySelector('.preview').appendChild(img) 12 | }) -------------------------------------------------------------------------------- /Preview Image/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Preview Image 8 | 15 | 16 | 17 | 18 |
19 | 23 | 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Preview Image/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap'); 2 | * { 3 | margin: 0; 4 | padding: 0; 5 | box-sizing: border-box; 6 | font-family: 'Poppins', sans-serif; 7 | } 8 | 9 | body { 10 | height: 100vh; 11 | overflow: hidden; 12 | background-image: linear-gradient(45deg, #3575d1, #7d4ed4); 13 | } 14 | 15 | .container { 16 | width: 500px; 17 | margin: 120px auto; 18 | } 19 | 20 | .preview { 21 | border: 2px dashed #fff; 22 | width: 100%; 23 | height: 350px; 24 | color: white; 25 | font-size: 22px; 26 | position: relative; 27 | border-radius: 6px; 28 | overflow: hidden; 29 | display: flex; 30 | align-items: center; 31 | justify-content: center; 32 | flex-direction: column; 33 | cursor: pointer; 34 | } 35 | 36 | .preview i { 37 | font-size: 60px; 38 | margin-bottom: 20px; 39 | } 40 | 41 | img { 42 | width: 100%; 43 | height: 100%; 44 | object-fit: cover; 45 | object-position: center; 46 | position: absolute; 47 | top: 0; 48 | left: 0; 49 | z-index: 1; 50 | } 51 | -------------------------------------------------------------------------------- /Product Card/Nike Zoom KD 12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Product Card/Nike Zoom KD 12.png -------------------------------------------------------------------------------- /Product Card/Product Card.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Product Card/Product Card.zip -------------------------------------------------------------------------------- /Product Card/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Product Card 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 |
16 | 17 |
18 |

Nike Zoom KD 12

19 |

$99

20 |
21 |

Size:

22 | 6 23 | 7 24 | 8 25 | 9 26 |
27 |
28 |

Color:

29 | 30 | 31 | 32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /Product Card/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap'); 2 | 3 | :root { 4 | --primary-color: #4daf54; 5 | } 6 | 7 | * { 8 | padding: 0; 9 | margin: 0; 10 | box-sizing: border-box; 11 | } 12 | 13 | body { 14 | font-family: 'Poppins', sans-serif; 15 | height: 100vh; 16 | background-image: linear-gradient(to right, #4daf54, #3d8880); 17 | overflow: hidden; 18 | } 19 | 20 | .card { 21 | margin: 120px auto; 22 | width: 320px; 23 | height: 400px; 24 | background-color: #272d40; 25 | border-radius: 15px; 26 | padding: 40px 20px; 27 | color: white; 28 | position: relative; 29 | } 30 | 31 | .card__img { 32 | width: 90%; 33 | transition: 0.5s; 34 | } 35 | 36 | img { 37 | width: 100%; 38 | height: 100%; 39 | object-fit: cover; 40 | object-position: center; 41 | } 42 | 43 | .card:hover .card__img { 44 | transform: translateY(-90px) rotate(-20deg); 45 | } 46 | 47 | .card__title { 48 | color: var(--primary-color); 49 | transition: 0.5s; 50 | text-align: center; 51 | } 52 | 53 | .card__price { 54 | font-weight: 600; 55 | font-size: 26px; 56 | transition: 0.5s; 57 | text-align: center; 58 | } 59 | 60 | .card:hover .card__title { 61 | transform: translate(-40px, -72px); 62 | } 63 | 64 | .card:hover .card__price { 65 | transform: translate(-112px, -72px); 66 | } 67 | 68 | .card__size, 69 | .card__color, 70 | .card__action { 71 | opacity: 0; 72 | visibility: hidden; 73 | transition: 0.5s; 74 | } 75 | 76 | .card__size { 77 | margin-top: 100px; 78 | } 79 | 80 | .card:hover .card__size { 81 | margin-top: -30px; 82 | } 83 | 84 | .card:hover .card__size, 85 | .card:hover .card__color, 86 | .card:hover .card__action { 87 | transition-delay: 0.2s; 88 | opacity: 1; 89 | visibility: visible; 90 | } 91 | 92 | .card__size, 93 | .card__color { 94 | display: flex; 95 | align-items: center; 96 | margin-bottom: 15px; 97 | } 98 | 99 | .card__size h3, 100 | .card__color h3 { 101 | font-weight: unset; 102 | margin-right: 12px; 103 | } 104 | 105 | .card__size span { 106 | padding: 2px 10px; 107 | background-color: #dadada; 108 | margin: 0 5px; 109 | border-radius: 5px; 110 | color: #272d40; 111 | cursor: pointer; 112 | } 113 | 114 | .card__color span { 115 | width: 20px; 116 | height: 20px; 117 | border-radius: 50%; 118 | margin: 0 5px; 119 | cursor: pointer; 120 | } 121 | 122 | .card__color span.green { 123 | background-color: green; 124 | } 125 | .card__color span.red { 126 | background-color: red; 127 | } 128 | .card__color span.black { 129 | background-color: black; 130 | } 131 | 132 | .card__action button { 133 | padding: 10px 20px; 134 | outline: none; 135 | border: none; 136 | background-color: var(--primary-color); 137 | border-radius: 5px; 138 | cursor: pointer; 139 | color: white; 140 | margin-right: 7px; 141 | font-weight: 600; 142 | transition: 0.25s; 143 | } 144 | 145 | .card__action button:hover { 146 | transform: scale(0.9); 147 | } 148 | 149 | 150 | .like, .cart { 151 | font-size: 25px; 152 | position: absolute; 153 | top: 12px; 154 | left: 20px; 155 | cursor: pointer; 156 | z-index: 2; 157 | } 158 | 159 | .cart { 160 | left: unset; 161 | right: 20px; 162 | } -------------------------------------------------------------------------------- /Profile Card/Cuong.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Profile Card/Cuong.jpg -------------------------------------------------------------------------------- /Profile Card/Profile Card.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Profile Card/Profile Card.zip -------------------------------------------------------------------------------- /Profile Card/aNam.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Profile Card/aNam.jpg -------------------------------------------------------------------------------- /Profile Card/aThai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Profile Card/aThai.jpg -------------------------------------------------------------------------------- /Profile Card/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Profile Card 8 | 15 | 16 | 17 | 18 |
19 |
20 | 21 |
22 |

Dinh Nam

23 |

Fullstack Developer

24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 |
40 |
41 |
42 | 43 |
44 |

Minh Thai

45 |

Fullstack Developer

46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
60 | 61 |
62 |
63 |
64 | 65 |
66 |

Cuong Nv

67 |

Fullstack Developer

68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
82 | 83 |
84 | 85 | 86 | -------------------------------------------------------------------------------- /Profile Card/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap'); 2 | 3 | :root { 4 | --pink-color: #f2726a; 5 | } 6 | 7 | * { 8 | margin: 0; 9 | padding: 0; 10 | box-sizing: border-box; 11 | font-family: 'Poppins', sans-serif; 12 | } 13 | 14 | body { 15 | height: 100vh; 16 | background: linear-gradient(45deg, #00853e, #50c878); 17 | display: flex; 18 | justify-content: center; 19 | align-items: center; 20 | } 21 | 22 | .card { 23 | width: 300px; 24 | height: 400px; 25 | border-radius: 10px; 26 | text-align: center; 27 | background-color: #242628; 28 | overflow: hidden; 29 | margin: 0 30px; 30 | } 31 | 32 | .card__img { 33 | width: 120px; 34 | height: 120px; 35 | overflow: hidden; 36 | transition: 0.25s; 37 | margin: 0 auto; 38 | transform: translateY(25px); 39 | border-radius: 50%; 40 | border: 4px solid var(--pink-color); 41 | cursor: pointer; 42 | } 43 | 44 | .card__img:hover { 45 | width: 100%; 46 | height: 100%; 47 | border-radius: unset; 48 | border: unset; 49 | transform: unset; 50 | } 51 | 52 | img { 53 | width: 100%; 54 | height: 100%; 55 | object-fit: cover; 56 | object-position: center; 57 | } 58 | 59 | h2 { 60 | color: white; 61 | margin-top: 40px; 62 | } 63 | 64 | p { 65 | color: var(--pink-color); 66 | } 67 | 68 | .card__social a { 69 | text-decoration: none; 70 | color: white; 71 | margin: 25px 20px 40px; 72 | display: inline-block; 73 | font-size: 18px; 74 | transition: 0.25s; 75 | } 76 | 77 | .card__social a:hover { 78 | color: var(--pink-color); 79 | } 80 | 81 | button { 82 | outline: none; 83 | border: none; 84 | color: white; 85 | background-color: transparent; 86 | border: 1px solid var(--pink-color); 87 | padding: 10px 20px; 88 | border-radius: inherit; 89 | cursor: pointer; 90 | transition: 0.25s; 91 | } 92 | 93 | button:hover { 94 | background-color: var(--pink-color); 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Hơn 30+ Projects thực chiến - HTML CSS JavaScript](https://www.nodemy.vn/projects-html-css-js) 2 | 3 | 👉👉 **[Chi tiết 30 projects thực chiến HTML CSS Javascript](https://www.nodemy.vn/projects-html-css-js)** 👈👈 4 | 5 | | # | Project | Live Demo | 6 | | :-: | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | 7 | | 01 | [Product Card](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Product%20Card) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/product-card/) | 8 | | 02 | [Profile Card](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Profile%20Card) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/profile-card/) | 9 | | 03 | [Create Modal](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Create%20Modal) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/create-modal/) | 10 | | 04 | [Image Gallery](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Image%20Gallery) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/image-gallery/) | 11 | | 05 | [Search Box](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Search%20Box) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/search-box/) | 12 | | 06 | [Detect Pressed Key](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Detect%20Pressed%20Key) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/detect-pressed-key/) | 13 | | 07 | [Search Tags](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Search%20Tags) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/search-tags/) | 14 | | 08 | [Register Form Validator](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Register%20Form%20Validator) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/register-form-validator/) | 15 | | 09 | [Weather App](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Weather%20App) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/weather-app/) | 16 | | 10 | [Todo List](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Todo%20List) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/todo-list/) | 17 | | 11 | [Toast Notification](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Toast%20Notification) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/toast-notification/) | 18 | | 12 | [Create Range Slider](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Create%20Range%20Slider) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/create-range-slider/) | 19 | | 13 | [Preview Image](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Preview%20Image) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/preview-image/) | 20 | | 14 | [Slideshow](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Slideshow) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/slideshow/) | 21 | | 15 | [Live Product Filter](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Live%20Product%20Filter) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/live-product-filter/) | 22 | | 16 | [Show On Scroll](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Show%20On%20Scroll) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/show-on-scroll/) | 23 | | 17 | [Counter Up](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Counter%20Up) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/counter-up/) | 24 | | 18 | [Drag And Drop](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Drag%20And%20Drop) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/drag-and-drop/) | 25 | | 19 | [Drawing App](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Drawing%20App) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/drawing-app/) | 26 | | 20 | [Typing Animation Effect](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Typing%20Animation%20Effect) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/typing-animation-effect/) | 27 | | 21 | [Dark And Light Mode](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Dark%20And%20Light%20Mode) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/dark-and-light-mode/) | 28 | | 22 | [Filter Elements](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Filter%20Elements) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/filter-elements/) | 29 | | 23 | [Zoom Image](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Zoom%20Image) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/zoom-image/) | 30 | | 24 | [Hover Board](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Hover%20Board) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/hover-board/) | 31 | | 25 | [Custom Video Player](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Custom%20Video%20Player) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/custom-video-player/) | 32 | | 26 | [Validator Password](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Vaidator%20Password) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/validator-password/) | 33 | | 27 | [Tooltip](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Tooltip) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/tooltip/) | 34 | | 28 | [Skeleton Loading Effect](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Skeleton%20Loading%20Effect) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/skeleton-loading-effect/) | 35 | | 29 | [Play Piano](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Play%20Piano) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/play-piano/) | 36 | | 30 | [Simple Carousel](https://github.com/namndwebdev/html-css-js-thuc-chien/tree/main/Simple%20Carousel) | [Live Demo](https://www.nodemy.vn/projects/html-css-js/simple-carousel/) | 37 | 38 | 39 | **TÍNH NĂNG:** 40 | 41 | - Live-demo: Xem demo trực tiếp 42 | - Youtube: Xem video hướng dẫn làm 43 | - Github: Link source 44 | - Code Mẫu: Cho phép sửa và thử code online 45 | 46 | **LƯU Ý: Mỗi tuần Nodemy sẽ ra thêm một project thực chiến giúp các bạn nâng cao kinh nghiệm code. Hãy chia sẻ dự án nếu bạn thấy có ích để team thêm động lực ra thêm nhiều dự án hơn về ReactJS, NodeJS ExpressJS, Docker... Xin cảm ơn** 47 | 48 | - Mọi đóng góp xin liên hệ : https://www.facebook.com/nam.nodemy 49 | - Nhóm Zalo tự học lập trình: https://zalo.me/g/yhdkef092 50 | -------------------------------------------------------------------------------- /Register Form Validator/Register Form Validator.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Register Form Validator/Register Form Validator.zip -------------------------------------------------------------------------------- /Register Form Validator/app.js: -------------------------------------------------------------------------------- 1 | const form = document.querySelector('form') 2 | const username = document.getElementById('username') 3 | const email = document.getElementById('email') 4 | const password = document.getElementById('password') 5 | const password2 = document.getElementById('password2') 6 | 7 | // Show input error message 8 | function showError(input, message) { 9 | const formControl = input.parentElement 10 | formControl.className = 'form-control error' 11 | const small = formControl.querySelector('small') 12 | small.innerText = message 13 | } 14 | 15 | // Show success outline 16 | function showSuccess(input) { 17 | const formControl = input.parentElement 18 | formControl.className = 'form-control success' 19 | const small = formControl.querySelector('small') 20 | small.innerText = '' 21 | } 22 | 23 | // Check email is valid 24 | function checkEmail(input) { 25 | const re = 26 | /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 27 | 28 | if (re.test(input.value.trim())) { 29 | showSuccess(input) 30 | } else { 31 | showError(input, 'Email is not valid') 32 | } 33 | } 34 | 35 | // Check required fields 36 | function checkRequired(inputArr) { 37 | let isRequired = false 38 | inputArr.forEach(function (input) { 39 | if (input.value.trim() === '') { 40 | showError(input, `${getFieldName(input)} is required`) 41 | isRequired = true 42 | } else { 43 | showSuccess(input) 44 | } 45 | }) 46 | 47 | return isRequired 48 | } 49 | 50 | // Check input length 51 | function checkLength(input, min, max) { 52 | if (input.value.length < min) { 53 | showError( 54 | input, 55 | `${getFieldName(input)} must be at least ${min} characters` 56 | ) 57 | } else if (input.value.length > max) { 58 | showError( 59 | input, 60 | `${getFieldName(input)} must be less than ${max} characters` 61 | ) 62 | } else { 63 | showSuccess(input) 64 | } 65 | } 66 | 67 | // Check passwords match 68 | function checkPasswordsMatch(input1, input2) { 69 | if (input1.value !== input2.value) { 70 | showError(input2, 'Passwords do not match') 71 | } 72 | } 73 | 74 | // Get fieldname 75 | function getFieldName(input) { 76 | return input.id.charAt(0).toUpperCase() + input.id.slice(1) 77 | } 78 | 79 | // Event listeners 80 | form.addEventListener('submit', function (e) { 81 | e.preventDefault() 82 | 83 | if (!checkRequired([username, email, password, password2])) { 84 | checkLength(username, 3, 15) 85 | checkLength(password, 6, 25) 86 | checkEmail(email) 87 | checkPasswordsMatch(password, password2) 88 | } 89 | }) 90 | -------------------------------------------------------------------------------- /Register Form Validator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Register Form Validator 8 | 9 | 10 | 11 |
12 |

Register

13 |
14 |
15 | 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 |
29 |
30 | 35 | 36 | 37 |
38 | 39 | 40 |
41 |
42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Register Form Validator/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | 10 | body { 11 | background: linear-gradient(120deg, #3ca7ee, #9b408f); 12 | height: 100vh; 13 | overflow: hidden; 14 | } 15 | 16 | .container { 17 | position: absolute; 18 | top: 50%; 19 | left: 50%; 20 | transform: translate(-50%, -50%); 21 | width: 400px; 22 | background: white; 23 | border-radius: 10px; 24 | box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.05); 25 | } 26 | 27 | .container h1 { 28 | text-align: center; 29 | padding-top: 20px; 30 | } 31 | 32 | .container form { 33 | padding: 0 40px; 34 | } 35 | 36 | form .form-control { 37 | position: relative; 38 | border-bottom: 2px solid #adadad; 39 | margin: 40px 0; 40 | } 41 | 42 | .form-control.success { 43 | border-bottom-color: #2691d9; 44 | } 45 | 46 | .form-control.error { 47 | border-bottom-color: #e74c3c; 48 | } 49 | 50 | .form-control input { 51 | width: 100%; 52 | height: 40px; 53 | font-size: 16px; 54 | border: none; 55 | background: none; 56 | outline: none; 57 | } 58 | 59 | small { 60 | position: absolute; 61 | left: 0; 62 | top: 100%; 63 | margin-top: 3px; 64 | color: #e74c3c; 65 | } 66 | 67 | .form-control span::before { 68 | content: ''; 69 | position: absolute; 70 | top: 40px; 71 | left: 0; 72 | width: 0%; 73 | height: 2px; 74 | background: #2691d9; 75 | transition: 0.3s; 76 | } 77 | 78 | .form-control input:focus ~ span::before { 79 | width: 100%; 80 | } 81 | 82 | input[type='submit'] { 83 | margin-top: 20px; 84 | width: 100%; 85 | height: 50px; 86 | border: 1px solid; 87 | background: #2691d9; 88 | border-radius: 25px; 89 | font-size: 18px; 90 | color: #e9f4fb; 91 | font-weight: 700; 92 | cursor: pointer; 93 | outline: none; 94 | transition: 0.5s; 95 | } 96 | 97 | input[type='submit']:hover { 98 | background-color: #08609a; 99 | } 100 | 101 | .signup_link { 102 | margin: 25px 0; 103 | text-align: center; 104 | font-size: 16px; 105 | color: #666666; 106 | } 107 | 108 | .signup_link a { 109 | color: #2691d9; 110 | text-decoration: none; 111 | } 112 | 113 | .signup_link a:hover { 114 | text-decoration: underline; 115 | } 116 | -------------------------------------------------------------------------------- /Search Box/Search Box.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Search Box/Search Box.zip -------------------------------------------------------------------------------- /Search Box/app.js: -------------------------------------------------------------------------------- 1 | document.querySelector('.search-btn').addEventListener('click', function () { 2 | this.parentElement.classList.toggle('open') 3 | this.previousElementSibling.focus() 4 | }) 5 | -------------------------------------------------------------------------------- /Search Box/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Search Box 8 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Search Box/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | height: 100vh; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | background-image: linear-gradient(to right, #4daf54, #3d8880); 13 | } 14 | 15 | .search-box { 16 | width: 70px; 17 | height: 70px; 18 | background-color: #242628; 19 | position: relative; 20 | overflow: hidden; 21 | transition: all 0.5s ease; 22 | border-radius: 15px; 23 | } 24 | 25 | .search-box.open { 26 | width: 420px; 27 | } 28 | 29 | .search-input { 30 | width: 100%; 31 | height: 100%; 32 | border: none; 33 | box-shadow: none; 34 | background: transparent; 35 | color: #fff; 36 | padding: 20px 100px 20px 35px; 37 | font-size: 40px; 38 | } 39 | 40 | .search-btn { 41 | color: #242628; 42 | outline: none; 43 | border: none; 44 | width: 70px; 45 | height: 70px; 46 | position: absolute; 47 | right: 0; 48 | top: 0; 49 | cursor: pointer; 50 | font-size: 30px; 51 | } 52 | -------------------------------------------------------------------------------- /Search Tags/Search Tags.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Search Tags/Search Tags.zip -------------------------------------------------------------------------------- /Search Tags/app.js: -------------------------------------------------------------------------------- 1 | const ul = document.querySelector('ul'), 2 | input = document.querySelector('input') 3 | 4 | let tags = ['nodejs', 'reactjs'] 5 | 6 | createTag() 7 | 8 | function createTag() { 9 | ul.innerHTML = '' 10 | tags.forEach((tag, index) => { 11 | let liTag = ` 12 |
  • ${tag} 13 | 14 |
  • 15 | ` 16 | ul.innerHTML += liTag 17 | }) 18 | 19 | ul.appendChild(input) 20 | input.focus() 21 | } 22 | 23 | function removeTag(element, index) { 24 | tags.splice(index, 1) 25 | element.parentElement.remove() 26 | input.focus() 27 | } 28 | 29 | function addTag(e) { 30 | if (e.key == 'Enter') { 31 | let tag = e.target.value.trim() 32 | if (tag != '' && !tags.includes(tag)) { 33 | tags.push(tag) 34 | createTag() 35 | } 36 | e.target.value = '' 37 | } 38 | } 39 | 40 | input.addEventListener('keyup', addTag) 41 | 42 | const removeBtn = document.querySelector('.btn-removeAll') 43 | removeBtn.addEventListener('click', () => { 44 | tags.length = 0 45 | createTag() 46 | }) 47 | -------------------------------------------------------------------------------- /Search Tags/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Search Tags 6 | 7 | 11 | 12 | 13 | 14 |
    15 |

    Search Tags

    16 | 19 | 20 |
    21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Search Tags/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | body { 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | height: 100vh; 14 | background: #8cc84c; 15 | } 16 | 17 | .wrapper { 18 | width: 500px; 19 | background: #fff; 20 | border-radius: 10px; 21 | padding: 20px; 22 | font-size: 16px; 23 | } 24 | 25 | .title { 26 | font-size: 22px; 27 | font-weight: 600; 28 | } 29 | 30 | .content { 31 | display: flex; 32 | flex-wrap: wrap; 33 | padding: 10px; 34 | margin: 20px 0; 35 | border-radius: 5px; 36 | border: 1px solid #888; 37 | } 38 | 39 | .content li { 40 | color: #333; 41 | margin: 4px 3px; 42 | list-style: none; 43 | border-radius: 5px; 44 | background: #8cc84c; 45 | padding: 10px; 46 | color: white; 47 | } 48 | 49 | .content li i { 50 | margin-left: 10px; 51 | cursor: pointer; 52 | } 53 | 54 | .content input { 55 | flex: 1; 56 | padding: 5px; 57 | border: none; 58 | outline: none; 59 | font-size: 20px; 60 | } 61 | 62 | button { 63 | border: none; 64 | outline: none; 65 | color: #fff; 66 | cursor: pointer; 67 | padding: 10px 20px; 68 | border-radius: 5px; 69 | background: #8cc84c; 70 | transition: all 0.3s ease; 71 | font-size: 16px; 72 | } 73 | 74 | button:hover { 75 | background: #7cac49; 76 | } 77 | -------------------------------------------------------------------------------- /Show On Scroll/Show On Scroll.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Show On Scroll/Show On Scroll.zip -------------------------------------------------------------------------------- /Show On Scroll/app.js: -------------------------------------------------------------------------------- 1 | let elToShow = document.querySelectorAll('.show-on-scroll') 2 | 3 | let isElInViewPort = (el) => { 4 | let rect = el.getBoundingClientRect() 5 | // some browsers support innerHeight, others support documentElement.clientHeight 6 | let viewHeight = window.innerHeight || document.documentElement.clientHeight 7 | 8 | return ( 9 | (rect.top <= 0 && rect.bottom >= 0) || 10 | (rect.bottom >= viewHeight && rect.top <= viewHeight) || 11 | (rect.top >= 0 && rect.bottom <= viewHeight) 12 | ) 13 | } 14 | 15 | function loop() { 16 | elToShow.forEach((item) => { 17 | if (isElInViewPort(item)) { 18 | item.classList.add('start') 19 | } else { 20 | item.classList.remove('start') 21 | } 22 | }) 23 | } 24 | 25 | window.onscroll = loop 26 | 27 | loop() -------------------------------------------------------------------------------- /Show On Scroll/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Show On Scroll 8 | 9 | 10 | 11 |
    12 |
    13 |

    Nodemy

    14 |

    15 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum 16 | recusandae sed illo sit, saepe laboriosam ea fugit, architecto quis 17 | quia 18 |

    19 |
    20 |
    21 | 22 |
    23 |
    24 |

    Title

    25 |

    26 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Pariatur, 27 | eveniet sequi. Nobis, dolorum culpa doloribus adipisci ut ab iure non 28 | voluptates vel exercitationem explicabo nam, sunt maxime error nemo 29 | assumenda. 30 |

    31 |
    32 |
    33 |

    Title

    34 |

    35 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Pariatur, 36 | eveniet sequi. Nobis, dolorum culpa doloribus adipisci ut ab iure non 37 | voluptates vel exercitationem explicabo nam, sunt maxime error nemo 38 | assumenda. 39 |

    40 |
    41 |
    42 | 43 |
    44 |
    45 |

    Person 1

    46 |

    47 | Lorem ipsum dolor sit amet consectetur adipisicing elit. In 48 | repudiandae sequi, ad cum aperiam odit eius earum molestiae voluptatem 49 |

    50 |
    51 |
    52 |

    Person 2

    53 |

    54 | Lorem ipsum dolor sit amet consectetur adipisicing elit. In 55 | repudiandae sequi, ad cum aperiam odit eius earum molestiae voluptatem 56 |

    57 |
    58 |
    59 |

    Person 3

    60 |

    61 | Lorem ipsum dolor sit amet consectetur adipisicing elit. In 62 | repudiandae sequi, ad cum aperiam odit eius earum molestiae voluptatem 63 |

    64 |
    65 |
    66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Show On Scroll/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | font-family: sans-serif; 6 | } 7 | 8 | body { 9 | overflow-x: hidden; 10 | } 11 | 12 | .full-height { 13 | height: 100vh; 14 | } 15 | 16 | .left-to-right { 17 | transform: translateX(-150%); 18 | } 19 | 20 | .left-to-right.start { 21 | transition: 0.7s ease-in-out; 22 | transform: translateX(0); 23 | } 24 | 25 | .right-to-left { 26 | transform: translateX(150%); 27 | } 28 | 29 | .right-to-left.start { 30 | transition: 0.7s ease-in-out; 31 | transform: translateX(0); 32 | } 33 | 34 | .zoom { 35 | transform: scale(0); 36 | } 37 | 38 | .zoom.start { 39 | transition: 0.5s ease-in-out; 40 | transform: unset; 41 | } 42 | 43 | #home { 44 | background-image: linear-gradient(to right, #4daf54, #3d8880); 45 | color: white; 46 | display: flex; 47 | align-items: center; 48 | padding-left: 80px; 49 | } 50 | 51 | #home h1 { 52 | font-size: 50px; 53 | margin-bottom: 20px; 54 | } 55 | 56 | #home p { 57 | font-size: 20px; 58 | width: 700px; 59 | transition-delay: 0.2s; 60 | } 61 | 62 | #about { 63 | display: flex; 64 | align-items: center; 65 | justify-content: center; 66 | } 67 | 68 | .box { 69 | background-color: #242628; 70 | width: 500px; 71 | border-radius: 10px; 72 | margin: 0 20px; 73 | padding: 20px; 74 | color: white; 75 | } 76 | 77 | .box h2 { 78 | margin-bottom: 15px; 79 | } 80 | 81 | #team { 82 | background: linear-gradient(180deg, #017c2c 50%, #fff 50%); 83 | display: flex; 84 | align-items: center; 85 | justify-content: center; 86 | } 87 | 88 | #team .person { 89 | width: 200px; 90 | padding: 20px; 91 | margin: 0 25px; 92 | background-color: #fff; 93 | box-shadow: 0 0 5px #2e363a; 94 | border-radius: 10px; 95 | } 96 | 97 | .person h3 { 98 | color: #017c2c; 99 | font-size: 22px; 100 | margin-bottom: 20px; 101 | } 102 | -------------------------------------------------------------------------------- /Simple Carousel/Simple Carousel.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Simple Carousel/Simple Carousel.zip -------------------------------------------------------------------------------- /Simple Carousel/app.js: -------------------------------------------------------------------------------- 1 | const wrapper = document.querySelector('.wrapper') 2 | let isMouseDown = false 3 | let startX, scrollLeft 4 | 5 | wrapper.addEventListener('mousedown', (e) => { 6 | isMouseDown = true 7 | startX = e.pageX - wrapper.offsetLeft 8 | scrollLeft = wrapper.scrollLeft 9 | }) 10 | 11 | wrapper.addEventListener('mouseleave', () => { 12 | isMouseDown = false 13 | }) 14 | 15 | wrapper.addEventListener('mouseup', () => { 16 | isMouseDown = false 17 | }) 18 | 19 | wrapper.addEventListener('mousemove', (e) => { 20 | if (!isMouseDown) return 21 | 22 | const x = e.pageX - wrapper.offsetLeft 23 | // 3 là tốc độ scroll 24 | const walk = (x - startX) * 3 25 | wrapper.scrollLeft = scrollLeft - walk 26 | }) 27 | -------------------------------------------------------------------------------- /Simple Carousel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple Carousel 8 | 9 | 10 | 11 |
    12 |
    01
    13 |
    02
    14 |
    03
    15 |
    04
    16 |
    05
    17 |
    06
    18 |
    07
    19 |
    08
    20 |
    09
    21 |
    10
    22 |
    11
    23 |
    12
    24 |
    13
    25 |
    14
    26 |
    15
    27 |
    16
    28 |
    17
    29 |
    18
    30 |
    19
    31 |
    20
    32 |
    21
    33 |
    22
    34 |
    23
    35 |
    24
    36 |
    25
    37 |
    38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Simple Carousel/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | font-family: sans-serif; 6 | } 7 | 8 | body { 9 | height: 100vh; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | background-image: linear-gradient(145deg, #413579, #632a4c); 14 | } 15 | 16 | .wrapper { 17 | margin: 0 100px; 18 | overflow: hidden; 19 | white-space: nowrap; 20 | user-select: none; 21 | cursor: pointer; 22 | border: 2px solid #dadada; 23 | padding: 20px; 24 | overflow-x: auto; 25 | } 26 | 27 | .wrapper::-webkit-scrollbar { 28 | width: 0; 29 | } 30 | 31 | .box { 32 | width: 200px; 33 | height: 200px; 34 | display: inline-flex; 35 | align-items: center; 36 | justify-content: center; 37 | font-size: 60px; 38 | color: white; 39 | background-color: #7043a5; 40 | margin: 0 10px; 41 | } 42 | -------------------------------------------------------------------------------- /Skeleton Loading Effect/Skeleton Loading Effect.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Skeleton Loading Effect/Skeleton Loading Effect.zip -------------------------------------------------------------------------------- /Skeleton Loading Effect/app.js: -------------------------------------------------------------------------------- 1 | const cardImg = document.querySelector('.card__img') 2 | const cardHeading = document.querySelector('.card__info h2') 3 | const cardContent = document.querySelector('.card__info p') 4 | const cardBtn = document.querySelector('.card__info button') 5 | 6 | setTimeout(() => { 7 | cardImg.innerHTML = '' 8 | 9 | cardHeading.innerHTML = 'Nodemy' 10 | cardContent.innerHTML = 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Ut soluta qui repudiandae, maxime optio voluptatem eius eveniet officiis' 11 | cardBtn.innerHTML = 'Read More' 12 | 13 | // remove loading 14 | document.querySelectorAll('.loading').forEach(item => { 15 | item.classList.remove('loading') 16 | }) 17 | 18 | }, 4000) -------------------------------------------------------------------------------- /Skeleton Loading Effect/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Skeleton Loading Effect 8 | 9 | 10 | 11 |
    12 |
    13 |
    14 |

    15 |

    16 | 17 |
    18 |
    19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Skeleton Loading Effect/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | 10 | body { 11 | height: 100vh; 12 | background-color: #b1b9d4; 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | } 17 | 18 | .card { 19 | width: 700px; 20 | background-color: #fff; 21 | border-radius: 10px; 22 | display: flex; 23 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); 24 | padding: 20px; 25 | } 26 | 27 | .card__img { 28 | width: 250px; 29 | border-radius: 10px; 30 | overflow: hidden; 31 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 32 | } 33 | 34 | .card__img img { 35 | width: 100%; 36 | height: 100%; 37 | object-fit: cover; 38 | object-position: center; 39 | } 40 | 41 | .card__info { 42 | flex: 1; 43 | padding-left: 25px; 44 | } 45 | 46 | .card__info h2 { 47 | height: 40px; 48 | margin-bottom: 5px; 49 | } 50 | 51 | .card__info p { 52 | margin-bottom: 20px; 53 | height: 80px; 54 | } 55 | 56 | .card__info button { 57 | outline: none; 58 | border: none; 59 | background-color: #000; 60 | color: white; 61 | min-width: 100px; 62 | height: 40px; 63 | border-radius: 10px; 64 | } 65 | 66 | .loading { 67 | position: relative; 68 | background-color: #b8b8b8 !important; 69 | overflow: hidden; 70 | border-radius: 10px; 71 | } 72 | 73 | .loading::after { 74 | content: ''; 75 | position: absolute; 76 | left: 0; 77 | top: 0; 78 | width: 100%; 79 | height: 100%; 80 | transform: translateX(-100%); 81 | background-image: linear-gradient( 82 | to right, 83 | rgba(255, 255, 255, 0.5), 84 | transparent 85 | ); 86 | animation: loading 1s infinite; 87 | } 88 | 89 | @keyframes loading { 90 | 100% { 91 | transform: translateX(100%); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Slideshow/Slideshow.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/Slideshow.zip -------------------------------------------------------------------------------- /Slideshow/app.js: -------------------------------------------------------------------------------- 1 | let listDivImg = document.querySelectorAll('.list-img div') 2 | let next = document.querySelector('.next') 3 | let prev = document.querySelector('.prev') 4 | let imgWrap = document.querySelector('.img-wrap img') 5 | 6 | let currentIndex = 0 7 | 8 | setCurrent(currentIndex) 9 | 10 | function setCurrent(index) { 11 | currentIndex = index 12 | imgWrap.src = listDivImg[currentIndex].querySelector('img').src 13 | 14 | // remove all active img 15 | listDivImg.forEach((item) => { 16 | item.classList.remove('active') 17 | }) 18 | 19 | // set active 20 | listDivImg[currentIndex].classList.add('active') 21 | } 22 | 23 | listDivImg.forEach((img, index) => { 24 | img.addEventListener('click', (e) => { 25 | setCurrent(index) 26 | }) 27 | }) 28 | 29 | next.addEventListener('click', () => { 30 | if (currentIndex == listDivImg.length - 1) { 31 | currentIndex = 0 32 | } else currentIndex++ 33 | 34 | setCurrent(currentIndex) 35 | }) 36 | 37 | prev.addEventListener('click', () => { 38 | if (currentIndex == 0) currentIndex = listDivImg.length - 1 39 | else currentIndex-- 40 | 41 | setCurrent(currentIndex) 42 | }) 43 | -------------------------------------------------------------------------------- /Slideshow/img1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img1.jpeg -------------------------------------------------------------------------------- /Slideshow/img2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img2.jpeg -------------------------------------------------------------------------------- /Slideshow/img3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img3.jpeg -------------------------------------------------------------------------------- /Slideshow/img4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img4.jpeg -------------------------------------------------------------------------------- /Slideshow/img5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img5.jpeg -------------------------------------------------------------------------------- /Slideshow/img6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img6.jpeg -------------------------------------------------------------------------------- /Slideshow/img7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img7.jpeg -------------------------------------------------------------------------------- /Slideshow/img8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img8.jpeg -------------------------------------------------------------------------------- /Slideshow/img9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Slideshow/img9.jpeg -------------------------------------------------------------------------------- /Slideshow/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Slideshow 8 | 9 | 13 | 14 | 15 | 16 |
    17 |
    18 | 19 | 20 | 21 | 22 | 23 | 24 |
    25 | 26 |
    27 |
    28 |
    29 |
    30 | 31 |
    32 |
    33 | 34 |
    35 |
    36 | 37 |
    38 |
    39 | 40 |
    41 |
    42 | 43 |
    44 |
    45 | 46 |
    47 |
    48 | 49 |
    50 |
    51 | 52 |
    53 |
    54 | 55 |
    56 |
    57 |
    58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Slideshow/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | font-family: sans-serif; 6 | } 7 | 8 | body { 9 | width: 100%; 10 | height: 100vh; 11 | background-color: #dadada; 12 | } 13 | 14 | img { 15 | width: 100%; 16 | height: 100%; 17 | object-fit: cover; 18 | object-position: center; 19 | } 20 | 21 | .container { 22 | margin: 50px auto; 23 | height: 80%; 24 | width: 70%; 25 | box-shadow: 0 0 10px #bbb; 26 | } 27 | 28 | .main { 29 | height: 85%; 30 | position: relative; 31 | } 32 | 33 | .control { 34 | position: absolute; 35 | top: 50%; 36 | transform: translateY(-50%); 37 | font-size: 80px; 38 | color: white; 39 | cursor: pointer; 40 | } 41 | 42 | .prev { 43 | left: -10px; 44 | } 45 | 46 | .next { 47 | right: -10px; 48 | } 49 | 50 | .img-wrap { 51 | width: 100%; 52 | height: 100%; 53 | } 54 | 55 | .list-img { 56 | display: flex; 57 | } 58 | 59 | .list-img div { 60 | cursor: pointer; 61 | padding: 5px; 62 | background-color: #bbb; 63 | flex: 1; 64 | } 65 | 66 | .list-img div.active { 67 | background-color: rgb(220, 86, 86); 68 | } -------------------------------------------------------------------------------- /Toast Notification/Toast Notification.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Toast Notification/Toast Notification.zip -------------------------------------------------------------------------------- /Toast Notification/app.js: -------------------------------------------------------------------------------- 1 | const buttonShows = document.querySelectorAll('.control button') 2 | buttonShows.forEach((btn) => { 3 | btn.addEventListener('click', (e) => { 4 | createToast(e.target.getAttribute('class')) 5 | }) 6 | }) 7 | 8 | const toasts = { 9 | success: { 10 | icon: '', 11 | msg: 'This is a success message !', 12 | }, 13 | error: { 14 | icon: '', 15 | msg: 'This is a error message !', 16 | }, 17 | warning: { 18 | icon: '', 19 | msg: 'This is a warning message !', 20 | }, 21 | } 22 | 23 | function createToast(status) { 24 | let toast = document.createElement('div') 25 | toast.className = `toast ${status}` 26 | 27 | toast.innerHTML = ` 28 | ${toasts[status].icon} 29 | ${toasts[status].msg} 30 | 31 | ` 32 | document.querySelector('#toasts').appendChild(toast) 33 | 34 | setTimeout(() => { 35 | toast.style.animation = 'hide_slide 1s ease forwards' 36 | }, 4000) 37 | setTimeout(() => { 38 | toast.remove() 39 | }, 6000) 40 | } 41 | -------------------------------------------------------------------------------- /Toast Notification/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Toast Notification 8 | 15 | 16 | 17 | 18 | 19 |
    20 | 21 | 22 | 23 |
    24 |
    25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Toast Notification/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | 10 | body { 11 | height: 100vh; 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | background-color: #ecf0f1; 16 | } 17 | 18 | button { 19 | padding: 8px 16px; 20 | font-size: 18px; 21 | border-radius: 4px; 22 | border: none; 23 | outline: none; 24 | color: white; 25 | cursor: pointer; 26 | margin: 0 10px; 27 | } 28 | 29 | button.success { 30 | background-color: rgb(0, 128, 0); 31 | } 32 | button.warning { 33 | background-color: rgb(255, 165, 0); 34 | } 35 | button.error { 36 | background-color: rgb(255, 0, 0); 37 | } 38 | 39 | #toasts { 40 | position: fixed; 41 | top: 10px; 42 | right: 10px; 43 | display: flex; 44 | flex-direction: column; 45 | } 46 | 47 | .toast { 48 | padding: 20px; 49 | border-radius: 4px; 50 | overflow: hidden; 51 | margin-bottom: 10px; 52 | animation: show_slide 1s ease forwards; 53 | display: flex; 54 | align-items: center; 55 | border-left: 6px solid; 56 | } 57 | 58 | .toast.success { 59 | background-color: rgba(0, 128, 0, 0.4); 60 | border-color: rgb(0, 128, 0); 61 | } 62 | 63 | .toast.warning { 64 | background: rgba(255, 165, 0, 0.4); 65 | border-color: rgb(255, 165, 0); 66 | } 67 | 68 | .toast.error { 69 | background-color: rgba(255, 0, 0, 0.4); 70 | border-color: rgb(255, 0, 0); 71 | } 72 | 73 | .toast i { 74 | font-size: 28px; 75 | color: white; 76 | } 77 | 78 | .toast .msg { 79 | padding: 0 20px; 80 | font-size: 16px; 81 | color: white; 82 | } 83 | 84 | .countdown { 85 | position: absolute; 86 | bottom: 0; 87 | left: 0; 88 | width: 100%; 89 | height: 4px; 90 | z-index: 1; 91 | animation: countdown 4s linear forwards; 92 | } 93 | 94 | .toast.success .countdown { 95 | background-color: rgb(0, 128, 0); 96 | } 97 | 98 | .toast.warning .countdown { 99 | background-color: rgb(255, 165, 0); 100 | } 101 | 102 | .toast.error .countdown { 103 | background-color: rgb(255, 0, 0); 104 | } 105 | 106 | @keyframes countdown { 107 | 20% { 108 | width: 100%; 109 | } 110 | 100% { 111 | width: 0; 112 | } 113 | } 114 | 115 | @keyframes show_slide { 116 | 0% { 117 | transform: translateX(100%); 118 | } 119 | 40% { 120 | transform: translateX(-10%); 121 | } 122 | 80% { 123 | transform: translateX(0%); 124 | } 125 | 100% { 126 | transform: translateX(-10px); 127 | } 128 | } 129 | 130 | @keyframes hide_slide { 131 | 0% { 132 | transform: translateX(-10px); 133 | } 134 | 40% { 135 | transform: translateX(0%); 136 | } 137 | 80% { 138 | transform: translateX(-10%); 139 | } 140 | 100% { 141 | transform: translateX(120%); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Todo List/Todo List.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Todo List/Todo List.zip -------------------------------------------------------------------------------- /Todo List/app.js: -------------------------------------------------------------------------------- 1 | const input = document.querySelector('form input') 2 | const ul = document.querySelector('.todos') 3 | const form = document.querySelector('form') 4 | 5 | const todos = JSON.parse(localStorage.getItem('todos')) 6 | 7 | if (todos) { 8 | todos.forEach((todo) => addTodo(todo)) 9 | } 10 | 11 | function addTodo(todo) { 12 | const li = document.createElement('li') 13 | 14 | li.setAttribute('class', todo.completed ? 'completed' : '') 15 | li.innerHTML = ` 16 | ${todo.text} 17 | 18 | ` 19 | 20 | li.addEventListener('click', function () { 21 | this.classList.toggle('completed') 22 | updateTodos() 23 | }) 24 | 25 | li.querySelector('i').addEventListener('click', (e) => { 26 | e.target.parentElement.remove() 27 | updateTodos() 28 | }) 29 | 30 | ul.appendChild(li) 31 | updateTodos() 32 | } 33 | 34 | form.addEventListener('submit', (e) => { 35 | e.preventDefault() 36 | const text = input.value.trim() 37 | text != '' ? addTodo({ text, completed: false }) : undefined 38 | input.value = '' 39 | }) 40 | 41 | function updateTodos() { 42 | const list = document.querySelectorAll('li') 43 | 44 | const todos = [] 45 | 46 | list.forEach((item) => { 47 | todos.push({ 48 | text: item.querySelector('span').innerHTML, 49 | completed: item.classList.contains('completed'), 50 | }) 51 | }) 52 | 53 | localStorage.setItem('todos', JSON.stringify(todos)) 54 | } 55 | -------------------------------------------------------------------------------- /Todo List/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Todo List 8 | 15 | 16 | 17 | 18 |
    19 |
    20 | 21 | 22 |
    23 | 24 |
    25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Todo List/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | font-family: sans-serif; 5 | box-sizing: border-box; 6 | } 7 | 8 | body { 9 | background: linear-gradient(to right, #ffe000, #799f0c); 10 | min-height: 100vh; 11 | } 12 | 13 | .container { 14 | width: 400px; 15 | background-color: #fff; 16 | margin: 100px auto; 17 | border-radius: 6px; 18 | } 19 | 20 | .form { 21 | padding: 10px 20px 10px 0; 22 | display: flex; 23 | align-items: center; 24 | justify-content: space-between; 25 | border-bottom: 1px solid #aaa; 26 | } 27 | 28 | .form input { 29 | outline: none; 30 | border: none; 31 | padding: 0 20px; 32 | font-size: 24px; 33 | color: #444; 34 | width: 100%; 35 | } 36 | 37 | .form button { 38 | outline: none; 39 | border: none; 40 | background-color: yellowgreen; 41 | padding: 5px 15px; 42 | border-radius: 6px; 43 | cursor: pointer; 44 | color: white; 45 | font-size: 20px; 46 | } 47 | 48 | .todos { 49 | padding: 0; 50 | margin: 0; 51 | list-style-type: none; 52 | } 53 | 54 | .todos li { 55 | border-top: 1px solid #e5e5e5; 56 | cursor: pointer; 57 | font-size: 22px; 58 | padding: 15px 22px; 59 | display: flex; 60 | align-items: center; 61 | justify-content: space-between; 62 | } 63 | 64 | .todos li i { 65 | color: #aaa; 66 | opacity: 0; 67 | } 68 | 69 | .todos li:hover i { 70 | opacity: 1; 71 | } 72 | 73 | .todos li span { 74 | overflow: hidden; 75 | text-overflow: ellipsis; 76 | padding-right: 10px; 77 | } 78 | 79 | .todos li.completed span { 80 | color: #b6b6b6; 81 | text-decoration: line-through; 82 | } 83 | -------------------------------------------------------------------------------- /Tooltip/Tooltip.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Tooltip/Tooltip.zip -------------------------------------------------------------------------------- /Tooltip/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tooltip 8 | 9 | 10 | 11 | 12 |
    13 |
    14 | 15 | Facebook 16 |
    17 |
    18 | 19 | Github 20 |
    21 |
    22 | 23 | Twitter 24 |
    25 |
    26 | 27 | Instagram 28 |
    29 |
    30 | 31 | Youtube 32 |
    33 |
    34 | 35 | -------------------------------------------------------------------------------- /Tooltip/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap'); 2 | 3 | :root { 4 | --facebook-color: #3b5999; 5 | --instagram-color: #e1306c; 6 | --youtube-color: #de463b; 7 | --twitter-color: #46c1f6; 8 | --github-color: #333; 9 | } 10 | 11 | * { 12 | margin: 0; 13 | padding: 0; 14 | box-sizing: border-box; 15 | font-family: 'Poppins', sans-serif; 16 | } 17 | 18 | body { 19 | height: 100vh; 20 | background-color: #dadada; 21 | display: flex; 22 | justify-content: center; 23 | align-items: center; 24 | } 25 | 26 | .wrapper { 27 | display: flex; 28 | } 29 | 30 | .icon { 31 | margin: 0 20px; 32 | cursor: pointer; 33 | position: relative; 34 | } 35 | 36 | .icon i { 37 | height: 60px; 38 | width: 60px; 39 | border-radius: 50%; 40 | background: #fff; 41 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2); 42 | font-size: 25px; 43 | display: flex; 44 | align-items: center; 45 | justify-content: center; 46 | } 47 | 48 | .icon .tooltip { 49 | position: absolute; 50 | top: 0; 51 | left: 50%; 52 | transform: translateX(-50%); 53 | color: #fff; 54 | padding: 10px 18px; 55 | font-size: 20px; 56 | font-weight: 500; 57 | border-radius: 25px; 58 | opacity: 0; 59 | pointer-events: none; 60 | box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.2); 61 | } 62 | 63 | .icon .tooltip:before { 64 | position: absolute; 65 | content: ''; 66 | height: 15px; 67 | width: 15px; 68 | left: 50%; 69 | bottom: -6px; 70 | transform: translateX(-50%) rotate(45deg); 71 | } 72 | 73 | .tooltip, 74 | .icon i { 75 | transition: 0.3s; 76 | } 77 | 78 | .icon:hover .tooltip { 79 | opacity: 1; 80 | pointer-events: auto; 81 | top: -70px; 82 | } 83 | 84 | .icon:hover i { 85 | color: white; 86 | } 87 | 88 | .facebook .tooltip:before, 89 | .facebook:hover i, 90 | .facebook .tooltip { 91 | background: var(--facebook-color); 92 | } 93 | .github .tooltip:before, 94 | .github:hover i, 95 | .github .tooltip { 96 | background: var(--github-color); 97 | } 98 | .twitter .tooltip:before, 99 | .twitter:hover i, 100 | .twitter .tooltip { 101 | background: var(--twitter-color); 102 | } 103 | .instagram .tooltip:before, 104 | .instagram:hover i, 105 | .instagram .tooltip { 106 | background: var(--instagram-color); 107 | } 108 | .youtube .tooltip:before, 109 | .youtube:hover i, 110 | .youtube .tooltip { 111 | background: var(--youtube-color); 112 | } 113 | -------------------------------------------------------------------------------- /Typing Animation Effect/Typing Animation Effect.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Typing Animation Effect/Typing Animation Effect.zip -------------------------------------------------------------------------------- /Typing Animation Effect/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Typing Animation Effect 8 | 9 | 10 | 11 |
    12 |

    Nodemy - Thực Chiến - Sáng Tạo

    13 |
    14 | 15 | 16 | -------------------------------------------------------------------------------- /Typing Animation Effect/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px; 3 | padding: 0px; 4 | height: 100vh; 5 | display: flex; 6 | background-color: #060802; 7 | } 8 | 9 | .text-typing { 10 | margin: 200px auto; 11 | font-family: monospace; 12 | } 13 | 14 | .text-typing p { 15 | color: #96ee1c; 16 | font-weight: 700; 17 | font-size: 70px; 18 | white-space: nowrap; 19 | overflow: hidden; 20 | animation: typing 10s steps(30) infinite, blink 1s infinite; 21 | text-shadow: 0 0 100px #96ee1c; 22 | } 23 | 24 | @keyframes typing { 25 | 0%, 26 | 100% { 27 | width: 0%; 28 | } 29 | 70%, 30 | 90% { 31 | width: 100%; 32 | } 33 | } 34 | 35 | @keyframes blink { 36 | 0%, 37 | 100% { 38 | border-right: 10px solid transparent; 39 | } 40 | 50% { 41 | border-right: 10px solid #96ee1c; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Vaidator Password/Vaidator Password.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Vaidator Password/Vaidator Password.zip -------------------------------------------------------------------------------- /Vaidator Password/app.js: -------------------------------------------------------------------------------- 1 | const form = document.querySelector('form') 2 | const input = document.querySelector('form input') 3 | const eye = document.querySelector('.form-eye') 4 | 5 | eye.addEventListener('click', (e) => { 6 | e.target.classList.toggle('show') 7 | 8 | input.type == 'password' ? (input.type = 'text') : (input.type = 'password') 9 | }) 10 | 11 | input.addEventListener('input', (e) => { 12 | const value = e.target.value 13 | 14 | const lowercase = document.querySelector('.lowercase').classList 15 | const uppercase = document.querySelector('.uppercase').classList 16 | const number = document.querySelector('.number').classList 17 | const symbol = document.querySelector('.symbol').classList 18 | const characters = document.querySelector('.characters').classList 19 | 20 | // validator 21 | value.search(/[a-z]/) >= 0 22 | ? lowercase.add('valid') 23 | : lowercase.remove('valid') 24 | 25 | value.search(/[A-Z]/) >= 0 26 | ? uppercase.add('valid') 27 | : uppercase.remove('valid') 28 | 29 | value.search(/[0-9]/) >= 0 30 | ? number.add('valid') 31 | : number.remove('valid') 32 | 33 | value.search(/\W/) >= 0 34 | ? symbol.add('valid') 35 | : symbol.remove('valid') 36 | 37 | value.length >= 8 38 | ? characters.add('valid') 39 | : characters.remove('valid') 40 | }) 41 | -------------------------------------------------------------------------------- /Vaidator Password/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Validator Password 8 | 9 | 10 | 11 | 12 | 13 |
    14 |
    15 |
    16 | 17 | 18 |
    19 | 20 |
    21 |
    22 |

    Lower-case

    23 |

    Upper-case

    24 |

    Number

    25 |

    At least one special symbol

    26 |

    At least 8 characters

    27 |
    28 |
    29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Vaidator Password/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | font-family: sans-serif; 5 | box-sizing: border-box; 6 | } 7 | 8 | input, 9 | button { 10 | border: none; 11 | outline: none; 12 | background-color: transparent; 13 | } 14 | 15 | body { 16 | height: 100vh; 17 | background-image: linear-gradient(145deg, #569377 50%, #dbede5 50%); 18 | overflow: hidden; 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | } 23 | 24 | form { 25 | display: flex; 26 | align-items: stretch; 27 | margin-bottom: 20px; 28 | } 29 | 30 | .form-input { 31 | background-color: #fff; 32 | padding: 10px; 33 | border-radius: 10px; 34 | } 35 | 36 | .form-input input { 37 | padding: 0px 10px; 38 | font-size: 20px; 39 | } 40 | 41 | .form-eye::before { 42 | font-family: 'Font Awesome 6 Free'; 43 | content: '\f070'; 44 | font-weight: 900; 45 | width: 20px; 46 | display: inline-block; 47 | cursor: pointer; 48 | } 49 | 50 | .form-eye.show::before { 51 | content: '\f06e'; 52 | } 53 | 54 | .submit { 55 | margin-left: 10px; 56 | border-radius: 10px; 57 | color: #fff; 58 | background-color: #48735f; 59 | padding: 10px 20px; 60 | font-weight: 600; 61 | font-size: 16px; 62 | cursor: pointer; 63 | } 64 | 65 | .validator { 66 | background-color: #48735f; 67 | border-radius: 10px; 68 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); 69 | } 70 | 71 | .validator p { 72 | color: white; 73 | padding: 10px 20px; 74 | } 75 | 76 | .validator p::before { 77 | font-family: 'Font Awesome 6 Free'; 78 | content: '\f00d'; 79 | font-weight: 900; 80 | width: 30px; 81 | display: inline-block; 82 | color: white; 83 | font-size: 20px; 84 | } 85 | 86 | .validator p.valid { 87 | color: rgba(255,255,255, 0.5); 88 | } 89 | 90 | .validator p.valid::before { 91 | content: '\f00c'; 92 | } 93 | -------------------------------------------------------------------------------- /Weather App/Weather App.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Weather App/Weather App.zip -------------------------------------------------------------------------------- /Weather App/app.js: -------------------------------------------------------------------------------- 1 | var search = document.querySelector('.search') 2 | var city = document.querySelector('.city') 3 | var country = document.querySelector('.country') 4 | var value = document.querySelector('.value') 5 | var shortDesc = document.querySelector('.short-desc') 6 | var visibility = document.querySelector('.visibility span') 7 | var wind = document.querySelector('.wind span') 8 | var sun = document.querySelector('.sun span') 9 | var value = document.querySelector('.value') 10 | var time = document.querySelector('.time') 11 | var content = document.querySelector('.content') 12 | var body = document.querySelector('body') 13 | 14 | 15 | async function changeWeatherUI(capitalSearch){ 16 | let apiURL = `https://api.openweathermap.org/data/2.5/weather?q=${capitalSearch}&appid=cb8d857e9528680474d1a7a90b239fba 17 | ` 18 | 19 | let data = await fetch(apiURL).then(res=> res.json()) 20 | if(data.cod == 200){ 21 | content.classList.remove('hide') 22 | city.innerText = data.name 23 | country.innerText = data.sys.country 24 | visibility.innerText = data.visibility + 'm' 25 | wind.innerText = data.wind.speed + 'm/s' 26 | sun.innerText = data.main.humidity + '%' 27 | let temp = Math.round((data.main.temp - 273.15 )) 28 | value.innerText = temp 29 | shortDesc.innerText = data.weather[0] ? data.weather[0].main : '' 30 | time.innerText = new Date().toLocaleString('vi') 31 | 32 | 33 | body.setAttribute('class', 'hot') 34 | if(temp <= 25){ 35 | body.setAttribute('class', 'cool') 36 | } 37 | 38 | if(temp <= 22){ 39 | body.setAttribute('class', 'warm') 40 | } 41 | 42 | if(temp <= 19){ 43 | body.setAttribute('class', 'cold') 44 | } 45 | 46 | 47 | }else{ 48 | content.classList.add('hide') 49 | } 50 | } 51 | 52 | search.addEventListener('keypress', function(e){ 53 | if(e.code === 'Enter'){ 54 | let capitalSearch = search.value.trim() 55 | changeWeatherUI(capitalSearch) 56 | } 57 | }) 58 | 59 | changeWeatherUI('Ha Noi') 60 | -------------------------------------------------------------------------------- /Weather App/cold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Weather App/cold.png -------------------------------------------------------------------------------- /Weather App/cool.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Weather App/cool.jpeg -------------------------------------------------------------------------------- /Weather App/hot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Weather App/hot.png -------------------------------------------------------------------------------- /Weather App/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Weather App 8 | 9 | 16 | 17 | 18 | 19 |
    20 | 21 |
    22 |

    23 | Ha Noi 24 | , 25 | VN 26 |

    27 | 28 |
    12/24/2021, 3:25:16 PM
    29 |
    30 | 23 oC 31 |
    32 | 33 |
    Clouds
    34 |
    35 |
    36 | 37 | 1000m 38 |
    39 |
    40 | 41 | 1000m 42 |
    43 |
    44 | 45 | 1000m 46 |
    47 |
    48 |
    49 |
    50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Weather App/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body{ 8 | font-family: sans-serif; 9 | height: 100vh; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | background: linear-gradient(to top, rgba(0,0,0,0.9), rgba(0,0,0,0.7)), url(cold.png) no-repeat center/cover; 14 | } 15 | 16 | #weather{ 17 | width: 350px; 18 | border-radius: 10px; 19 | padding: 30px 20px; 20 | background: url(cold.png) no-repeat center/cover; 21 | } 22 | 23 | .hot{ 24 | background: linear-gradient(to top, rgba(0,0,0,0.9), rgba(0,0,0,0.7)), url(hot.png) no-repeat center/cover; 25 | } 26 | 27 | .hot #weather{ 28 | background: url(hot.png) no-repeat center/cover; 29 | } 30 | 31 | .cold{ 32 | background: linear-gradient(to top, rgba(0,0,0,0.9), rgba(0,0,0,0.7)), url(cold.png) no-repeat center/cover; 33 | } 34 | 35 | .cold #weather{ 36 | background: url(cold.png) no-repeat center/cover; 37 | } 38 | 39 | .cool{ 40 | background: linear-gradient(to top, rgba(0,0,0,0.9), rgba(0,0,0,0.7)), url(cool.jpeg) no-repeat center/cover; 41 | } 42 | 43 | .cool #weather{ 44 | background: url(cool.jpeg) no-repeat center/cover; 45 | } 46 | 47 | .warm{ 48 | background: linear-gradient(to top, rgba(0,0,0,0.9), rgba(0,0,0,0.7)), url(warm.jpeg) no-repeat center/cover; 49 | } 50 | 51 | .warm #weather{ 52 | background: url(warm.jpeg) no-repeat center/cover; 53 | } 54 | 55 | .search{ 56 | width: 100%; 57 | padding: 10px 20px; 58 | border: none; 59 | outline: none; 60 | background: rgba(255,255,255, 0.3); 61 | border-radius: 0 15px 0 15px; 62 | box-shadow: 0 5px 4px rgba(0,0,0,0.2); 63 | transition: 0.4s; 64 | font-size: 19px; 65 | } 66 | 67 | .search:focus{ 68 | background: rgba(255,255,255, 0.7); 69 | border-radius: 15px 0 15px 0; 70 | } 71 | 72 | .content{ 73 | text-align: center; 74 | color: white; 75 | margin: 40px 0; 76 | transition: 0.4s; 77 | } 78 | 79 | .capital{ 80 | text-shadow: 2px 2px rgba(0,0,0,0.5) 81 | } 82 | 83 | .time{ 84 | margin: 15px 0 20px 0; 85 | font-size: 15px; 86 | } 87 | 88 | .temperature{ 89 | font-size: 65px; 90 | text-shadow: 4px 4px rgba(0,0,0,0.7); 91 | background: rgba(255,255,255, 0.4); 92 | box-shadow: 4px 4px rgba(0,0,0,0.5); 93 | border: none; 94 | border-radius: 10px; 95 | margin: 0 30px; 96 | padding: 20px; 97 | } 98 | 99 | .short-desc{ 100 | font-size: 40px; 101 | font-weight: bold; 102 | text-shadow: 2px 2px rgba(0,0,0,0.5);; 103 | margin: 30px; 104 | } 105 | 106 | .more-desc{ 107 | display: flex; 108 | justify-content: space-between; 109 | } 110 | 111 | .more-desc div{ 112 | display: flex; 113 | flex-direction: column; 114 | padding: 5px; 115 | } 116 | 117 | .more-desc div i { 118 | margin: 10px 0; 119 | } 120 | 121 | .hide{ 122 | visibility: hiddend; 123 | opacity: 0; 124 | } 125 | -------------------------------------------------------------------------------- /Weather App/warm.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Weather App/warm.jpeg -------------------------------------------------------------------------------- /Zoom Image/Zoom Image.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Zoom Image/Zoom Image.zip -------------------------------------------------------------------------------- /Zoom Image/app.js: -------------------------------------------------------------------------------- 1 | const zoomer = document.querySelector('.zoomer') 2 | const wrapImg = document.querySelectorAll('.zoomer .image') 3 | const result = document.querySelector('.zoomer .result') 4 | 5 | const size = 4 6 | 7 | wrapImg.forEach((item) => { 8 | item.addEventListener('mousemove', function (e) { 9 | result.classList.remove('hide') 10 | 11 | const img = item.querySelector('img') 12 | 13 | let x = (e.offsetX / this.offsetWidth) * 100 14 | let y = (e.offsetY / this.offsetHeight) * 100 15 | 16 | // move result 17 | let posX = e.pageX - zoomer.offsetLeft 18 | let posY = e.pageY - zoomer.offsetTop 19 | 20 | result.style.cssText = ` 21 | background-image: url(${img.src}); 22 | background-size: ${img.width * size}px ${img.height * size}px; 23 | background-position : ${x}% ${y}%; 24 | left: ${posX}px; 25 | top: ${posY}px; 26 | ` 27 | }) 28 | 29 | item.addEventListener('mouseleave', function (e) { 30 | result.style = `` 31 | result.classList.add('hide') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /Zoom Image/bike1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Zoom Image/bike1.jpg -------------------------------------------------------------------------------- /Zoom Image/bike2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/namndwebdev/html-css-js-thuc-chien/9d0b782a324fa5801e4c39dd73ce31cca3f0d27b/Zoom Image/bike2.jpg -------------------------------------------------------------------------------- /Zoom Image/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zoom Image 8 | 9 | 10 | 11 |
    12 |
    13 | 14 |
    15 |
    16 | 17 |
    18 |
    19 |
    20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Zoom Image/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | background-color: #dadada; 9 | height: 100vh; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | overflow: hidden; 14 | } 15 | 16 | .zoomer { 17 | position: relative; 18 | display: flex; 19 | } 20 | 21 | img { 22 | width: 100%; 23 | height: 100%; 24 | object-fit: cover; 25 | object-position: center; 26 | } 27 | 28 | .zoomer .image { 29 | width: 500px; 30 | margin:40px; 31 | box-shadow: 0 0 10px rgba(0,0,0,0.6); 32 | cursor: none; 33 | } 34 | 35 | .result { 36 | position: absolute; 37 | width: 250px; 38 | height: 250px; 39 | box-shadow: 0 0 10px rgba(0,0,0,0.6); 40 | border-radius: 50%; 41 | overflow: hidden; 42 | transform: translate(-50%, -50%); 43 | pointer-events: none; 44 | } 45 | 46 | .hide { 47 | display: none; 48 | } --------------------------------------------------------------------------------