๐ŸŽจ Scroll-blog

kirin.logยท2021๋…„ 3์›” 14์ผ
0
post-custom-banner

๐Ÿช logic

  • html ๋‚ด input์„ ์ƒ์„ฑ, ๋ธ”๋กœ๊ทธ ๋ฆฌ์ŠคํŠธ๋Š” js๋กœ ๊ตฌ์„ฑ ์˜ˆ์ •(๊ฐ์‹ธ๋Š” div๋งŒ ์ƒ์„ฑํ•ด์ฃผ๊ธฐ)
  • fetchํ•จ์ˆ˜๋กœ item์„ ๋ฐ›์•„์˜จ๋‹ค(getPost()).
  • ๋ฐ›์•„์˜จ item๋“ค์„ ๊ฐ๊ฐ htmlํƒœ๊ทธ๋กœ ๊ตฌ์„ฑํ•ด์„œ(showPost()) innerHTMLํ•ด์ค€๋‹ค.
  • fetch์˜ limit์€ 5๋กœ ์ œํ•œ(5๊ฐœ๊นŒ์ง€๋งŒ ํ™”๋ฉด์— ๋ณด์ด๊ธฐ)ํ•˜๊ณ , page๋Š” 1๋กœ ๊ทœ์ •(1ํŽ˜์ด์ง€๋ถ€ํ„ฐ)ํ•œ๋‹ค.
  • loading dot์ด ํŽ˜์ด์ง€๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด 3์ดˆ ํ›„์— ๋ณด์ด๋„๋ก css ์ ์šฉํ•œ ํ•จ์ˆ˜ ์ƒ์„ฑ
๐Ÿš€ html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <link rel="stylesheet" href="index.css" />
        <title>My Blog</title>
    </head>
    <body>
        <h1>My Blog</h1>
        <div class="filter-container">
            <input type="text" id="filter" class="filter" placeholder="Filter posts..." />
        </div>

        <div id="posts-container">
            <!--js๋กœ ์ƒ์„ฑํ•œ contents ๋“ค์–ด๊ฐˆ ๋ถ€๋ถ„-->
        </div>

        <div class="loader">
            <div class="circle"></div>
            <div class="circle"></div>
            <div class="circle"></div>
        </div>
        <script src="index.js"></script>
    </body>
</html>
๐Ÿš€ css
@import url("https://fonts.googleapis.com/css?family=Roboto&display=swap");

* {
    box-sizing: border-box;
}

body {
    background-color: #296ca8;
    color: #fff;
    font-family: "Roboto", sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    margin: 0;
    padding-bottom: 100px;
}

h1 {
    margin-bottom: 0;
    text-align: center;
}

.filter-container {
    margin-top: 20px;
    width: 80vw;
    max-width: 800px;
}

.filter {
    width: 100%;
    padding: 12px;
    font-size: 16px;
}

/* js */
.post {
    position: relative;
    background-color: #4992d3;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
    border-radius: 3px;
    padding: 20px;
    margin: 40px 0;
    display: flex;
    width: 80vw;
    max-width: 800px;
}

.post .post-title {
    margin: 0;
}

.post .post-body {
    margin: 15px 0 0;
    line-height: 1.3;
}

.post .post-info {
    margin-left: 20px;
}

.post .number {
    position: absolute;
    top: -15px;
    left: -15px;
    font-size: 15px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #fff;
    color: #296ca8;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 7px 10px;
}

/* loading */
.loader {
    opacity: 0;
    display: flex;
    position: fixed;
    bottom: 50px; /*position์ด fixed์ผ๋•Œ, ๊ธฐ๋ณธ์ ์œผ๋กœ ์ตœ์ƒ๋‹จ ์ตœ์ขŒ์ธก์— ์œ„์น˜. bottom์˜ ๊ฐ’์„ ์ฃผ๋ฉด ํ•ด๋‹น ๊ฐ’๋งŒํผ bottom์—์„œ ์œ„์น˜ํ•˜๊ฒŒ ๋จ */
    transition: opacity 0.3s ease-in;
}

.loader.show {
    opacity: 1;
}

.circle {
    background-color: #fff;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    margin: 5px;
    animation: bounce 0.5s ease-in infinite;
    /* animation ์ด๋ฆ„, duration(์ง€์†๊ธฐ๊ฐ„), ์†๋„(ease-in : ์ฒ˜์Œ์„ ๋Š๋ฆฌ๊ฒŒ), infinite(๋ฌดํ•œ๋ฐ˜๋ณต) */
}

.circle:nth-of-type(2) {
    animation-delay: 0.1s;
}

.circle:nth-of-type(3) {
    animation-delay: 0.2s;
}

@keyframes bounce {
    0%,
    100% {
        transform: translateY(0);
    }
    50% {
        transform: translateY(-10px);
    }
}
๐Ÿš€ js
"use strict";

const postsContainer = document.getElementById("posts-container");
const loading = document.querySelector(".loader");
const filter = document.getElementById("filter"); // input

let limit = 5;
let page = 1;

// Fetch posts from API
async function getPosts() {
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts?_limit=${limit}&_page=${page}`);

    const data = await res.json();

    return data;
}

// Show posts in DOM
async function showPosts() {
    const posts = await getPosts();

    // forEach : ์ฃผ์–ด์ง„ ํ•จ์ˆ˜๋ฅผ ๋ฐฐ์šœ ์š”์†Œ ๊ฐ๊ฐ์— ๋Œ€ํ•ด ์‹คํ–‰
    posts.forEach((post) => {
        const postEl = document.createElement("div");
        postEl.classList.add("post");
        postEl.innerHTML = `
     		 <div class="number">${post.id}</div>
     		 <div class="post-info">
      		    <h2 class="post-title">${post.title}</h2>
      		    <p class="post-body">${post.body}</p>
    		 </div>
    `;

        postsContainer.appendChild(postEl);
    });
}

// Show loader & fetch more posts
function showLoading() {
    loading.classList.add("show");

    setTimeout(() => {
        loading.classList.remove("show");

        setTimeout(() => {
            page++;
            showPosts();
        }, 300);
    }, 1000);
}

// Filter posts by input
function filterPosts(e) {
    const term = e.target.value.toUpperCase();
    const posts = document.querySelectorAll(".post");

    posts.forEach((post) => {
        const title = post.querySelector(".post-title").innerText.toUpperCase();
        const body = post.querySelector(".post-body").innerText.toUpperCase();

        if (title.indexOf(term) > -1 || body.indexOf(term) > -1) {
            post.style.display = "flex";
        } else {
            post.style.display = "none";
        }
    });
}

// Show initial posts
showPosts();

window.addEventListener("scroll", () => {
    const { scrollTop, scrollHeight, clientHeight } = document.documentElement;

    if (scrollTop + clientHeight >= scrollHeight - 5) {
        showLoading();
    }
});

filter.addEventListener("input", filterPosts);
profile
boma91@gmail.com
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€