목차
1. aside 부분 돔 구현
2. 댓글 추가, 삭제 및 좋아요 기능
3. 게시글에 좋아요 누르기
4. nav에 아이디 검색 기능
5. 후기
4월 12일부터 위코드 부트캠프 생활을 시작했다 !
바닐라자바스크립트로 인스타그램을 클론코딩 해봤다.😀
다음주 3주차에는 인스타그램을 리액트를 이용해서 다시 구현해볼 것이다.
본 게시물은 위코드 2주차 과제인 인스타그램 클론코딩-로그인페이지와 메인페이지 구현 내용이다. 기억하고 싶은 코드가 있는 기능을 위주로 작성해보겠다 !
구현한 기능
1. 댓글 추가 삭제 및 각각 댓글의 좋아요 누르기
2. 아이디 검색 시 팝업창을 띄워서 현재 페이지에 보이는 아이디만 보이기신경 쓴 부분
- 오른쪽 aside부분 프로필 리스트 추가 시 객체들의 배열을 만들고 배열을 반복하면서 돔을 그려나갔다. API를 받아와서 처리할 때 객체와 배열 사용에 익숙해져야 해서 하드코딩을 지양하고 유지보수, 확장성이 용이하게 만들려고 노력했다 !
const users = [
{ id: "jiyon", img: "./images/hao.jpg" },
{ id: "Shaman_king", img: "./images/hao2.jpg" },
{ id: "iu", img: "./images/iu.jpg" },
{ id: "Jeon_dongseok", img: "./images/dongseok.jpg" },
{ id: "kakashi", img: "./images/kakashi.png" },
{ id: "gaara", img: "./images/gaara.jpg" },
{ id: "hyoshin", img: "./images/hyoshin.jpeg" },
{ id: "lock-li", img: "./images/lock-li.jpg" },
{ id: "naruto", img: "./images/naruto.jpg" },
];
const userIds = users.reduce((pre, cur) => {
return [...pre, cur.id];
}, []);
const userImgs = users.reduce((pre, cur) => {
return [...pre, cur.img];
}, []);
const createProfileLists = (dom, imgsrc, profileName, postedTime) => {
const asideMain = document.querySelector(`${dom} .asideMain`);
const imgAndId = document.createElement("li");
const asideProfileImg = document.createElement("img");
const idAndTime = document.createElement("div");
const profileId = document.createElement("div");
const postTime = document.createElement("div");
imgAndId.classList.add("imgAndId");
asideProfileImg.classList.add("asideProfileImg");
idAndTime.classList.add("idAndTime");
profileId.classList.add("profileId");
postTime.classList.add("postTime");
postTime.classList.add("grey");
idAndTime.append(profileId);
idAndTime.append(postTime);
imgAndId.append(asideProfileImg);
imgAndId.append(idAndTime);
asideMain.append(imgAndId);
profileId.textContent = profileName;
postTime.textContent = postedTime;
asideProfileImg.setAttribute("src", imgsrc);
};
users.forEach((user) => {
createProfileLists(".story", user.img, user.id, "20분 전");
});
users.forEach((user) => {
createProfileLists(
".recommend",
user.img,
user.id,
"000님이 팔로우합니다."
);
});
forEach 메소드로 users 객체를 돌면서 createProfileLists함수를 실행시켜서 돔을 그려냈다.
postCommentBtn.addEventListener("click", () => {
clickCommentBtn();
});
postCommentInput.addEventListener("keyup", (e) => {
postCommentInput.value.length > 0
? postCommentBtn.classList.add("blue")
: postCommentBtn.classList.remove("blue");
if (e.keyCode !== 13) {
return;
} else {
clickCommentBtn();
}
});
const clickCommentBtn = () => {
if (postCommentInput.value.length == 0) {
return;
}
const commentWrap = document.createElement("div");
const commenter = document.createElement("span");
const comment = document.createElement("span");
const commentDeleteBtn = document.createElement("button");
const commentLikesBtn = document.createElement("button");
commentWrap.classList.add("commentWrap");
commenter.classList.add("commenter");
comment.classList.add("comment");
commentDeleteBtn.classList.add("commentBtn");
commentDeleteBtn.classList.add("commentDeleteBtn");
commentLikesBtn.classList.add("commentBtn");
commentLikesBtn.classList.add("commentLikesBtn");
commentWrap.append(commenter);
commentWrap.append(comment);
commentWrap.append(commentDeleteBtn);
commentWrap.append(commentLikesBtn);
articleComment.append(commentWrap);
commentDeleteBtn.innerHTML = `<i class="far fa-trash-alt"></i>`;
commentLikesBtn.innerHTML = `<i class="emptyHeart far fa-heart"></i>
<i class="redHeart fas fa-heart red hide"></i>`;
commenter.textContent = "Shaman_king";
comment.textContent = postCommentInput.value;
postCommentInput.focus();
commentTime.textContent = "방금";
postCommentBtn.classList.remove("blue");
postCommentInput.value = "";
commentLikesBtn.addEventListener("click", (e) => {
let emptyHeart = e.currentTarget.querySelector(".emptyHeart");
let redHeart = e.currentTarget.querySelector(".redHeart");
emptyHeart.classList.toggle("hide");
redHeart.classList.toggle("hide");
});
const commentDeleteBtns = document.querySelectorAll(".commentDeleteBtn");
commentDeleteBtns.forEach((commentDeleteBtn) => {
commentDeleteBtn.addEventListener("click", () => {
commentDeleteBtn.parentNode.remove();
});
});
};
pushLike.addEventListener("click", (e) => {
e.preventDefault();
if (pushLike.style.backgroundPosition.slice(0, 4) === "-156") {
pushLike.style.backgroundPosition = `-130px -478px`;
likeNum.textContent = Number(likeNum.textContent) + 1;
} else {
pushLike.style.backgroundPosition = `-156px -478px`;
likeNum.textContent = Number(likeNum.textContent) - 1;
}
});
const createSearchLists = (searchInputValue) => {
if (searchPopup.childNodes.length > 0) {
return;
}
const searchedList = document.createElement("li");
const profileImg = document.createElement("img");
const idAndNickname = document.createElement("div");
const userid = document.createElement("div");
const nickname = document.createElement("div");
userid.textContent = searchInputValue;
let userIdIndex = userIds.indexOf(searchInputValue);
profileImg.setAttribute("src", users[userIdIndex].img);
// ----------------------------------------
// ex) iu의 위치를 받아서 그 위치의 이미지를 넣어줬다.
// setAttribute를 처음 사용해보았다.
// ----------------------------------------
idAndNickname.append(userid);
idAndNickname.append(nickname);
searchedList.append(profileImg);
searchedList.append(idAndNickname);
searchPopup.append(searchedList);
};
navSearch.addEventListener("input", (e) => {
if (e.target.value.length === 0) {
searchPopup.innerHTML = "";
} else {
for (i = 0; i < userIds.length; i++) {
if (userIds[i].startsWith(e.target.value)) {
createSearchLists(userIds[i]);
break;
} else {
searchPopup.innerHTML = "";
}
}
// ------------------------------------------------
// forEach 사용시 break 가 되지 않아서 for loop을 돌렸다.
// ------------------------------------------------
}
});
navSearch.addEventListener("blur", () => {
searchPopup.innerHTML = "";
});
로직은 다 구현이 된 상태니 아마 다음주 리액트로 구현하는 것은 금방 할 수 있지 않을까?!! (허튼 생각이 아니길!)
와 filter 말고 reduce를 사용할 수도 있네요!!! 지연님 응용력 짱..👍🏻