projects from youtube videos that I found interesting or useful. More to come

This commit is contained in:
Robert McGovern 2022-03-22 11:45:01 +00:00
parent 5f0e68b80b
commit 4be741a09b
7 changed files with 279 additions and 0 deletions

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Typewriter Animation Effect with HTML, CSS, and JavaScript</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<main>
<div class="container">
<p data-typewriter="Developer...,Designer...,Photographer... ">
</p>
<p data-typewriter="Coffee Roaster...,Pizza Maker...,Basket Weaver..."></p>
</div>
</main>
</body>
</html>

View File

@ -0,0 +1,4 @@
Just some quick notes. This project comes from the video [Typewriter Animation Effect with HTML, CSS, and JavaScript](https://www.youtube.com/watch?v=7uDdiMCVwJk). It appealed because it was showing the sort of typing effect without using any frameworks, sdks or anything else.

View File

@ -0,0 +1,67 @@
class Typewriter {
constructor(el, options) {
this.el = el;
this.words = [...this.el.dataset.typewriter.split(",")];
this.speed = options?.speed || 100;
this.delay = options?.delay || 1000;
this.repeat = options?.repeat || false;
this.initTyping();
}
wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
toggleTyping = () => this.el.classList.toggle("typing");
async typewrite(word) {
await this.wait(this.delay);
this.toggleTyping();
for (const letter of word.split("")) {
this.el.textContent += letter;
await this.wait(this.speed);
}
this.toggleTyping();
await this.wait(this.delay);
this.toggleTyping();
while (this.el.textContent.length !== 0) {
this.el.textContent = this.el.textContent.slice(0, -1);
await this.wait(this.speed);
}
this.toggleTyping();
}
async initTyping() {
for (const word of this.words) {
await this.typewrite(word);
}
if (this.repeat) {
await this.initTyping();
} else {
this.el.style.animation = "none";
}
// can't use forEach with await
// this.words.forEach((word) => {
// await this.typewrite(word);
// });
}
}
// const el1 = new Typewriter(document.querySelector("[data-typewriter]"), {
// speed: 10,
// delay: 10,
// repeat: true,
// });
// console.log(el1);
document.querySelectorAll("[data-typewriter]").forEach((el) => {
new Typewriter(el, {
repeat: true,
});
});

View File

@ -0,0 +1,44 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
main {
display: grid;
align-items: center;
padding: 4rem;
min-height: 100vh;
background: linear-gradient(#eff0f3, #efe0ef);
}
.container {
display: grid;
justify-items: start;
gap: 2rem;
}
[data-typewriter] {
font-family: system-UI;
font-weight: bold;
font-size: 4.5rem;
color: #d9376e;
height: 6rem;
border-right: 0.8rem solid transparent;
padding: 0.6rem;
}
[data-typewriter]:not(.typing) {
animation: blink-cursor 1.1s step-end infinite;
}
@keyframes blink-cursor {
0%,
100% {
border-color: transparent;
}
50% {
border-color: #ff8e3c;
}
}

View File

@ -0,0 +1,35 @@
.search-wrapper {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
input {
font-size: 1rem;
}
.user-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 0.25rem;
margin-top: 1rem;
}
.card {
border: 1px solid black;
background-color: whitesmoke;
padding: 0.5rem;
}
.card > .name {
margin-bottom: 0.25rem;
}
.card > .email {
font-size: 0.8rem;
columns: #777;
}
.hide {
display: none;
}

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/style.css">
<script src="js/script.js" defer></script>
<title>Using Template with Search</title>
</head>
<body>
<header>
</header>
<main>
<!-- * Section for search -->
<!-- note that really this is filtering, not searching -->
<div class="search-wrapper">
<label for="search">Search (filter) Users</label>
<input type="search" id="search" data-search>
</div>
<!-- * Section for users -->
<div class="user-cards" data-user-cards-container>
</div>
</main>
<template data-user-template>
<div class="card">
<div class="name" data-name>My Name</div>
<div class="email" data-email>My email address</div>
</div>
</template>
</body>
</html>

View File

@ -0,0 +1,63 @@
// * https://jsonplaceholder.typicode.com/users
const userCardTemplate = document.querySelector("[data-user-template]");
// console.log(userCardTemplate);
const userCardsContainer = document.querySelector(
"[data-user-cards-container]"
);
// console.log(userCardsContainer);
// Original code from video used the attribute for the query rather
// than the ID that he had setup. So I swapped it over.
// const searchInput = document.querySelector("[data-search]");
const searchInput = document.querySelector("#search");
// console.log(searchInput);
let users = []; // filed from user data retrieved from server
searchInput.addEventListener("input", (event) => {
const value = event.target.value.toLowerCase();
// console.log(users);
users.forEach((user) => {
// this seems wasteful to run toLowerCase each time you type a letter
// surely better to just store a lowercase version when saving original data
// const isVisible =
// user.name.toLowerCase().includes(value) ||
// user.email.toLowerCase().includes(value);
const isVisible =
user.lowercaseName.includes(value) ||
user.lowercaseEmail.includes(value);
user.element.classList.toggle("hide", !isVisible);
});
});
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => {
users = data.map((user) => {
const card = userCardTemplate.content.cloneNode(true).children[0];
const name = card.querySelector("[data-name]");
const email = card.querySelector("[data-email]");
name.textContent = user.name;
email.textContent = user.email;
// console.log(card);
userCardsContainer.append(card);
// Below is original return
// return { name: user.name, email: user.email, element: card };
return {
name: user.name,
email: user.email,
lowercaseName: user.name.toLowerCase(),
lowercaseEmail: user.email.toLowerCase(),
element: card,
};
});
});