JavaScript 

1. 모달 띄우기 버튼 클릭 시 모달창 활성화, 모달창 닫기 버튼 클릭시 비활성화

변수명에 element 저장

const modal = document.querySelector('.modal');
const overlay = document.querySelector('.overlay');
const btn_modal = document.querySelector('#btn-modal');
const btn_close = document.querySelector('.btn-close');
const close_elements = [btn_close, overlay];

https://print-blue.tistory.com/186

 

[Javascript] Element.classList 속성, 자바스크립트로 클래스 추가/제거 하기

Element.classList 속성다양한 메서드를 통해 클래스를 간편하게 제어할 수 있다 !classList 자체는 읽기 전용 속성이지만 add() 와 remove() 메서드를 이용해 변형할 수 있다 ! 1. add()classList.add('클래스명')

print-blue.tistory.com

[ 모달 : 활성화 ] 

1-1. btn_modal(모달창 띄우기 버튼) 클릭 했을 때 이벤트 설정

btn_modal.addEventListener('click', () => { // 콜백함수 
  modal.classList.add('active');    // modal 에 active 클래스를 추가
  overlay.classList.add('active') ; // overlay 에 active 클래스를 추가
});

모달창 띄우기 버튼 클릭시 active class 추가된 모습과 모달창이 띄워진 모습 !

[ 모달 : 비활성화 ] 

1-2. btn_close(모달창 x 버튼) or overlay(버튼 외) 클릭 했을 때 이벤트 설정

btn_close.addEventListener('click', () => {
  modal.classList.remove('active'); // modal 에 active 클래스를 제거
  overlay.classList.remove('active') ; // overlay 에 active 클래스를 제거
});
overlay.addEventListener('click', () => {
  modal.classList.remove('active'); // modal 에 active 클래스를 제거
  overlay.classList.remove('active') ; // overlay 에 active 클래스를 제거
});

 

이렇게 코드 작성하면 작동은 되지만 중복된 코드가 보인다 ! 함수로 btn_close 와 overlay 속성을 합쳐주자

const close_elements = [btn_close, overlay];

close_elements.forEach(element => {
  console.log('element >>', element)
});

 

확인용 !

const close_elements = [btn_close, overlay];

close_elements.forEach(element => {
  element.addEventListener('click', () => { 
    modal.classList.remove('active');   // modal 에 active 클래스를 제거
    overlay.classList.remove('active'); // overlay 에 active 클래스를 제거
  })
});

 

이렇게 되면 ! 모달창 x 버튼을 클릭해도, 모달창 이외에 overlay 를 클릭해도 ! 

modal 과 overlay 에 active 클래스 제거가 되면서 잘 된다 !

 

1-3. 모달창에 텍스트(이메일) 입력 후 닫고 다시 열었을 때 텍스트 초기화

const inputData = document.querySelector("input[type='email']");
const modal_input = document.querySelector('.modal input');
// [모달창 활성화]
btn_modal.addEventListener('click', () => { 
  modal.classList.add('active'); 
  overlay.classList.add('active') ; 
  
  modal_input.value = null; // 모달창 입력값 비우기
});

 


2. 상품명 입력 후 등록

변수명에 element 저장

const form = document.querySelector('form');
const sign_up = document.querySelector('.sign-up');
const input = document.querySelector('input');
const ul = document.querySelector('ul');

text box(상품명 입력창)에 입력 후, 등록 버튼 클릭하면 항목(li) 추가

form 은 text 입력 후 엔터를 치면 기본적으로 submit 이 되는데,

현재 form 태그에 action 을 설정을 안해서 티가 안날 뿐 페이지 요청을 하게 된다.

하지만 ! server 에 페이지 요청 하고 싶지 않기 때문에 자바스크립트를 사용해서 요청하지 않을 것 !

 

자바스크립트에 perventDefault() 를 사용해서 refresch (새로고침) 방지 즉, submit 기능을 삭제한다.

form.addEventListener('submit', (event) => { // submit : 엔터를 치면
  console.log('입력했다 !!');
});

 

페이지 요청을 했고 현재 페이지가 요청되면서 새로 고침이 됐기 때문에 콘솔창에 log 가 안뜬다 !

페이지 요청된 것이 url 를 보면 알 수 있다

1) li element 를 생성

form.addEventListener('submit', (event) => { // submit : 엔터를 치면
  // 1) refresch(새로 고침) 방지
  event.preventDefault();
  console.log('입력했다 !!');
});

 

새고 고침 방지와 submit 기능을 삭제 했기 때문에 페이지 요청 하지 않아 새로 고침 되지 않기 때문에

콘솔창에 log 가 잘 뜬다 !

 

 

그럼 이제 li element 를 생성 -> li element 에 content 추가 -> ul 의 자식 li element 추가 해보자

2-1) li element 를 생성

form.addEventListener('submit', (event) => { // submit : 엔터를 치면
  // 1) refresch(새로 고침) 방지
  event.preventDefault(); 
  console.log('입력했다 !!');
  // 2-1) li element 생성
  const li = document.createElement('li');
  console.log(li);
});

2-2) li element 에 content 추가

form.addEventListener('submit', (event) => { // submit : 엔터를 치면
  // 1) refresch(새로 고침) 방지
  event.preventDefault(); 
  console.log('입력했다 !!');
  // 2-1) li element 생성
  const li = document.createElement('li');
  console.log(li);
  // 2-2) li element 에 content 추가
  console.log(input.value);
  li.innerText = input.value;
});

2-3) ul 의 자식 li element 추가

form.addEventListener('submit', (event) => { // submit : 엔터를 치면
  // 1) refresch(새로 고침) 방지
  event.preventDefault(); 
  console.log('입력했다 !!');
  
  // 2-1) li element 생성
  const li = document.createElement('li');
  console.log(li);
  // 2-2) li element 에 content 추가
  console.log(input.value);
  li.innerText = input.value;
});

3) input(텍스트 박스)에 입력된 문자열 삭제

form.addEventListener('submit', (event) => { // submit : 엔터를 치면
  // 1) refresch(새로 고침) 방지
  event.preventDefault(); 
  console.log('입력했다 !!');
  
  // 2-1) li element 생성
  const li = document.createElement('li');
  console.log(li);
  // 2-2) li element 에 content 추가
  console.log(input.value);
  li.innerText = input.value;
  
  // 3) input(텍스트 박스)에 입력된 문자열 삭제
  input.value = '';
  input.value = null;
});

 

 

입력창에 텍스트 입력 엔터 했을 때 li 가 추가된 모습

 

지금까지의 전체 코드 !

// 변수명에 element 저장
// 1
const modal = document.querySelector('.modal');
const overlay = document.querySelector('.overlay');
const btn_modal = document.querySelector('#btn-modal');
const btn_close = document.querySelector('.btn-close');
const close_elements = [btn_close, overlay];
// 2
const form = document.querySelector('form');
const sign_up = document.querySelector('.sign-up');
const input = document.querySelector('input');
const ul = document.querySelector('ul');
// 1. 모달 띄우기 버튼 클릭 시 모달창 활성화, 모달창 닫기 버튼 클릭시 비활성화
[모달 : 활성화]
1-1. btn_modal(모달창 띄우기 버튼) 클릭 했을 때 이벤트 설정
btn_modal.addEventListener('click', () => { // 콜백함수 
  modal.classList.add('active');    // modal 에 active 클래스를 추가
  overlay.classList.add('active') ; // overlay 에 active 클래스를 추가
});  

[모달 : 비활성화]
1-2. btn_close(모달창 x 버튼) or overay(버튼 외) 클릭 했을 때 이벤트 설정
close_elements.forEach(element => {
  element.addEventListener('click', () => { 
    modal.classList.remove('active');   // modal 에 active 클래스를 제거
    overlay.classList.remove('active'); // overlay 에 active 클래스를 제거
  })
});

1-3. 모달창에 텍스트(이메일) 입력 후 닫고 다시 열었을 때 텍스트 초기화
// 코드 복붙하기
2. 상품명 입력 후 등록
[구현 1]
text box(상품명 입력창)에 입력 후, 등록 버튼 클릭하면 항목(li) 추가 
form.addEventListener('submit', (event) => { // submit : 엔터를 치면
   // 1) refresch(새로 고침) 방지
   event.preventDefault(); 
  
   // 2-1) li element 생성
   const li = document.createElement('li');
   // 2-2) li element 에 content 추가
   li.innerText = input.value;
   // 2-3) ul 의 자식 li element 추가
   ul.appendChild(li);
  
   // 3) input(텍스트 박스)에 입력된 문자열 삭제
   input.value = '';
 });

 


구현된 코드 리팩토링 !

현재 코드에서 기능이 많아지면서 분리하는게 좋겠다 생각 들어서 리팩토링 들어갑니당

listener, handle 그리고 li 기능을 분리할 것 !

 

명확한 기능이 있는 경우 따로 빼주는 것이 좋은데, 다른 작업하면서 또 쓸 수 있기 때문이다 !

input.value 보다는 호출할 때 매개변수를 그니까 li 에 content 를 넣어주는 거니까 이렇게 코드 작성하는 것이 추후 작업할 때 편하다

const addLi = (content) => {
  const li = document.createElement('li');
  li.innerText = content;
  ul.appendChild(li);
}

 

보통 무슨 handler 인지 지정해주면 좋지만, 하나만 있어서 그냥 handler 라 지정 !

근데 코드를 막 작성하는 것이 아니라 생각을 하고 ! 순서대로 해야된다.

일단 새로 고침 후 li 추가 한 다음에 텍스트 박스에 입력된 문자열을 삭제해줘야한다.

const handler = (event) => { 
  event.preventDefault(); 
  addLi(input.value);
  input.value = '';
}

 

근데 또 ! 여기서 또 생각해야할 점은

사용자가 텍스트를 입력하지 않고 엔터를 쳤을 때도 li 를 추가하기 때문에 이것 또한 방지 해야한다.

const handler = (event) => { 
  event.preventDefault(); 
  if (input.value !== '') { // type 비교까지 하기 위해 사용
    addLi(input.value);
    input.value = '';
  }
}
form.addEventListener('submit', handler);

 

하지만 !! form 전체보다는 등록 버튼에 등록하는 것이 명확하다.

// form.addEventListener('submit', handler);
sign_up.addEventListener('click', handler);

지금까지의 전체 코드 !

[구현 2] refactoring
위 코드 분리 : listener, handler, li 기능 분리
[함수] li 추가 기능
const addLi = (content) => {
    const li = document.createElement('li');
    li.innerText = content;
	ul.appendChild(li);
}
[handler]
const handler = (event) => { 
    event.preventDefault(); 
    if (input.value !== '') { // type 비교까지 하기 위해 사용
    	addLi(input.value);
	}
	input.value = '';
}
[listener]
form.addEventListener('submit', handler);
sign_up.addEventListener('click', handler);

리팩토링한 코드에 기능 추가 !

1. 삭제버튼

1-1. li element 가 추가되면, 삭제 버튼도 같이 추가

li 가 생성되면서 ul 에 자식으로 들어오고, button 이 생성되면서 li 자식으로 들어오는 형태 ! 

근데 여기서 li content 에 button 을 추가될건데 그냥 content 보다는 span 으로 감싸는 것이 좋다 !

// [함수] li 추가 기능
const addLi = (content) => {
  const li = document.createElement('li'); 
  ul.appendChild(li);
  
  // span element 생성 후 추가
  const span_content = document.createElement('span');
  span_content.innerText = content;

  // 삭제 버튼 생성 후 추가
  const btn_cross = document.createElement('button');

  // li 자식으로 span, button 한 번에 추가
  li.append(span_content, btn_cross); // 순서대로 들어옴
}

현재 css 를 미리 적용해서 ::before 로 되어 있는 것 !

1-2. 삭제 버튼을 클릭하면, li element 삭제

// 삭제 기능
const deleteLi = () => {
  console.log('삭제 버튼 클릭됨 !');
}
// [함수] li 추가 기능
const addLi = (content) => {
  const li = document.createElement('li'); 
  ul.appendChild(li);
  
  // span element 생성 후 추가
  const span_content = document.createElement('span');
  span_content.innerText = content;

  // 삭제 버튼 생성 후 추가
  const btn_cross = document.createElement('button');

  // 삭제 버튼 : listener 에 handler 등록
  btn_cross.addEventListener('click', deleteLi)

  // li 자식으로 span, button 한 번에 추가
  li.append(span_content, btn_cross); // 순서대로 들어옴
}

 

근데 삭제 기능을 만들 때도 ! 무작정 삭제가 아닌 해당 li 만 삭제 해야된다.

// 삭제 기능
const deleteLi = (event) => {
  console.log('삭제 버튼 클릭됨 !');

  console.log(event.target.parentElement);
  const target = event.target.parentElement; // 삭제하고자 하는 element 만 삭제

  target.remove();
}
// [함수] li 추가 기능
const addLi = (content) => {
  const li = document.createElement('li'); 
  ul.appendChild(li);
  
  // span element 생성 후 추가
  const span_content = document.createElement('span');
  span_content.innerText = content;

  // 삭제 버튼 생성 후 추가
  const btn_cross = document.createElement('button');

  // 삭제 버튼 : listener 에 handler 등록
  btn_cross.addEventListener('click', deleteLi); // li button 클릭하면 이 코드 실행

  // li 자식으로 span, button 한 번에 추가
  li.append(span_content, btn_cross); // 순서대로 들어옴
}

 

2. local storage

https://print-blue.tistory.com/187

 

개발자 도구 [Application]-[web local storage] 저장하기/가져오기

웹 브라우저는 Application 에 web local storage 에 정보가 남아있는데, 여기서 가져올 것 ! 가져오는 함수가 있음 !단점 : 다른 웹 브라우저 사용시 보여지지 않음 [저장하기 / 가져오기]localStorage.s

print-blue.tistory.com

2-1. li content 를 web local storage 에 저장

key 값은 하나로 하고 value 에 상품들을 넣어줄건데
각 상품마다 특정 구분도 해줄 거임(생성날짜+시간)

현재 프로젝트에서는 key 값으로 쓸만한게 없어서 Date.now() 로 사용할 것 !

// [handler]
let li_items = [];

const handler = (event) => { 
  event.preventDefault(); 
  if (input.value !== '') { // type 비교까지 하기 위해 사용
    const li_item = { // 2-1) li content 를 web local storage 에 저장
      id : Date.now(),
      content : input.value,
    };

    // console.log(li_item);
    li_items.push(li_item);
    // addLi(input.value);
    addLi(li_item.content);
  }
  input.value = '';
}

 

addLi(li_item.content);

 

값 가져오는 거 잊지 않기 !

이렇게 되면 안된당

 

그럼 다시 코드를 수정해줘야한다 . . .

// 삭제 기능 -> 변경사항 없음
const deleteLi = (event) => {
  console.log('삭제 버튼 클릭됨 !');

  const target = event.target.parentElement; // 삭제하고자 하는 element 만 삭제
  target.remove();
}
// [함수] li 추가 기능
const addLi = (li_item) => { // content -> li_item 수정
  const li = document.createElement('li'); 
  ul.appendChild(li);
  
  // span element 생성 후 추가
  const span_content = document.createElement('span');
  span_content.innerText = li_item.content; // content -> li_item 수정

  // 삭제 버튼 생성 후 추가
  const btn_cross = document.createElement('button');

  // 삭제 버튼 : listener 에 handler 등록
  btn_cross.addEventListener('click', deleteLi); // li button 클릭하면 이 코드 실행

  // li 자식으로 span, button 한 번에 추가
  li.append(span_content, btn_cross); // 순서대로 들어옴
}
// [handler]
let li_items = [];

const handler = (event) => { 
  event.preventDefault(); 
  if (input.value !== '') { // type 비교까지 하기 위해 사용
    const li_item = { // 2-1) li content 를 web local storage 에 저장
      id : Date.now(),
      content : input.value,
    };

    li_items.push(li_item);
    addLi(li_item); // input.value -> li_item 변경
  }
  input.value = '';
}

 

그럼 이제 상품을 추가할 때마다 새롭게 저장되어야 한다.

 

 

local storage 에 저장하는 함수를 만들어보자 !

배열 안에 객체가 들어있고, 직렬화해주자

// 삭제 기능
const deleteLi = (event) => {
  console.log('삭제 버튼 클릭됨 !');

  const target = event.target.parentElement; // 삭제하고자 하는 element 만 삭제
  target.remove();
}
// [함수] li 추가 기능
const addLi = (li_item) => {
  const li = document.createElement('li'); 
  ul.appendChild(li);
  
  // span element 생성 후 추가
  const span_content = document.createElement('span');
  span_content.innerText = li_item.content;

  // 삭제 버튼 생성 후 추가
  const btn_cross = document.createElement('button');

  // 삭제 버튼 : listener 에 handler 등록
  btn_cross.addEventListener('click', deleteLi); // li button 클릭하면 이 코드 실행

  // li 자식으로 span, button 한 번에 추가
  li.append(span_content, btn_cross); // 순서대로 들어옴

  // local storage 에 저장된 요소 삭제할 때
  // 기준으로 사용될 id 지정
  li.id = li_item.id;
  console.log('매개변수 li_item >>', li_item);
}
// [handler]
let li_items = [];

// local storage 에 저장하는 함수
const storage_save = () => {
  localStorage.setItem('li_items', JSON.stringify(li_items)); // 배열안에 객체가 들어가 있고, 직렬화 해주기
};

const handler = (event) => { 
  event.preventDefault(); 
  if (input.value !== '') { // type 비교까지 하기 위해 사용
    const li_item = { // li content 를 web local storage 에 저장
      id : Date.now(),
      content : input.value,
    };

    li_items.push(li_item);
    // addLi(input.value);
    addLi(li_item);
    storage_save(); 
  }
  input.value = '';
}

 

id 가 설정 됐다 !

 

2-2. 삭제 버튼 클릭하면, web browser 에 저장된 데이터 삭제

// 삭제 기능
const deleteLi = (event) => {
  console.log('삭제 버튼 클릭됨 !');

  const target = event.target.parentElement; // 삭제하고자 하는 element 만 삭제

  // 삭제 버튼 클릭된 element id 확인
  console.log(target.id);

  target.remove();
}

 

// 삭제 버튼 클릭된 element 제외한 나머지 추출
li_items = li_items.filter((item) => {
console.log('item >> ', item);
console.log('item.id > > ', item.id);
console.log('item.id type> > ', typeof item.id);
console.log('target.id >>', target.id);
console.log('target.id type> > ', typeof target.id);
});

 

순회를 하면서 다시 시간이 찍히기 때문에 id 값이 다른 것 !

li_items = li_items.filter((item) => {
console.log('item >> ', item);
console.log('item.id > > ', item.id);
console.log('item.id type> > ', typeof item.id);
console.log('target.id >>', target.id);
console.log('target.id type> >> ', typeof target.id);
console.log('item.id != target.id >> ', item.id != target.id);
return item.id != target.id;
});

 

배열에 삭제된 아이디 제외하고 잘 담아왔는데 확인

  // 삭제 버튼 클릭된 element 제외한 나머지 추출
  li_items = li_items.filter((item) => {
    console.log('item >> ', item);
    console.log('item.id > > ', item.id);
    console.log('item.id type> > ', typeof item.id);
    console.log('target.id >>', target.id);
    console.log('target.id type> >> ', typeof target.id);
    console.log('item.id != target.id >> ', item.id != target.id);
    return item.id != target.id;
  });
  console.log('삭제 후 li_items >> ', li_items);
  target.remove();

 

이제 로컬 스토리지에 반영하자 . . .

const deleteLi = (event) => {
  console.log('삭제 버튼 클릭됨 !');

  const target = event.target.parentElement; // 삭제하고자 하는 element 만 삭제

  // 삭제 버튼 클릭된 element id 확인
  console.log(target.id);

  // 삭제 버튼 클릭된 element 제외한 나머지 추출
  li_items = li_items.filter((item) => {
    console.log('item >> ', item);
    console.log('item.id > > ', item.id);
    console.log('item.id type> > ', typeof item.id);
    console.log('target.id >>', target.id);
    console.log('target.id type> >> ', typeof target.id);
    console.log('item.id != target.id >> ', item.id != target.id);
    return item.id != target.id;
  });
  console.log('삭제 후 li_items >> ', li_items);
  target.remove();

  // local storage 에 저장
  storage_save();
}

로컬 스토리지에 삭제 된게 확인됨 !

3) 새로 고침해도 local storage 에 있는 값들 보여지게 하기

새로 고침 된다는 것은 HTML 도 CSS 도 JS 도 다시 코드를 읽는다.

 

새로 고침해도 ! 저장된 값이 있는 경우 보여지게 하는 작업을 초기화 작업이라고 한다

페이지가 로드 될 때, 로컬 스토리지에 저장된 데이터 가져와보자

const init = () => {
  const local_storage_data = JSON.parse(localStorage.getItem('li_items'));
  console.log('local_storage_data >> ', local_storage_data);

  console.log('[전] li_items >> ', li_items);
  li_items = local_storage_data
  console.log('[후] li_items >> ', li_items);
};

init();

 

현재 로컬 스토리지는 2개 저장되어 있다

잘 출력되는군 !

아까 li 추가 기능을 따로 함수에 만들었으니까 써먹자 !

이러면 새로 고침해도 ! 저장된 값이 보여질 거다

const init = () => {
  const local_storage_data = JSON.parse(localStorage.getItem('li_items'));
  console.log('local_storage_data >> ', local_storage_data);

  console.log('[전] li_items >> ', li_items);
  li_items = local_storage_data
  console.log('[후] li_items >> ', li_items);

  // li 추가
  li_items.forEach(value => addLi(value));
};

 

근데 또 문제가 있음

만약 로컬 스토리지에 아무것도 저장된 것이 없으면 에러가 발생함

 

배열 형태에서 타입이 변환이 되니까 막 코드 작성하면 안된다 ㅠ

확인용

https://print-blue.tistory.com/188

 

자바스크립트 논리값 확인하기

console.log('>');console.log('null : ', Boolean(null));console.log('0 : ', Boolean(0));console.log('-5 : ', Boolean(-5));console.log('빈 배열 [] : ', Boolean([]));console.log('빈 객체 [] : ', Boolean({}));

print-blue.tistory.com

 

local_storag_data 가 있을 때 없을 때 조건문 걸어서 저장하자

const init = () => {
  const local_storage_data = JSON.parse(localStorage.getItem('li_items'));
  console.log('local_storage_data >> ', local_storage_data);

  if(local_storage_data) {
    console.log('[전] li_items >> ', li_items);
    console.log('[전] li_items type >> ', typeof li_items);
    li_items = local_storage_data 
    console.log('[후] li_items >> ', li_items);
    console.log('[후] li_items type >> ', typeof li_items);
  
    // li 추가
    li_items.forEach(value => addLi(value));
  }
};

init();

 

그럼 모든 기능 끝 !

 

그럼 완성 코드 !

const modal = document.querySelector('.modal');
const overlay = document.querySelector('.overlay');
const btn_modal = document.querySelector('#btn-modal');
const btn_close = document.querySelector('.btn-close');
const close_elements = [btn_close, overlay];
const form = document.querySelector('form');
const sign_up = document.querySelector('.sign-up');
const input = document.querySelector('input');
const ul = document.querySelector('ul');

// 1
// [모달창 활성화]
btn_modal.addEventListener('click', () => { 
  modal.classList.add('active'); 
  overlay.classList.add('active') ; 
});  
// [모달창 비활성화]
close_elements.forEach(element => {
  element.addEventListener('click', () => { 
    modal.classList.remove('active');   
    overlay.classList.remove('active'); 
  })
});
// [모달창 텍스트 초기화]

// 2
// [삭제 기능]
const deleteLi = (event) => {
  const target = event.target.parentElement;

  li_items = li_items.filter((item) => {
    return item.id != target.id;
  });
  target.remove();
  storage_save(); 
}
// [li 추가]
const addLi = (li_item) => {
  const li = document.createElement('li'); 
  ul.appendChild(li);
  
  const span_content = document.createElement('span');
  span_content.innerText = li_item.content;

  const btn_cross = document.createElement('button');
  btn_cross.addEventListener('click', deleteLi); 

  li.append(span_content, btn_cross); 
  li.id = li_item.id;
}

// [handler]
let li_items = [];

const storage_save = () => {
  localStorage.setItem('li_items', JSON.stringify(li_items)); 
};

const handler = (event) => { 
  event.preventDefault(); 

  if (input.value !== '') {
    const li_item = { 
      id : Date.now(),
      content : input.value,
    };

    li_items.push(li_item);
    addLi(li_item);
    storage_save(); 
  }

  input.value = '';
}

// [listener]
sign_up.addEventListener('click', handler);

// [초기화 작업]
const init = () => {
  const local_storage_data = JSON.parse(localStorage.getItem('li_items'));

  if(local_storage_data) {
    li_items = local_storage_data 
    li_items.forEach(value => addLi(value));
  }
};

init();