오늘 배운 것
- 모달창 기능 구현
- 블로그 홈 섹션에 표시되는 글 목록 만들기 날짜 표시
- lastChild()에서 lastElementChild()의 차이점
오늘은 블로그 글 쓰기 폼을 제출하면 홈 섹션에 글 목록이 표시되고 글 목록이 5개가 넘어가면 오래된 항목의 표시를 삭제하고 새 항목을 추가하는 기능을 구현했고 오늘 코딩을 하는데 큰 문제점은 없었지만 firestore에서 받은 API 키를 github에 push 할 수 없어서. gitignore로 push 했지만 문제가 npx http-server로 서버를 활성화하니까 API키를 읽을 수 없었고 모듈 js파일을 script src 하면서 보안 정책인 CORS (Cross-Origin Resource Sharing) 에러와 API키의 문제점을 해결하려고 시간을 엄청 많이 썼지만 결국 해결하지 못했다
모달창 코드
HTML
<table class="blog-list-table">
<thead>
<tr class="column-headers">
<th class="title-column"></th>
<th class="date-column"></th>
</tr>
</thead>
<tbody id="blog-list">
<!-- 블로그 목록 항목이 추가 -->
</tbody>
</table>
<!-- 모달 창 -->
<div id="modal" class="modal">
<div class="modal-content">
<!-- 모달에 표시될 내용은 displayModal() 함수에서 처리 -->
</div>
</div>
CSS
.modal {
display: none; /* 초기에는 모달을 숨김 */
position: fixed; /* 화면에 고정 */
z-index: 999; /* 다른 요소 위에 표시 */
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5); /* 반투명한 배경색 */
overflow: auto; /* 화면이 모달보다 작을 경우 스크롤 가능하도록 */
}
JS
// 리스트를 클릭하면 모달을 띄우는 이벤트 핸들러 추가
document.getElementById('blog-list').addEventListener('click', async (event) => {
// 클릭된 요소가 tr 또는 td인지 확인
if (event.target.tagName === 'TR' || event.target.tagName === 'TD') {
// 클릭된 tr 또는 td 요소에 저장된 doc-id 가져오기
const trElement = event.target.tagName === 'TR' ? event.target : event.target.closest('tr');
const docId = trElement.getAttribute('data-doc-id');
try {
// 해당 docId에 해당하는 데이터 가져오기
const entryData = await getEntryData(docId);
// 모달에 데이터 표시하기
displayModal(entryData);
} catch (error) {
console.error('Error fetching entry data:', error);
// 데이터를 가져오지 못한 경우에 대한 처리
}
}
});
// 데이터베이스에서 해당 docId에 해당하는 데이터 가져오기
async function getEntryData(docId) {
try {
const docRef = await getDoc(doc(db, "blog", docId));
if (docRef.exists()) {
const entryData = docRef.data();
// 필요한 데이터 형식으로 가공
return {
name: entryData.name,
title: entryData.title,
content: entryData.message,
date: entryData.createdTime.toDate(),
};
} else {
throw new Error('Document not found');
}
} catch (error) {
console.error('Error fetching entry data:', error);
// 데이터를 가져오지 못한 경우에 대한 처리
throw error;
}
}
// 모달에 데이터 표시하기
function displayModal(entryData) {
// 모달 요소 가져오기
const modal = document.getElementById('modal');
// 모달 내부의 요소들에 데이터 표시
const modalContent = `
<div class="modal-content">
<span class="close">×</span>
<div class="modal-wrap">
<h2 class="modal-title">${entryData.title}</h2>
<div class="modal-name-date">
<p class="modal-name"><strong>작성자:</strong> ${entryData.name}</p>
<p class="modal-date"><strong>작성일:</strong> ${formatDate(entryData.date)}</p>
</div>
<p class="model-content">${entryData.content}</p>
</div>
</div>
`;
// 모달에 데이터 삽입
modal.innerHTML = modalContent;
// 모달을 보이도록 설정
modal.style.display = "block";
// 모달 내부의 닫기 버튼에 이벤트 리스너 추가
modal.querySelector('.close').addEventListener('click', () => {
closeModal();
});
// 모달 외부를 클릭하면 모달 닫기
window.addEventListener('click', (event) => {
if (event.target === modal) {
closeModal();
}
});
}
// 모달 닫기 함수
function closeModal() {
const modal = document.getElementById('modal');
modal.style.display = "none";
}
문제점
- 글 목록을 table로 해서 tr인. post-entry가 잘 클릭되지 않아서 모달창을 켜는데 어려움이 있었음
해결방안
- 자식요소인 td까지 클릭할 수있게 || 논리 연산자를 이용했고 조건부 연산자를 이용해서 tr이면 그대로 td이면 closest()을 이용해서 tr을 선택하게 했다.
저번에 피드백 받았던 닫는 버튼 외에 모달 창 밖을 클릭하면 닫을 수 있는 기능도 추가해 봤다
글목록 만들기
JS
// 블로그 목록 화면에 표시
function displayBloglist(docId, entry) {
const blogListTbody = document.getElementById("blog-list");
// 화면에 표시되는 블로그 목록 개수 조회
const currentEntryCount = blogListTbody.children.length;
// Timestamp 객체를 Date 객체로 변환
const timestamp = entry.createdTime.toDate();
// Date 객체를 (YY.MM.DD)로 표시
const formattedDate = formatDate(timestamp);
// 블로그 글이 6개 이상이면 오래된 항목을 삭제
if (currentEntryCount > 6) {
console.log("Removing the oldest entry");
blogListTbody.lastChild.remove(); // 가장 오래된 항목 삭제
}
// 새 항목 추가
const entryTr = document.createElement("tr");
entryTr.classList.add("post-entry");
entryTr.setAttribute("data-doc-id", docId); // ID 설정
entryTr.innerHTML = `
<td class="post-title" data-doc-id="${docId}">${entry.title}</td>
<td class="post-date" data-doc-id="${docId}">${formattedDate}</td>
`;
blogListTbody.appendChild(entryTr);
}
문제점
- firestore에서 현재 시간을 가져오는 serverTimestamp()를 사용하면 블로그 작성 시간일도 나타낼 수 있으니 Number(new Date())를 사용하지 않고 serverTimestamp()를 사용했는데
2024년 4월 19일 오전 5시 2분 33초 UTC+9 같은 형식으로 저장 됌
해결방안
- toDate() 메서드를 사용하여 JavaScript Date 객체로 변환해서 모달창에도 날짜를 입력하려고
function formatDate(date) {
return `${String(date.getFullYear()).slice(2)}.${String(date.getMonth() + 1).padStart(2, '0')}.${String(date.getDate()).padStart(2, '0')}`;
}
함수로 만들어서 해결
문제점2
- 만들고 싶은 기능은 화면에 5개 이상 글목록이 생기면 가장 오래된 항목을 제거하고 새로운 항목이 표시되는 것을 원했다 선입선출 방식으로 하지만 첫 번째 항목은 계속 살아있고 두 번째 항목부터 지워지는 문제
해결방안
- lastChild()에서 lastElementChild()로 바꾸니 해결되었다
lastChild는 해당 요소의 마지막 자식 노드를 가져온다. 이 자식 노드가 텍스트 노드, 요소 노드 또는 주석 노드일 수 있고 마지막 자식 노드가 의미 없는 공백 또는 줄바꿈일 경우 lastChild는 실제로 내용이 있는 마지막 요소를 가리키지 않을 수 있다. 따라서 실제로 삭제해야 하는 요소를 찾는 데 문제가 발생할 위험이 있다.
lastElementChild는 해당 요소의 마지막 자식 요소를 가져온다. 텍스트 노드나 주석은 무시하고, 실제로 화면에 나타나는 요소 중에서 마지막 요소를 반환한다. 따라서 lastElementChild를 사용하면 실제로 보이는 마지막 요소를 가져올 수 있다.
lastElementChild를 사용하면 불필요한 요소를 무시하고 보다 정확하게 요소를 선택해서 요소를 선입선출로 삭제하는 데 더 쉽게 접근 가능하다
'TIL' 카테고리의 다른 글
TIL - DocumentFragment를 활용하여 DOM 조작을 최적화하고 성능을 향상 (0) | 2024.04.23 |
---|---|
TIL - 이벤트 리스너에서 querySelectorAll 를 사용하면서 생긴 문제와 해결방안: 동적으로 생성되는 요소에 대해서도 이벤트를 처리 (1) | 2024.04.22 |
TIL - JS와 Firestore 이용해서 블로그 만들기 - 탭 전환 (1) (0) | 2024.04.19 |
TIL - Git의 필요성과 prepend()를 사용하면서 생긴 문제 발견과 해결 (1) | 2024.04.18 |
TIL - 간단한 Todo list 만들어서 CRUD기능 구현 (0) | 2024.04.17 |