이번에는 저번 자바스크립트로 댓글 작성 기능을 구현하는 코드에 이어 댓글을 삭제하는 기능을 구현해보도록 하겠습니다.
이전에 배웠던 내용보다는 양이 많지는 않지만, 매우 중요한 DOM Events를 포함하고 있으니 잘 알아두시면 아주 도움이 될 것이라 생각합니다.
추가적으로 이것은 서버와의 통신 없이 단순히 DOM Events 요소만을 사용하여 구현한 코드라는 점 참고하시고 봐주시면 되겠습니다! 👻
<h1>Instagram Comments</h1>
<form action="/dogs" id="instaForm">
<input type="text" name="username" placeholder="username" />
<input type="text" name="comment" placeholder="comment" />
<button>댓글 달기</button>
</form>
<h2>댓글:</h2>
<ul id="comments">
<li>I AM LI!!!</li>
<li>I AM LI!!!</li>
</ul>
const instaForm = document.querySelector("#instaForm");
const commentsContainer = document.querySelector("#comments");
instaForm.addEventListener("submit", (e) => {
e.preventDefault();
const usernameInput = instaForm.elements.username;
const commentInput = instaForm.elements.comment;
addComment(usernameInput.value, commentInput.value);
usernameInput.value = "";
commentInput.value = "";
});
const addComment = (username, comment) => {
const newComment = document.createElement("li");
const bTag = document.createElement("b");
bTag.append(username);
newComment.append(bTag);
newComment.append(`- ${comment}`);
commentsContainer.append(newComment);
};
위의 코드로 코멘트를 달면 아래에 자동으로 댓글이 적히는 코드를 완성하였습니다. 물론, 웹 상에서의 CRUD에 해당하는 것들은 모두 서버와의 통신으로 이루어져 있습니다. 하지만 여기서 배우는 것은 DOM Events이기 때문에 간단하게 자바스크립트로 댓글을 클릭하면 사라지도록 코드를 작성해보도록 하겠습니다.
두 가지의 예를 보여드릴 예정이며, 첫 번째 예시는 li 태그를 직접 삭제하는 방법과 두 번째로는 addEventListener의 매개 변수 event.target을 활용한 방법을 살펴봅시다.
const lis = document.querySelectorAll("li");
for (let li of lis) {
li.addEventListener("click", () => {
li.remove();
});
}
먼저, 모든 li
태그를 선택해야하므로 querySelectorAll
을 이용해 li
를 지정해줍니다. 그리고 반복문을 통해서 클릭을 하면, li
가 지워지게 만들면 되는 것이죠.
여기에서 필요한 메소드가 li.remove()
입니다. remove는 아시다시피 '제거하다'라는 의미를 가지고 있죠.
하지만 이 코드에는 문제점이 있습니다. 혹시 눈치채신 분들이 있으실까요?
바로 HTML에 코드에 미리 적혀있었던 I AM LI, Orosy- I Like You!는 삭제가 가능하지만 위의 화면과 같이 이후에 제가 작성한 댓글을 삭제가 되지 않습니다. 왜냐하면, 자바스크립트가 선택한 li
태그는 이미 적혀있던 li
태그 외에는 querySelectAll
로 포함되지 않기 때문이죠.
결국, 새롭게 추가된 리스트 아이템에는 클릭 이벤트 리스너가 등록되지 않았던 것입니다. 그렇다면 이를 해결하려면 어떻게 해야할까요? 이 때, DOM Event가 등장합니다.
여기서 나오는 개념은 Event Delegation, 바로 이벤트 위임이라고 하는 것입니다. 이는 이벤트 핸들러를 말 그대로 내가 해야되는 일이지만 다른 상위 태그에 위임하는 것을 의미합니다.
어려운 말로 풀이하자면, 하위 요소에 각각 이벤트를 붙이지 않고 상위 요소에서 하위 요소의 이벤트들을 제어하는 방식이 이벤트 위임입니다.
그렇다면, 여기서 li
태그의 상위 요소는 무엇일까요? 그렇습니다. 바로 ul
태그가 되겠죠. 현재 코드에서 id 값이 commentsContainer
이므로 여기에 클릭 이벤트 리스너를 지정해주면 되는 것입니다.
왜냐하면 원래 있던 리스트이건 나중에 생긴 리스트이건 모두 상위 요소인 commentsContainer
에 속해있기 때문입니다.
commentsContainer.addEventListener("click", (event) => {
event.target.remove();
});
그렇기 때문에 위와 같은 코드가 나온 것입니다. 하지만, 여기서 추가적으로 또 알아야하는 것은 바로 event.target
입니다. 우리의 목적은 단순히 ul
태그에 있는 리스트를 모두 삭제하는 것이 아닙니다. 그 안에 있는 리스트 중에 내가 클릭하는 것만 삭제를 진행해줘야 하는 것이죠.
그것이 바로 event
객체 내에 있는 프로퍼티 중의 하나인 target
, 클릭이 되어진 리스트인 것입니다. 그리고 여기에 remove()
메소드를 더해주면 코드가 완성이 됩니다.
여기서 마무리해도 지장은 없지만, 추가적으로 우리는 리스트만을 제거하고 싶다면 어떻게 해야할까요? 해당 ul
태그 내에 button
, p
, span
등의 태그가 포함되어 있다하더라도 우리는 li
태그만을 없애고 싶습니다. 이 또한 방법을 알아봅시다.
container.addEventListener("click", (e) => {
console.dir(e.target);
e.target.remove();
});
그렇다면, 이를 알아보기 위해event.target
의 속성에서 리스트만을 뽑아낼 수 있는지 확인해봅시다.
ul
태그 내에 li
, p
태그를 둘 다 만들어놓고 확인해보기로 했습니다.
그랬더니 저는 두 개를 확인할 수 있었습니다. 먼저, nodeName
이라는 속성에서 "LI"를 발견했습니다.
그리고 이뿐만 아니라 tagName
이라는 속성값도 "LI"였습니다. 당연하게도 p
태그는 "P"라는 속성값으로 되어 있었습니다. 결국 이 값을 코딩하면, ul 태그 내에 있는 li 태그만을 골라 제거가 가능하게 만들 수 있다는 의미입니다.
실제로 스택 오버 플로우에 두 속성의 차이를 답변해주는 글이 있었고, 결론은 nodeName
사용을 추천한다는 답변이었습니다. 자세한 내용은 저도 아직 부족하여 내용을 이해하지 못했지만, 직접 보시고 싶으신 분은 이 링크를 참고해주시기 바랍니다. 그리고 저에게 알려주시면, 참으로 감사하겠습니다.
commentsContainer.addEventListener("click", (e) => {
e.target.nodeName === "LI" && e.target.remove();
});
그래서 if
를 사용할 수도 있지만, 간편하게 논리연산자인 &&
을 이용하여 앞의 데이터가 거짓이면 뒤의 내용이 실행되지 않도록 하는 코드로 작성을 하였습니다.
자, 이렇게 해서 저번 글과 함께 자바스크립트를 사용하여 댓글을 작성하고 삭제하는 방법까지 구현을 하였습니다. 어찌 보면 쉬운 코드 진행이지만, 이 내용 안에는 DOM Events, preventDefault, Event Delegation 등 굉장히 많은 내용이 내포되어 있었다고 생각합니다.
그리고 위 내용은 어떤 프레임워크를 쓰느냐와 관계없이 기본적인 브라우저의 이벤트 감지 방식이기 때문에 알아두시면 유용한 정보일 것입니다.
해당 글은 Udemy의 웹 강의와 캡틴 판교님의 글을 참고하였으니 직접 해당 페이지에 들어가서 보시는 것도 추천드립니다. 마지막으로 아래에 완성 코드를 남기며, 모두 즐코하세요!🙋♂️
const instaForm = document.querySelector("#instaForm");
const commentsContainer = document.querySelector("#comments");
// 댓글 작성
instaForm.addEventListener("submit", function (e) {
e.preventDefault();
const usernameInput = instaForm.elements.username;
const commentInput = instaForm.elements.comment;
addComment(usernameInput.value, commentInput.value);
usernameInput.value = "";
commentInput.value = "";
});
const addComment = (username, comment) => {
const newComment = document.createElement("li");
const bTag = document.createElement("b");
bTag.append(username);
newComment.append(bTag);
newComment.append(`- ${comment}`);
commentsContainer.append(newComment);
};
// 댓글 삭제
commentsContainer.addEventListener("click", (e) => {
e.target.nodeName === "LI" && e.target.remove();
});
I'm constantly searching on the internet for posts that will help me. Too much is clearly to learn about this. I believe you created good quality items in Functions also. Keep working, congrats!
https://www.raipurescortsservices.in/korba-call-girls.html
https://www.raipurescortsservices.in/ambikapur-call-girls.html
https://www.raipurescortsservices.in/rajnandgaon-call-girls.html
https://www.raipurescortsservices.in/dhamtari-call-girls.html
Incredible work for streaming such a positive site. Your weblog isn't just mammoth yet it is in a way astoundingly innovative likely.
https://www.raipurescortsservices.in/dongargarh-call-girls.html
https://www.raipurescortsservices.in/naya-raipur-atal-nagar-call-girls.html
https://www.raipurescortsservices.in/durg-call-girls.html
https://www.raipurescortsservices.in/ratanpur-call-girls.html
I was very encouraged to find this site. The reason is that this is such an informative post. Thanks for sharing!
https://www.raipurescortsservices.in/bilaspur-call-girls.html
https://www.raipurescortsservices.in/bhilai-call-girls.html
https://www.raipurescortsservices.in/jagdalpur-call-girls.html
https://www.raipurescortsservices.in/raigarh-call-girls.html