TIL 23 - [clone] Instagram - js

hojung choi·2021년 7월 16일
1

js

목록 보기
16/17

인스타그램 클론코딩! js로 진행하였다. 필수구현 , 추가구현 , 리팩토링까지 완료하며 느꼇던 점, 어려웠던 부분을 정리하려 한다🔥


📖 Login 페이지

👉🏻 id > '@' 포함 / pw > 5글자 이상 시 로그인 버튼 활성화

<div id="login">
  <div class="loginWrap">
     <h1 class="sitelogo">Westargram</h1>
     <form action="main.html">
       <input type="text" class="input idInput" name="id" placeholder="전화번호, 사용자 이름 또는 이메일" required>
       <input type="password" class="input pwdInput" name="password" placeholder="비밀번호" required>
       <button>로그인</button>
     </form>
     <a href="#" class="forgetIink">비밀번호를 잊으셨나요?</a>
  </div>
</div>
const form = document.querySelector("#login form");
const id = document.querySelector("#login form .idInput");
const pwd = document.querySelector("#login form .pwdInput");
const button = document.querySelector("#login button");

form.addEventListener("keyup", function activeButton(){
    ((id.value.length >= 1 ) && (pwd.value.length >= 5) && (id.value.indexOf("@") > -1)) ? button.style.opacity = "1" : button.style.opacity = "0.5";
});
  • 필수 구현 사항 : input값이 들어갈때 버튼 활성화
  • 추가 구현 사항 : id > '@' 포함 / pw > 5글자 이상 시 로그인 버튼 활성화

input과 button을 form으로 묶어 form에 addEventListener을 주었다. 또한 input의 속성에 required를 추가하여, input에 값이 있을때만 submit이 되도록 하였다.
event는 keyup을 주어 키보드를 누르고 떼는 순간 이벤트가 발생하도록 했다.
그리고 이번에 처음 써봤던 삼항 연산자! 처음에는 너무 낯설었는데 모든 if문을 삼항연산자로 구현하니 이제는 삼항 연산자가 더 편해졌다!




📖 main 페이지

👉🏻 댓글 내용 입력 후 Enter press, 혹은 게시 버튼 클릭 시 댓글 추가 기능 / input입력 시 게시 버튼 활성화

<div class="feeds">
    <article>
        <div class="feed">
            <div class="writer">
                <a href="#" class="writerInfo">
                    <div class="writerImg">
                        <img alt="프로필 이미지" src="images/profile_img.jpeg">
                    </div>
                    <div class="writerId">canon_mj</div>
                </a>
                <a href="#" class="writerOtherMenu">
                    <img alt="게시글에 대한 다른메뉴 보기" src="images/feed_writer_other_menu.png">
                </a>
            </div>
            <div class="imgs">
                <img alt="피드 이미지" src="images/feed_img.png">
            </div>
            <div class="feedInfoWrap">
                <div class="feedLink">
                    <div class="feedLinkLeft">
                        <a href="#"><img alt="좋아요 아이콘" src="images/feed_link_left1.png"></a>
                        <a href="#"><img alt="댓글 아이콘" src="images/feed_link_left2.png"></a>
                        <a href="#"><img alt="쪽지 아이콘" src="images/feed_link_left3.png"></a>
                    </div>
                    <div class="feedLinkRight">
                        <a href="#"><img alt="책갈피 아이콘" src="images/feed_link_right1.png"></a>
                    </div>
                </div>
                <div class="likeCount">
                    <div class="likeImg">
                        <img alt="좋아요 누른 사람의 프로필사진" src="images/profile_img.jpeg">
                    </div>
                    <div class="likeInfo">
                        <a href="#" class="likeUser">anieworld</a><a href="#" class="likeUserOther">외 10명</a>이 좋아합니다
                    </div>
                </div>
                <div class="feedDecs">
                    <a href="#" class="feedWriter">canon_mj</a>
                    <div class="feedTxt">위워크에서 진행한 베이킹 클래스... <a href="#">더 보기</a></div>
                </div>
                <div class="feedComment">
                    <ul></ul>
                    <div class="feedTime">42분전</div>
                </div>
            </div>
            <div class="feedCommentInput">
                <form>
                    <input type="text" class="input" name="comment" placeholder="댓글달기...">
                    <button class="commentBtn">게시</button>
                </form>
            </div>
        </div>
    </article>
</div>
const commentSubmit = () => {
    const commentList = document.querySelector(".feedComment ul");
    const commentText = commentInput.value;
    commentInput.value = "";
    const li = document.createElement("li");
    commentList.appendChild(li);
    const a = document.createElement("a");
    li.appendChild(a);
    a.innerText = "user_id";
    const p = document.createElement("p");
    li.appendChild(p);
    p.innerText = commentText;
    const buttonHeart = document.createElement("button");
    buttonHeart.classList.add("heartBtn");
    buttonHeart.innerText = "좋아요하트";
    li.appendChild(buttonHeart);
    buttonHeart.addEventListener("click", likeHeart);
    const deleteButton = document.createElement("button");
    deleteButton.classList.add("deleteBtn");
    deleteButton.innerText = "삭제버튼";
    li.appendChild(deleteButton);
    deleteButton.addEventListener("click", deleteComment);
}
commentForm.addEventListener("submit", handleCommentSubmit);
commentForm.addEventListener("keyup", function activeCommentButton () {commentInput.value !== "" ? commentButton.style.opacity = "1" : commentButton.style.opacity = "0.3"});

핵심 포인트

  • createElement, appendChild, innerText 구분 및 순서 확인 잘 하기!

📌 댓글 등록
1. .feedComment ul에 li값을 추가해주기 위해, .feedComment ul을 commentList로 선언한다.
2. input값을 commentText값에 넣어주고 input값을 초기화 시킨다.
3. createElement을 통해 li을 만들어준다 (a,p,button 도 마찬가지로)
4. commentText값을 p안에 innerText을 통해 넣어준다 (a,p,button 도 마찬가지로 넣고 싶은 값을 넣어준다)
5. 만든 태그들을 appendChild 을 통해 html에 뿌려준다 (li는 ul안에, a / p / button은 li안에 넣어준다)

📌 input입력 시 게시글 버튼 활성화
keyup을 통해 키를 누르고 뗄 때 이벤트가 발생되도록 구현하였다.
input값이 비어있지 않을때는 opacity값을 1로, 반대일 경우에는 0.3으로 값을 주었다.


👉🏻 댓글 좋아요 / 삭제 기능

➡️ 추가 코드

const likeHeart = (e) => {
    e.preventDefault();
    const li = e.target.parentElement;
    e.target.classList.contains("active") ? e.target.classList.remove("active") : e.target.classList.add("active");
}

const deleteComment = (e) => {
    e.preventDefault();
    const li = e.target.parentElement;
    li.remove();
}
main .feeds .feed .feedComment ul li button {
    border: 0;
    text-indent: -999em;
    display: block;
    width: 20px;
    height: 20px;
    position: absolute;
    top: 50%;
    margin-top: -10px;
    background-color: transparent;
    background-size: 100% 100%;
}

#main .feeds .feed .feedComment ul li button.heartBtn {
    right: 30px;
    background-image: url("../images/feed_link_left1.png");
}

#main .feeds .feed .feedComment ul li button.heartBtn.active {
    background-image: url("../images/feed_link_left1_active.png");
}

#main .feeds .feed .feedComment ul li button.deleteBtn {
    right: 0;
    background-image: url("../images/close.png");
}

위에서 댓글 등록할 때 만들어 놨던 button(heartBtn,deleteBtn)을 css로 꾸며주고, 이벤트를 걸어주었다! 🔥

  • e.preventDefault(); : 이벤트 기본 속성으로 버튼을 누르게 되면 새로고침이 된다. 이를 막기위해 e(event)에 preventDefault함수를 걸어주는 것이다. preventDefault 함수를 주게 되면 새로고침 되는 현상을 강제로 막게 된다.
  • e.target.parentElement : e에 클릭한 button를 찾아 준 뒤(e.target), parentElement를 통해 button의 부모를 찾아준다.
  • e.target.classList.contains("active") : e에 클릭한 button의 class에 acitve가 있는지 없는지 확인한다. (acitve가 있으면 background-image값이 바뀐다)
  • li.remove(); : e.target을 통해 알아낸 li를 지운다.

👉🏻 nav 프로필 사진 클릭 시 메뉴 박스 생성 (메뉴 박스 바깥 영역 클릭시 닫히도록)

const openMypage = (e) => {
    mypagePop.style.display = "block";
}

const closePop = (e) => {
    (!mypageBox.contains(e.target)) && (!searchZone.contains(e.target)) ? (mypagePop.style.display = "none") && (searchPop.style.display = "none") : false;
}

시간을 은근 많이 잡아 먹었던 부분 😱
contains(e.target) 을 찾는데에 시간이 오래 걸렸었다..
만들어 놓고 보니 검색기능 팝업과 겹쳐 함수를 묶어 주었다!
e.target은 이벤트가 실행 된 영역이라고 앞에서 설명했다.
e.taeget이 포함되지 않은 영역을 선택 했을 때(contains), style.display = "none"을 실행시켜준다.


👉🏻 반응형 구현

@media (max-width: 1024px) {
    .sideMenu {display:none;}
    #main .feeds {width:100%;}
}

브라우져가 1024px에 도달하면 오른쪽 메뉴가 없어지도록 반응형을 구현하였다.


👉🏻 검색기능 구현

const idOfWestagram = [
    {id: "W0nhong__", imgUrl: "images/profile_img.jpeg", info: "W0nhong소개글", profileUrl: "https://www.naver.com/"},
    {id: "hj._.__s2", imgUrl: "images/profile_img.jpeg", info: "hj._.__s2소개글", profileUrl: "https://www.daum.net/"},
    {id: "kvwowv", imgUrl: "images/profile_img.jpeg", info: "kvwowv소개글", profileUrl: "https://wecode.co.kr/?gclid=CjwKCAjwlrqHBhByEiwAnLmYUD8aCer8FbdtZ8BzOFlwHX8HI-HwDbdHb4PW6-YW1NDDsDwMYGlsZRoCurgQAvD_BwE"},
    {id: "kvvow22", imgUrl: "images/profile_img.jpeg", info: "kvvow22소개글", profileUrl: "#"},
    {id: "abcd_efg", imgUrl: "images/profile_img.jpeg", info: "abcd_efg소개글", profileUrl: "#"},
    {id: "abcd_efaaaa__", imgUrl: "images/profile_img.jpeg", info: "abcd_efaaaa__소개글", profileUrl: "#"},
    {id: "ww0_jfd", imgUrl: "images/profile_img.jpeg", info: "ww0_jfd소개글", profileUrl: "#"}
];

const searchDataPaint = () => {
    idOfWestagram.forEach((el)=>{
        const li = document.createElement("li");
        const a = document.createElement("a");
        a.href = el.profileUrl;
        a.target= "_blank";
        const searchImg = document.createElement("div");
        searchImg.classList.add("searchImg");
        const img = document.createElement("img");
        img.src = el.imgUrl;
        const searchInfo = document.createElement("div");
        searchInfo.classList.add("searchInfo");
        const userId = document.createElement("div");
        userId.classList.add("userId");
        userId.innerText = el.id;
        const userInfo = document.createElement("div");
        userInfo.classList.add("userInfo");
        userInfo.innerText = el.info;
        searchUl.appendChild(li);
        li.appendChild(a);
        a.appendChild(searchImg);
        searchImg.appendChild(img);
        a.appendChild(searchInfo);
        searchInfo.appendChild(userId);
        searchInfo.appendChild(userInfo);
    });
}

const searchFilter = (e) => {
    const searchText = searchInput.value.toUpperCase(); 
    const li = searchUl.getElementsByTagName("li");
    Array.from(li).forEach((el) => {
        const userName = el.getElementsByTagName("div")[1].getElementsByTagName("div")[0];
        const txtValue = userName.innerText;
        txtValue.toUpperCase().indexOf(searchText) > -1 ? el.style.display = "block" : el.style.display = "none";
    });
}

searchForm.addEventListener("keyup", searchFilter);
window.addEventListener("load", searchDataPaint);
    1. idOfWestagram 배열에 가상 데이터를 만든 후 window가 로드되면 searchDataPaint를 실행시켜 li를 생성한다.
    1. input데이터를 모두 대문자로 바꿔준뒤 ul안에 있는 모든 li를 - getElementsByTagName를 통해 배열로 바꾼다.
    1. getElementsByTagName를 통해 만든 배열은 유사배열이므로 forEach,map같은 메소드를 실행하면 Type Error가 뜬다!
    1. Array.from(li)을 통해 li를 찐! 배열로 만들어준다.
    1. li안에 있는 2번째 div안에 있는 1번째 div(userName)를 변수로 선언한다.
    1. userNamed의 텍스트를 가져와 대문자로 바꿔준 뒤, 찾을 텍스트(검색할 텍스트)를 indexOf로 조건문을 만든다
    1. 찾을 텍스트가 있으면 (num > -1) display:block이고, 찾을 텍스트가 없으면 (num = -1) display:none;이 된다!



📌 TMI

아쉬운 부분 🙇🏻‍♀️

원래 검색기능을
첫 번째 글자 입력 시, 첫 번째 글자가 일치하는 아이디 출력 -> 두 번째 글자 입력 시, 두 번째 글자까지 일치하는 아이디 출력
이렇게 구현을 하고 싶었다....... 입력하는 값이 id에 포함되는 값을 표출하는 것이 생각보다 오래 걸리지 않아서 오! 할 수 있겠다! 했는데 ㅎㅎ...
생각 할 것이 너무나도 많았다...
도현님께 얻은 키워드는 빈 배열을 선언 후 조건문을 걸어 다시 그 배열을 뿌려주는 것이다! 그치만... 그렇게 구현하지 못했다 😱
마지막 날 컨디션도 너무 좋지 못했고 온전히 코딩에만 집중하지 못했던 탓도 있는것 같다ㅠㅠ


느낀점 🔥

리팩토링을 하기 전 내 코드를 다시한번 더욱 더!!! 꼼꼼히!!!! 제발 확인하자,,
왜 늘 커밋을 하고 난 다음에 수정할 코드가 보이는 것일까 😂
그리고 이번 프로젝트를 진행하면서 너무 신기했던건 2시간 동안 붙잡고 있었던 코드를 도현님께 여쭤봤는데 도현님께 내 코드를 설명하면서 내가 그 코드를 완전히 이해해버려 답까지 혼자 찾아냈다....ㅎㅎ 왜 혼자 할 떄는 그게 안될까 🥺 ...

profile
🧚🏻‍♀️ Front-End Developer

0개의 댓글