-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[2주차 기본/심화 과제] 웨비들의 냠냠 🍰 창업🏠 손님을 모셔오자!🌈 #4
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
카드 / 카테고리 태그 를 구현하는 부분을 나와 다르게 구현한 점이 인상 깊었다!
유지보수 적인 면에서는 은서 코드가 좀 더 캡슐화되어 있는 면에서 더 깔끔하다는 생각이 들어.
또 주석도 꼼꼼하고 생각을 많이 한 느낌을 받아서 많이 배우고 가 .ㅎㅎ
<label for="cateogry"><h4>카테고리</h4></label> | ||
<select required name="category"> | ||
<option value="">=== 선택 ===</option> | ||
<option value="veg">채소</option> | ||
<option value="mush">버섯</option> | ||
<option value="tofu">두부</option> | ||
<option value="etc">기타</option> | ||
</select> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시맨틱 태그 넘 좋다 !
|
||
const form = document.getElementById("add-card-form"); | ||
form.addEventListener("submit", (e) => { | ||
e.preventDefault(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
꼼꼼하다!
나도 넣어줘야겠어 ㅎㅎ
<ul> | ||
<li> | ||
<input class="main__nav__checkbox" type="checkbox" id="check-all" /> | ||
<label for="check-all">전체</label> | ||
</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ul, li, input, label.. 정말 꼼꼼한 시맨틱 태그다!
#cards { | ||
display: grid; | ||
grid-template-columns: repeat(5, 13rem); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 우연히 발견했는데
grid-template-columns: repeat(auto-fill,minmax(최소너비,auto));
이런 식으로 사용하면 아래 미디어 쿼리로 따로 개수 일일이 조정 안해줘도
알아서 너비에 따라 repeat되게 해주더라고?
덕분에 나도 코드 몇줄 줄였어 ㅎㅎ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헐 이거 하드코딩하는거 진짜 맘에 안들었는데 완전 꿀팁!!
#header__button:hover + #header__menu, | ||
#header__menu:hover { | ||
display: block; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
중복되는 스타일링을
같이 써준 거 좋다 !!
/************* | ||
필터링된 데이터 기반으로 화면에 보여주기 | ||
**************/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 한 줄 한 줄마다 주석 처리해준 거 너무 좋다!
덕분에 곰방 곰방 이해함
item.checked, //감지된 변화가 체크인가, 체크 해제인가 | ||
CATEGORY_NAME[item.id], //변화가 감지된 checkBox의 카테고리명 | ||
curItemList |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
나는 이벤트 하면 'click'밖에 생각을 못했는데
input (type: checkbox)로 해두고 해당 변화에 대한 값으로 해체/클릭까지 구현해준 점이
인상깊고 꼼꼼하다!
(카테고리 nav단에서도 카테고리 선택/해제 기능을 해야하는 지 모른 1인)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시험 기간라 바빴을텐데 심화과제까지 한 은서 언니 너무 멋져,,, 정말 최고다!!!!! 💯
|
||
//list를 탐색하면서 요소를 하나씩 card 노드로 만드는 함수 | ||
function listToCard(list) { | ||
cardsSection.replaceChildren(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 replaceChildren 대신 innerHTML("")을 사용하면 어떨까??
replaceChildren를 사용하면 자식 노드를 모두 삭제한 뒤 다시 null 을 추가하게 되는데 innerHTML을 사용하면 기존의 것을 삭제하지 않고 바로 빈 문자열로 대체할 수 있어서 자원 낭비를 줄일 수 있을 것 같아!! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오대박!! 그런방법이 있다니 같은 기능도 참 여러가지로 쓸수있구나... 고마워유
//tag들 또한 리스트이므로 그 안에서 map을 돌린다. | ||
let tags = ``; | ||
item.tags.forEach((tag) => { | ||
tags += `<small>` + tag + `</small>\n`; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const tags = item.tags.map((tag) => <small>${tag}</small>
).join("\n");
이 부분은 이렇게 바꾸어 보면 어떨까???? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와 대박이네 join 메소드 진짜 유용하다...
let content = cardTemplate.cloneNode(true); //템플릿 복사 | ||
let newHtml = content.innerHTML; //템플릿 안의 html 복사 | ||
newHtml = newHtml //복사한 html에서 필요한 부분을 item 내용에 맞게 변경 | ||
.replace("{item_name}", item.name) | ||
.replace("{tags}", tags) | ||
.replace("{modal_tags}", tags) | ||
.replace("{img_alt}", item.name) | ||
.replace("{img_src}", item.img); | ||
|
||
content.innerHTML = newHtml; //새롭게 바뀐 html을 템플릿에 적용 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요 부분 넘넘 잘했는데 const content = cardTemplate.content.cloneNode(true); 이렇게 해서 node 중에 content 만 복사해서 content 에 넣고, replace 대신에 content.querySelector 를 사용해 요소를 선택하고, 요소 변경은 textContent 와 setAttribute 이용해 보는 건 어떨까??
- 추가로 객체같은 경우에는 프로퍼티를 바꾸는 건 const 로 선언해도 수정 가능하기 때문에, let 대신에 const 많이 활용해주면 좋을 것 같아!! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 그렇게하면 애초에 content만 복사할 수 있구나... replace로 텍스트자체를 갈아끼우는게 별로라고는 느껴졌는데 섬세히 대안 남겨줘서 넘 고마워...
}); | ||
}); | ||
|
||
//overlay 영역 클릭시 모달 close |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와 overlay 영역 클릭 시 모달 닫히게 하는 것까지 구현하다니!!! 최고다 은서 언니!!! "v
function makeCategoryTag(checkBox) { | ||
if (checkBox.checked) { | ||
let content = categoryTagTemplate.cloneNode(true); //템플릿 복사 | ||
let newHtml = content.innerHTML; //템플릿 안의 html 복사 | ||
newHtml = newHtml | ||
.replace("{category_name}", CATEGORY_NAME[checkBox.id]) | ||
.replace("{checkbox_id}", checkBox.id) | ||
.replace("{category-tag_id}", "tag__" + checkBox.id); | ||
content.innerHTML = newHtml; | ||
categoryTagSection.appendChild(content.content); | ||
} else { | ||
const target = document.getElementById("tag__" + checkBox.id); | ||
target.remove(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요기두 위에서 얘기한 방식으루 바꿔봐도 좋겠다!!! :)
🔗 구현 페이지
✨ 구현 기능 명세
기본 과제
상품 데이터
nav
card article
심화 과제
목록
새상품 추가 페이지
카드 애니메이션
🌼 PR Point
template 태그 사용
정찬우씨가 template 태그라는 신문물을 알려주셔서 적극 활용해보았습니다! 추가로 https://youtu.be/sgJMeiV0tyc 이 영상 보니 이해가 확 되었어요! 정말 유용해요!!
template 태그를 사용하면, 동적으로 사용할 태그 '템플릿'을 html문서에 만들어놓고 javascript에서 가져다 쓸 수 있어요. MDN에서는 콘텐츠 조각을 나중에 사용하기 위해 담아놓는 컨테이너 라고 소개하고 있습니다. template 태그 내의 콘텐츠는 페이지가 로드될 때 즉시 렌더링되지 않기 때문에 사용자에게는 보이지 않구요, 나중에 자바스크립트로 해당 콘텐츠를 복제해서 렌더링하는 방식으로 사용합니다.
예를들어 이번 과제에서 아이템 카드들을 동적으로 생성해야 하잖아요. 이걸 javascript로 하나하나 createElement...하면 만들게 너무 많아지니까 아래와 같이 template태그로 아이템 카드의 틀을 미리 만들어 놓습니다. 보시면 템플릿 안에서 동적으로 변경되어야 하는 내용들은 {item_name}이런식으로 작성해두었어요.
그리고 이걸 js파일에서 가져와서, replace() method로 변경할 부분을 변경하고 집어넣는 방식이에요!
이런식으로 카드아이템을 구현했고, modal또한 동적으로 반복되는 부분이기에 template태그로 구현하였습니다. 와우 너무 스마트해요
카테고리 필터링
카테고리 필터링에서는 체크가 된건지 해제된건지를 나타내는
isChecked
, 변화가 나타난 카테고리 이름인categoryName
, 그리고 최종적으로 화면에 보여지는 아이템 목록을 담을list
가 핵심입니다.newItemList
는 localStorage에서 가져온 전체 아이템 리스트입니다.변화가 감지된 카테고리가 "전체"일 때와 아닐 때로 나누어서 구현했습니다. 먼저 "전체" 카테고리를 선택했을 때는 단순히 newItemList의 모든 항목을 보여줄 아이템으로 push했습니다. "전체" 카테고리 선택 해제시에는 현재 체크박스 목록을 다시 훑으면서, 선택되어있는 카테고리는 "전체" 카테고리가 해제되더라도 보여줘야하므로, 선택되어있지 않은 카테고리의 아이템만 제거하였습니다.
카테고리 필터링은 기본적으로 체크박스의 변화를 감지할 때 위의 반복문을 돌려서 구현했는데요. newItemList의 요소를 하나씩 보면서 현재 변화가 감지된 카테고리에 해당하는 아이템이라면 list에 push하거나, 삭제합니다. 그런데 "전체"카테고리가 선택되어 있으면 삭제하면 안되므로 선택되어있지 않을 때만 삭제하도록 했습니다. (이 단순한 아이디어를 생각해내는데 꽤 오래걸렸네요...) removeByCategoryName()은
categoryName
에 해당하는 아이템을 list에서 제거하는 함수로, 다음과 같이 filter() 메소드를 사용했습니다.카테고리 태그 만들기
카테고리 태그를 만드는 부분은 걱정했던 것 보단 별 문제 없이 구현했습니다. 왜냐면 카테고리 태그의 X버튼을 label의 for속성을 통해서 실제 nav의 체크박스와 연동(?)을 했더니 굉장히 간편하게 구현이 되더라구요!
이미지 미리보기
이미지 미리보기도 fileReader()라는 객체를 사용하니 간편하게 구현할 수 있었습니다. fileReader()는 비동기적으로 파일을 읽어들일 때 사용하는 객체인데, 위 코드를 보면
imageInput.files[0]
가 사용자가 업로드한 파일이고, readAsDataURL()을 통해 파일의 url을 읽어들입니다. 그렇게 reader객체가 load가 되면 html상의 img태그를 붙잡아온imageThumbNail
의 src속성에 해당 url을 넣어줌으로써 사용자가 업로드한 이미지를 화면에 띄울 수 있습니다. 이 url을 그대로 localStorage에도 넘겨주니 홈에서도 사용자가 직접 업로드한 이미지를 보여줄 수 있었습니다!소소한 디테일 🙂
🥺 소요 시간, 어려웠던 점
12h
javaScript 문법
제가 javaScript에 아직 능숙하지 않은데, 세미나에서 배운 여러 문법을 활용하고 싶어서 삼항연산자도 써보고 && || 이런것도 써보려고 노력했습니다! 근데 어떤 때 어떨 걸 써야 좋을지 아직 잘 모르겠더라구요. 뭔가 사람들이 map을 많이 사용하는 것 같은데, 언제 forEach를 쓰고 언제 map을 써야할지 이런... 저는 단순 반복탐색이 필요할 때가 많아서 대부분 forEach를 쓴 것 같습니다. 그리고 반복이 중첩되는건 피하려고 했어요! 근데 이렇게 자스를 제대로(?) 써본 게 거의 처음이라 정말 재밌었어요!!! 그런한편 이게 맞나.. 싶은게 많은 것 같네요 최대한 깔끔한 코드를 짜려고 노력은 했는데 결국은 좀 덕지덕지가 된 것 같은 ㅎ_ㅠ
상수파일 따로 분리해서 사용하기
data를 상수로 저장할 때, data를 처리하는 부분이랑 같이 저장해둘 수도 있지만 data는 파일을 아예 따로 분리해두고 싶더라구요. 근데 그렇게 따로 분리해둔 파일을 어떻게 가져와서 사용하지?에 대해 좀 생각해야 했던 것 같아요. data를 저장한 파일에서 해당 상수를 export default를 통해 내보내기해주고, 이걸 사용할 파일에서는 import를 통해 불러올 수 있었는데, 자꾸 다음과 같은 오류가 나더라구요...
구글링 결과, html파일에서 js파일을 연결하는 script 태그에 type=”module” 속성을 추가하여 해결할 수 있었습니다. 이렇게 해야 script 태그가 참조하는 파일을 모듈로 인식할 수 있다더라구요. 참고자료
HTMLCollection
이번 과제, 배열을 잡아와서 탐색해야 하는 상황이 굉장히 많았는데, 어떨 땐 forEach같은 걸로 탐색을 해도 자꾸 undefined가 뜨더라구요! 그래서 초반에 헤맸는데, HTMLCollection과 Array의 차이 때문이었습니다.
HTMLCollection
은getElementsByClassName
과getElementsByTagName
메소드를 통해 얻을 수 있는 객체이고, 이는 일반 배열이 아닌 ‘유사 배열’이며, 일반 배열과의 차이는 ‘살아있다’라는 점이라고 합니다. 정적으로 존재하는 것이 아니라, 실제 노드 객체의 상태 변화를 감지하고 반영하고 있기 때문이에요. 그래서 HTMLCollection을 배열로서 사용하기 위해서는 배열로 변환을 해줘야하는데, 보통 Array.from({HTMLCollection})을 많이 쓰는 것 같아요. 저는 spread 연산자도 활용했습니다. 참고자료"전체" 카테고리 필터링
처음에는 정말 단순히, "전체"카테고리를 선택했을 때는 다보여주고, 선택 해제했을 때는 다 삭제 하면 되는거 아닌가? 했는데요. "전체"카테고리를 선택 해제 하더라도 현재 체크되어있는 카테고리의 아이템들을 그대로 남겨놓아야하는 부분이 정말 어려웠습니다. 현재 선택되어있는 카테고리 목록을 저장하는 배열을 하나 따로 만들어야 하나? 현재 보여지고 있는 아이템 목록과 전체 아이템 목록의 교집합 배열을 구해서 해당하는 것들을 삭제해야하나??? 여러 시도를 해보다가, 현재 체크되어있는 카테고리를 확인하고 그 카테고리에 속하지 않는 것만 삭제하는 식으로 구현했습니다. 또 배열에서 특정 요소를 삭제할 때 splite() 메소드를 이용해서 삭제할 요소의 인덱스 값을 주어야 하다보니, 하나를 삭제하면 인덱스 값이 바뀌고 이래서 이 부분에서도 시행착오를 많이 겪었습니다. 그래서 배열에서 요소를 삭제할 때 splite()가 아니라 filter()를 사용했습니다!
🌈 구현 결과물
카테고리 필터링
Untitled1.mp4
해시태그 더보기
Untitled2.mp4
새 상품 추가
Untitled3.mp4