게시글
질문&답변
2024.01.22
flexbox와 grid
레이아웃을 짜는데 정답은 없습니다! 그리드든 플렉스는 애초에 어떤 구성을 기획하셨는지가 제일 중요해요. 그 모양으로 만들 수 있다면 그리드를 썼다고 틀린 것은 아닙니다. 또한 모바일에 대한 반응형 처리를 어떻게 할지에 따라 달라질 수 있습니다. 위와 같은 디자인은 전부 플렉스로 잡아도 충분할 것 같습니다 ^^ 한 마디로 그리드든 플렉스는 2개 다 반드시 잘 알고 계셔야 하고, 어떤 걸 쓰더라도 본인이 생각한 레이아웃을 잡을 수 있으면 되기 때문에 어떤 식으로 레이아웃을 잡아야 하는지 다양한 옵션에 대한 이해가 기본 되어야 합니다. 다만 추천을 드리자면, 그냥 가로든 세로든 쌓여 있기만 하면 된다면 플렉스를, 조금 독특한 구조의 레이아웃을 구성해야 하거나, 완전 표 형식의 규칙적인 데이터를 다룰 때는 그리드를 사용하시면 더 좋습니다.
- 0
- 2
- 102
질문&답변
2024.01.22
#21 그리드 강의 정렬 관련 속성
작성하신 코드의 상속 관계를 정확히 보여주시면 더 정확한 답변 드릴 수 있을 것 같습니다~
- 0
- 2
- 73
질문&답변
2024.01.22
재밌게 잘 듣고있어요~
safeArea님 안녕하세요. 네 학습 순서는 괜찮은 것 같습니다. 다만 자바스크립트로 개발 경험이 많지 않으시다면, 딥다이브 강의가 조금 어려울 순 있을 것 같습니다만, 워낙 좋은 강의이니 딥다이브 책과 강의는 교양처럼 개발하시면서 공부하셔도 좋을 것 같아요~ 최종적으로 리액트로 포폴을 만들고 싶으신거라면, 이 강의 들으시고 난 다음 리액트 강의를 기초부터 다양하게 들어보시면 더 좋을 것 같습니다. ^^ 그리고 ES는 자바스크립트 표준 규격이고, 크롬은 자바스크립트를 포함하고 있기 때문에 작동은 걱정하지 않으셔도 됩니다~ https://www.w3schools.com/js/js_versions.asp https://caniuse.com/
- 0
- 1
- 92
질문&답변
2023.12.18
bmi 계산기 css가 적용이 안됩니다
안녕하세요 지만님. 해당 오류는 경로가 잘못 되었을 경우 발생합니다. 작업 중인 폴더 /common/reset.css 경로에 해당 파일이 존재하는지 확인해보시고, 만약 공용으로 사용하는 파일을 추가하지 않으셨다면 추가해주시면 됩니다. https://github.com/erumcoding/js-project-101/tree/main/common
- 0
- 1
- 131
질문&답변
2023.11.13
trello-app에서 질문 있습니다
duswlskfk42님 안녕하세요. 먼저, dragOver 에서 말하는 타겟이란, 드래그-오버 되고 있는 대상을 의미하는데요. 이 이벤트는 이벤트 리스너가 달려있는 블록에서 드래그가 이루어지면 발생합니다. (현재 각각 todo, doing, done 리스트 블록) 또한 listIds 는 애초 리스트의 이름 (아이디)를 뽑아내기 위해 Object.keys 를 사용해 가져오는 것이므로, todo, doing, done만 나오는 것이 맞습니다. 결론적으로, 할 일을 드래그 해서, listIds 값 중 하나와 동일한 아이디를 가진 블록 위로 드래그 한다면, to 에 해당 리스트의 아이디 값을 대입해 도착지를 그곳으로 설정하게 되고, 드래그가 끝난 시점에 출발지와 도착지가 다른 경우에만 dragEnd 함수의 동작을 통해 요소가 이동 (실제로는 출발지에서 삭제 후 도착지에서 새롭게 생성)하게 됩니다. 즉, 올바르게 블록 단위로 드래그 되어 타겟 아이디가 리스트 블록의 아이디 중 하나를 갖지 않는다면, uuid 는 당연히 listIds 에 없기 때문에 이동하지 않습니다. 그래서 반드시 다른 블록 (이벤트 리스너가 달려 있는 리스트 블록) 안에 놓아야만 이동하는 것으로 구현이 됩니다. 다음 코드를 테스트 해보시면 좋을 것 같습니다. const dragOver = (event) => { event.preventDefault(); const { id: targetId } = event.target; const listIds = Object.keys(lists); console.log(event.currentTarget.id); if (listIds.includes(targetId)) { to = targetId; } }; 만약 왜 내부 할 일 항목에서도 이벤트가 발생하는 것처럼 보이는지 이 부분을 더 자세히 알고 싶으시다면, 자바스크립트의 이벤트 전파 방식 부분, Event Target과 Current Target의 차이점 등을 함께 공부하시면 도움이 될 것 같습니다.
- 0
- 1
- 156
질문&답변
2023.11.01
깃허브 자료에 bmi 계산기 문의
안녕하세요 그레이룩쇼님! 파일 정상임을 확인했습니다. index.html 파일 상단에 다음과 같이 2개의 스타일시트 관련 코드가 있습니다. 참고로 common 파일은 공통으로 쓰는 부분입니다! 확인 결과 (사진)링크 https://github.com/erumcoding/js-project-101/tree/main/common https://github.com/erumcoding/js-project-101/blob/main/01-exercise/03-bmi-cal/style.css
- 0
- 1
- 116
질문&답변
2023.10.10
해결되지않는 부분이 있어서 질문드려요
다른 글에 답변 드렸습니다. 혹시 같은 문제가 있으신 분들은 해당 글을 참고해주세요! https://www.inflearn.com/questions/1041836
- 0
- 3
- 146
질문&답변
2023.10.10
아 네~다시 질문드립니다.
우선, init() 함수에서 만약 로컬스토리지에 저장된 todos 가 있다면 그것을 파싱 해오는데, 그것을 페이지 내에서 조작하기 위해서 사용하는 글로벌 변수 todos 에 넣는 과정이 없습니다. 그렇기 때문에 로컬스토리지에는 있고, 글로벌 변수에는 없어서, 마치 제대로 저장되고 복원 되는 것처럼 보이지만, 조작하면 (조작하는 대상은 글로벌 변수 todos 이므로) 문제가 생기게 됩니다. // 페이지 내에서 조작하는 todos는 글로벌 변수인 todos 입니다. let todos = []; const init = () => { // 이 친구는 어디로? const userTodos = JSON.parse(localStorage.getItem('todos')); if (userTodos) { userTodos.forEach((todo) => { addItem(todo); }); // 아래 부분을 추가해 주어야 합니다. todos = userTodos; } }; 또한 init() 함수에서 로컬스토리지에 저장된 항목이 있음에도 불구하고 todoCount.textContent 를 0으로 초기화 하고 있습니다. 추측컨데, 이 기능이 올바르게 작동하려면 0으로 초기화 하는 것이 아니라, 저장된 개수를 세서 넣어줘야 할 것으로 보입니다. const init = () => { const userTodos = JSON.parse(localStorage.getItem('todos')); if (userTodos) { userTodos.forEach((todo) => { addItem(todo); }); todos = userTodos; // 다음과 같이 수정되어야 합니다. updateTodoCount(); } }; 추가로, 더 정확히는, 이 기능은 저장된 배열이 있던 없던 저장된 항목의 개수를 세어 표기하는 기능으로 보이기 때문에, 다음과 같이 if 문 밖에 작성되어야 할 것 같습니다. 그래야 저장된 항목이 없어도 0 task 라고 표기됩니다. const init = () => { const userTodos = JSON.parse(localStorage.getItem('todos')); if (userTodos) { userTodos.forEach((todo) => { addItem(todo); }); todos = userTodos; } updateTodoCount(); }; 세 번째로, 전체 삭제 함수에서 임의로 테스크 개수를 0이라고 덮어씌울 필요없이, 이미 글로벌 변수 todos 의 모든 내용을 지웠기 때문에 그냥 개수를 세는 함수를 호출하시면 됩니다. clearAll.addEventListener('click', () => { ul.innerHTML = ''; todos = []; localStorage.removeItem('todos'); updateTodoCount(); }); 네 번째로, 이미 이벤트 핸들러에서 input.value !== '' 임을 체크하고 공백이 아닌 것들만 addItem() 함수를 실행하고 있기 때문에, 중복으로 검증하실 필요는 없습니다. 따라서 if 문을 핸들러 안에 두신다면 다음과 같이 수정하셔도 됩니다. const addItem = (todo) => { const li = document.createElement('li'); const span = document.createElement('span'); const icon = document.createElement('i'); d icon.classList.add('fa-solid', 'fa-trash-can'); span.innerText = todo.text; icon.addEventListener('click', delItem); ul.appendChild(li); li.appendChild(span); li.appendChild(icon); li.id = todo.id; updateTodoCount(); }; 수정된 전체 코드 const form = document.querySelector('form'); const input = document.querySelector('input'); const ul = document.querySelector('ul'); const todoCount = document.querySelector('.todo-count'); const clearAll = document.querySelector('.clear-all'); let todos = []; const save = () => { localStorage.setItem('todos', JSON.stringify(todos)); }; const updateTodoCount = () => { todoCount.textContent = todos.length; }; const clearItems = () => { ul.innerHTML = ''; todos = []; localStorage.removeItem('todos'); updateTodoCount(); }; const delItem = (event) => { const target = event.target.parentElement; todos = todos.filter((todo) => todo.id !== parseInt(target.id)); save(); target.remove(); updateTodoCount(); }; const addItem = (todo) => { const li = document.createElement('li'); const span = document.createElement('span'); const icon = document.createElement('i'); icon.classList.add('fa-solid', 'fa-trash-can'); span.innerText = todo.text; icon.addEventListener('click', delItem); ul.appendChild(li); li.appendChild(span); li.appendChild(icon); li.id = todo.id; updateTodoCount(); }; const handler = (event) => { event.preventDefault(); const todo = { id: Date.now(), text: input.value, }; if (input.value !== '') { todos.push(todo); addItem(todo); save(); input.value = ''; } }; const init = () => { const userTodos = JSON.parse(localStorage.getItem('todos')); if (userTodos) { userTodos.forEach((todo) => addItem(todo)); todos = userTodos; } updateTodoCount(); }; init(); form.addEventListener('submit', handler); clearAll.addEventListener('click', clearItems);
- 0
- 1
- 120
질문&답변
2023.10.08
해결되지않는 부분이 있어서 질문드려요
안녕하세요 gangsugi님! AI가 답변을 달아서 그런지, 제가 실수로 읽음 처리를 한 건지 ㅠㅠ 알림이 누락되어 이제야 확인했습니다. 답변이 늦어 죄송합니다. 혹시 해결 되셨을까요? 아직 해결되지 않으셨다면 html 파일을 포함하여 전체 코드를 올려주시면 확인 후 최대한 빨리 답변 업데이트 하도록 하겠습니다. (공부하시면서 코드를 약간 수정하신 듯 하여, 정확히 답변을 드리기 위해 전체 코드가 필요합니다. 그리고 '새로고침 후 화면 상에는 제대로 되는데 로컬스토리지는 무조건 삭제가 된다'가 어떤 상황인지 조금만 더 자세히 설명 부탁드립니다!)
- 0
- 3
- 146
질문&답변
2023.09.30
filter 영상에서 질문이 있습니다
티라미수님 안녕하세요. A1 먼저 HTMLCollection 은 유사 배열 객체이고, Iterable입니다. console.log(typeof e.target.options); 따라서 정확히 배열 객체는 아니나, 배열 객체와 같은 방식으로 접근 (인덱스를 사용해 접근하는 등)하거나 반복할 수 있고, 구조분해할당을 사용할 수 있습니다. 하지만 유사 배열 객체니까 map 같은 건 바로 사용할 수 없습니다. A2 두 번째 질문은 잘 이해가 가지 않습니다. 우선 올바르게 사용하고 계십니다…! 혹시 구조분해할당을 헷갈리신 것은 아닐까요? ‘한 번에 가져온다’라고 표현하시는 부분에 구조분해할당이 적용되어 있습니다. console.dir(e.target.options[selectedIndex]); 하나의 옵션 객체를 열어서 그 안에 구조를 보시면, 이 안에 value 가 있고, 현재 그 값을 가져오려고 하고 있습니다. 즉, 객체.value 를 가져오려고 하기 때문에, 이를 구조분해할당하여 바로 가져온 것입니다. (이는 selectedIndex 를 e.target.options 객체에서 가져온 것과 같은 방법입니다.) 같은 방법으로, 만약 동일한 객체의 text 속성도 가져오고 싶다면 다음과 같이 쓸 수 있습니다. const { value, text } = e.target.options[selectedIndex]; console.log(value, text); 연휴 잘 보내고 계신가요? 답변이 공부하시는 데 도움이 되길 바랍니다. 행복한 연휴 보내세요~
- 0
- 1
- 140