인스타그램 클론 코딩 프로젝트의 추가 구현 사항중, 피드에 달린 댓글을 삭제하거나 좋아요를 누르는 기능을 추가하는 것을 진행하고 있다. (역시나..) 추가한 자바스크립트 코드에서 여러가지 이슈가 발생하였고 그 중에 꼭 기억해야 할 것들을 정리한다.
// 댓글 좋아요
commentList.addEventListener('click', function() { // 첫번째 클릭 이벤트 리스너
var commentLike = document.querySelectorAll('.comment-like');
commentLike.forEach(function(e) {
e.addEventListener('click', function() { // 두번째 클릭 이벤트 리스너
var likeBtn = this.querySelector('.comment-heart');
var likedBtn = this.querySelector('.comment-heart-liked');
if (likeBtn.style.display === 'none') {
likeBtn.style.display = 'inline-block';
likedBtn.style.display = 'none';
} else {
likeBtn.style.display = 'none';
likedBtn.style.display = 'inline-block';
}
})
})
})
위 사진과 같이 댓글의 맨 우측에 위치한 하트(Like Button) 클릭 시, 하트의 색상이 채워지고, 이미 Like 된 하트는 다시 Like가 해제되는 기능을 구현하고자 짠 코드이다. 지난번에 정리했던 이벤트 위임을 적용하여 Like 버튼을 포함하는 부모 엘리먼트인 commentList
에 클릭이벤트 발생 시, commentLike
변수를 정의하고, 각 commentLike
를 forEach
로 돌며 클릭 된 버튼의 색상이 변경될 수 있도록 만든 코드이다. 위 코드를 실행했을때, 하트를 클릭시 색이 변하는 것이 규칙적이지 않은 오류가 발생했다.
문제의 원인은 commentList
에서 발생한 클릭이벤트와, commentLike
에서 발생한 클릭이벤트를 중복으로 감지하기 때문이었다. console.log
로 확인해보니 한번의 클릭에 여러번의 리턴이 나오는 것을 확인 할 수 있었다.
var commentLike = document.querySelectorAll('.comment-like');
commentLike.forEach(function(e) {
e.addEventListener('click', function() {
var likeBtn = this.querySelector('.comment-heart');
var likedBtn = this.querySelector('.comment-heart-liked');
if (likeBtn.style.display === 'none') {
likeBtn.style.display = 'inline-block';
likedBtn.style.display = 'none';
} else {
likeBtn.style.display = 'none';
likedBtn.style.display = 'inline-block';
}
// likedBtn.style.display = likedBtn.style.display === 'none' ? 'inline-block' : 'none';
// likeBtn.style.display = likeBtn.style.display === 'none' ? 'inline-block' : 'none';
})
})
불필요하게 중복된 클릭이벤트를 수정하였다. 하지만 수정 후 발생한 또 다른 문제점, 페이지에서 새롭게 인풋된 댓글은 Like 기능이 적용되지 않았다. 자바스크립트가 실행될때 commentLike
를 이미 정의해버리고 클릭이벤트 리스너가 실행되기 때문에 생기는 문제점이었다. 도저히 구글링해서 답을 얻을 수 없어서 여기부터는 멘토님의 조언을 구하기로 했다.
멘토님은 comment를 추가하는 함수 안에서 기존의 li
엘리먼트 안에 innerHTML
로 포함되어 생성되었던 아이콘들(삭제, 하트)을 따로 div
로 createElement
로 만들고 함수로 추가하는 방법을 제안하셨다. (💥 : 머리 깨지는 소리 )
const commentInput = document.getElementsByClassName('input-comment')[0];
const commentBtn = document.getElementsByClassName('submit-comment')[0];
const commentList = document.getElementsByClassName('comments')[0];
// 댓글 달기
function addComment() {
var newComment = document.createElement('li')
newComment.innerHTML = `<span><span class="point-span userID">thisisyourhyung</span>` + commentInput.value + `</span>`;
// 코멘트에 더해지는 버튼 생성
let commentBtns = document.createElement('div');
let deleteBtn = document.createElement('img');
deleteBtn.classList.add("comment-more");
deleteBtn.src = "https://s3.ap-northeast-2.amazonaws.com/cdn.wecode.co.kr/bearu/more.png";
deleteBtn.alt = "more";
let likeBtn = document.createElement('img');
likeBtn.classList.add("comment-heart");
likeBtn.src = "https://s3.ap-northeast-2.amazonaws.com/cdn.wecode.co.kr/bearu/heart.png";
likeBtn.alt = "하트";
let likedBtn = document.createElement('img');
likedBtn.classList.add("comment-heart-liked");
likedBtn.src = "img/liked.png";
likedBtn.alt = "좋아요된하트";
let commentLike = document.createElement('div');
commentLike.classList.add("comment-like");
// 버튼에 함수 선언
deleteBtn.addEventListener('click', function() {
this.parentNode.parentNode.remove();
})
commentLike.addEventListener('click', () => {
if (likeBtn.style.display === 'none') {
likeBtn.style.display = 'inline-block';
likedBtn.style.display = 'none';
} else {
likeBtn.style.display = 'none';
likedBtn.style.display = 'inline-block';
}
})
// 코멘트에 버튼 추가
commentLike.appendChild(likeBtn);
commentLike.appendChild(likedBtn);
commentBtns.appendChild(deleteBtn);
commentBtns.appendChild(commentLike);
newComment.appendChild(commentBtns);
commentList.appendChild(newComment);
commentInput.value = "";
commentBtn.disabled = true;
}
// 댓글 좋아요
let commentLike = document.querySelectorAll('.comment-like');
commentLike.forEach(function(event) {
event.addEventListener('click', function() {
var likeBtn = this.querySelector('.comment-heart');
var likedBtn = this.querySelector('.comment-heart-liked');
if (likeBtn.style.display === 'none') {
likeBtn.style.display = 'inline-block';
likedBtn.style.display = 'none';
} else {
likeBtn.style.display = 'none';
likedBtn.style.display = 'inline-block';
}
})
})
갑자기 코드가 길어진 이유는 코멘트를 추가하는 함수가 버튼에 함수를 추가하는 함수를 포함한 구조가 되었기 때문이다. 코멘트 <li>
가 addComment()
함수로 추가될 때마다 newComment
인 <li>
내부에 함수가 선언된 엘리먼트들이 .appendChild()
되는 구조가 되었다. 이미지를 동적으로 생성하는 방법이다. 이렇게 코드를 수정하면, 기존에 default로 세팅되어있던 댓글과 새로 생성한 댓글 모두에 삭제나 좋아요 기능이 동일하게 적용된다. 생각보다 코드가 엄청 길어져서 더 깔끔하게 짤 수는 없을지 고민이 된다.
참고 자료 : 이미지 동적 생성하는 간단한 예제
오늘 배운 점! 자바스크립트에서 변수를 선언하는 시점이 함수가 제대로 작동하는데 중요한 요소이기 때문에 이를 고려하여 처음부터 구조를 잘 짜는게 정말 정말 중요하다!