TodoList 1단게
//HTML파일
<!DOCTYPE html>
<html lang="ko">
<head>
<title>ToDo</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<h1>ToDo</h1>
<div class="todo-container">
<input type="text" id="todo-input" onkeydown="keyCodeCheck()" />
<ul id="todo-list"></ul>
</div>
</body>
</html>
//자바스크립트 파일
const todoInput = document.querySelector("#todo-input");
const keyCodeCheck = function () {
if (window.event.keyCode === 13 && todoInput.value !== "") {
const todoList = document.querySelector("#todo-list");
const newLi = document.createElement("li"); //변수에 태그 요소를 담음
const newSpan = document.createElement("span");
newSpan.textContent = todoInput.value;
newLi.appendChild(newSpan);
todoList.appendChild(newLi);
todoInput.value = "";
}
};
//요약
window : 전역 객체(=최상위 객체)
document 객체는 window객체에 속한다.
window.event
window객체에는 event객체를 가지고 있다.
window 단어 생략가능
태그를 html에서 만들지 않고, 자바스크립트로 태그를 별도로 만듬
document.createElement('태그')
1단계
newLi.appendChild(newSpan)
→li태그 안에 span태그를 추가하겠다.
2단계
todoList.appendChild(newLi)
->ul태그 안에 li태그를 추가
TodoList 2단계
const todoInput = document.querySelector("#todo-input");
const createTodo = function () {
{
const todoList = document.querySelector("#todo-list");
const newLi = document.createElement("li"); //변수에 태그 요소를 담음
const newSpan = document.createElement("span");
const newBtn = document.createElement("button");
newBtn.addEventListener("click", () => {
newLi.classList.toggle("complete");
});
newSpan.textContent = todoInput.value;
newLi.appendChild(newBtn);
newLi.appendChild(newSpan);
todoList.appendChild(newLi);
todoInput.value = "";
}
};
const keyCodeCheck = function () {
if (window.event.keyCode === 13 && todoInput.value !== "") {
createTodo();
}
};
//CSS
* {
box-sizing: border-box;
}
h1 {
color: white;
text-shadow: 2px 2px gray;
opacity: 70%;
}
input {
opacity: 80%;
}
body {
background-image: url("./images/Clear.jpg");
background-size: cover;
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
min-height: 100vh;
padding: 0;
}
.todo-container {
max-width: 100%;
width: 400px;
}
#todo-input {
background-color: lightyellow;
border: none;
display: block;
font-size: 2rem;
padding: 0.5rem 2rem 0.5rem 0.5rem;
width: 100%;
}
#todo-list {
background-color: lightyellow;
list-style-type: none;
margin: 0;
padding: 0;
}
#todo-list li {
border-top: 1px solid reb(242, 242, 242);
font-size: 1.5rem;
user-select: none;
}
.complete {
color: rgb(155, 155, 155);
text-decoration: line-through;
}
li button {
background-color: mintcream;
width: 1.5rem;
height: 1.5rem;
margin: 0.5rem;
border: none;
border-radius: 5px;
cursor: pointer;
box-shadow: 3px 3px gray;
}
li button:active {
border: 2px solid grey;
}
.delete-btn-wrapper {
background-color: red;
margin-top: 1rem;
}
.delete-btn-wrapper button {
font-weight: bold;
background-color: antiquewhite;
padding: 0.2rem 1rem;
cursor: pointer;
}
.delete-btn-wrapper button:active {
box-shadow: none;
margin-left: 1px;
margin-top: 1px;
}
const newBtn = document.createElement("button");
( ) 괄호안에 생성하고자 하는 "태그"를 입력
변수.addEventListener('액션' , 함수이름)
액션: keydown, click
newLi.classList.toggle('complete')
→새로운 class선택자를 추가해주겠다.
→다만, toggle속성을 이용해서 한번 더
클릭하는 경우에는 해당 클래스 선택자를 삭제한다.
newLi.appendChild(newBtn)
→새로 만든 li태그 안에 button태그를 추가해 주겠다.
newLi.appendChild(newSpan)
→새로 만든 li태그 안에 span태그를 추가해 주겠다.
TodoList 3단계
//스크립트 파일
const todoInput = document.querySelector("#todo-input");
const todoList = document.querySelector("#todo-list");
const savedTodoList = JSON.parse(localStorage.getItem("saved-items"));
//savedTodoList의 데이터 타입은 배열!!
const createTodo = function (storageData) {
let todoContents = todoInput.value;
if (storageData) {
//로컬 스토리지에 저장된 데이터가 존재한다면
todoContents = storageData.contents;
}
const newLi = document.createElement("li"); //변수에 태그 요소를 담음
const newSpan = document.createElement("span");
const newBtn = document.createElement("button");
newBtn.addEventListener("click", () => {
newLi.classList.toggle("complete");
saveItemsFn();
});
newLi.addEventListener("dblclick", () => {
newLi.remove();
saveItemsFn();
});
if (storageData?.complete === true) {
newLi.classList.add("complete");
}
newSpan.textContent = todoContents; //★
newLi.appendChild(newBtn);
newLi.appendChild(newSpan);
todoList.appendChild(newLi);
todoInput.value = "";
saveItemsFn();
};
const keyCodeCheck = function () {
if (window.event.keyCode === 13 && todoInput.value.trim() !== "") {
createTodo();
}
};
const deleteAll = function () {
const liList = document.querySelectorAll("li");
for (let i = 0; i < liList.length; i++) {
liList[i].remove();
}
saveItemsFn();
};
const saveItemsFn = function () {
const saveItems = [];
for (let i = 0; i < todoList.children.length; i++) {
const todoObj = {
contents: todoList.children[i].querySelector("span").textContent,
complete: todoList.children[i].classList.contains("complete"),
};
saveItems.push(todoObj);
}
console.log(saveItems);
saveItems.length === 0
? localStorage.removeItem("saved-items")
: localStorage.setItem("saved-items", JSON.stringify(saveItems));
};
if (savedTodoList) {
//로컬 스토리지에 저장된 데이터가 존재한다면
for (let i = 0; i < savedTodoList.length; i++) {
createTodo(savedTodoList[i]);
}
}
버튼태그와 함수의 연결
<div class="delete-btn-wrapper">
<button onclick="deleteAll()">전체삭제</button>
</div>
const deleteAll = function () {
const liList = document.querySelectorAll("li");
for (let i = 0; i < liList.length; i++) {
liList[i].remove();
}
};
객체 만들기
const 객체이름 = { key:value }
const todoObj = {
contents: todoList.children[i].querySelector("span").textContent,
complete: todoList.children[i].classList.contains("complete"),
};
todoList(변수)의 자식의 span태그요소를 선택한 다음 text내용을 뽑아낸다.
contains() : 포함여부를 판별 반환타입 boolean
전역스코프(global scope) vs 지역스코프(local scope)
전역스코프에 있는 변수는 함수(지역) 내부에서 사용가능하다.
let x =0;
let y= 1;
const sccopeTest = function() {
let z =2;
console.log(x); //출력가능
console.log(y); //출력가능
};
console.log(x); //출력가능
console.log(y); //출력가능
지역스코프에 있는 변수는 "함수 내부에서만" 사용가능하며, 함수 밖에서는 사용할수 없다.
let x =0;
let y= 1;
const sccopeTest = function() {
let z =2;
console.log(x); //출력가능
console.log(y); //출력가능
};
console.log(z); //출력불가!!!
지역 스코프(local scope) →let 변수를 사용해야 되는 이유 { } 내부에서만 유효한 변수
1.함수 레벨 스코프 : 함수 내부
const sum = function() {
let x = 0;
}
console.log(x) =>x출력 불가
2.블록 레벨 스코프 : 조건문, 반복문 등의 중괄호{ } 내부
if() { let y =0;}
console.log(y) =>y출력불가!!!
지역 스코프(local scope) → var변수 블록 레벨 스코프를 따르지 않아서 bad case발생
1.함수 레벨 스코프 : 함수 내부
const sum = function() {
var x = 0;
}
console.log(x) =>x출력 불가
2.블록 레벨 스코프 : 조건문, 반복문 등의 중괄호{ } 내부
if() { var y =0;}
console.log(y) =>0 출력가능 (bad case!!!!!)
Scope changing
현재 실행되고 있는 local scope범위에서 변수를 찾아 보고,
변수가 없으면, 좀더 상위의 scope범위에서 변수를 찾아보고,
변수가 없으면 global scope범위에서 변수를 찾는다.
이러한 과정은 scope changing이라고 한다.
localStorage에는 '문자열'만 저장이 가능하다.
JSON 데이터 : 객체나 배열을 '문자열'을 변환시켜주는 "데이터 포맷"이 존재한다.
★JSON.stringify(객체이름 또는 배열이름) : 객체나 배열을 '문자열'로 변환
★JSON.parse(localStorage.getItem('saved-items')) : '문자열'을 다시 객체나 배열로 변환
localStorage.setItem('키(문자열)', 저장할 데이터)
localStorage.getItem('키(문자열)')
var vs let
console.log(varKeyword);
varKeyword ='var is not safe'
→undefined
→실행가능
→에러방지불가★
undefined인 이유
var varKeyword; ← 호이스팅
console.log(varKeyword);
varKeyword ='var is not safe'
console.log(letKeyword)
let letKeyword= "안녕하세요"
<출력결과>
→letKeyword is not define
→실행불가
→에러를 방지할 수 있음
--------------------------------
선언식 vs 표현식
선언식으로 함수를 작성한 경우- 사용X
fn1()
function fn1() {
console.log("hoistirng occurred")
}
→ 호이스팅(hoisting)이 되어서
→fn1() 이 잘 실행됨
→ 에러방지 불가
표현식으로 함수를 작성한 경우
const fn2 =function() {
console.log("error occured")
}
→ fn2 is not defined
→실행불가
→에러방지가능!!
-------------------------------------
옵셔널 체이닝
if (storageData?.complete === true) {
newLi.classList.add("complete");
}
옵셔널 체이닝과 동일한 표현
if (storageData && storageData.complete === true) {
newLi.classList.add("complete");
}
메카니즘 : storageData가 null 또는 undefined된 경우 작동X
TodoList 4단계(완성)
<!DOCTYPE html>
<html lang="ko">
<head>
<title>ToDo</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<h1 id="location-name-tag">ToDo</h1>
<div class="todo-container">
<input type="text" id="todo-input" onkeydown="keyCodeCheck()" />
<ul id="todo-list"></ul>
</div>
<div class="delete-btn-wrapper">
<button onclick="deleteAll()">전체삭제</button>
</div>
</body>
</html>
* {
box-sizing: border-box;
}
body {
background-image: url("./images/Clear.jpg");
background-size: cover;
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
min-height: 100vh;
}
h1 {
color: white;
text-shadow: 2px 2px gray;
opacity: 80%;
}
input {
opacity: 80%;
}
.todo-container {
max-width: 100%;
width: 400px;
}
#todo-input {
background-color: lightyellow;
border: none;
display: block;
font-size: 2rem;
padding: 0.5rem 2rem 0.5rem 0.5rem;
width: 100%;
}
#todo-list {
background-color: lightyellow;
list-style-type: none;
margin: 0;
padding: 0;
}
#todo-list li {
border-top: 1px solid rgb(242, 242, 242);
font-size: 1.5rem;
user-select: none;
}
.complete {
color: rgb(155, 155, 155);
text-decoration: line-through;
}
li button {
background-color: mintcream;
width: 1.5rem;
height: 1.5rem;
margin: 0.5rem;
border: 2px solid black;
border-radius: 8px;
cursor: pointer;
}
li button:active {
border: 2px solid grey;
}
.delete-btn-wrapper {
margin-top: 1rem;
}
.delete-btn-wrapper button {
font-weight: bold;
border: none;
background-color: antiquewhite;
padding: 0.2rem 1rem;
cursor: pointer;
border-radius: 5px;
box-shadow: 3px 3px gray;
}
.delete-btn-wrapper button:active {
box-shadow: none;
margin-left: 3px;
margin-top: 3px;
}
const todoInput = document.querySelector("#todo-input");
const todoList = document.querySelector("#todo-list");
const savedWeatherData = JSON.parse(localStorage.getItem("saved-weather"));
const savedTodoList = JSON.parse(localStorage.getItem("saved-items"));
const createTodo = function (storageData) {
let todoContents = todoInput.value;
if (storageData) {
todoContents = storageData.contents;
}
const newLi = document.createElement("li");
const newSpan = document.createElement("span");
const newBtn = document.createElement("button");
newBtn.addEventListener("click", () => {
newLi.classList.toggle("complete");
saveItemsFn();
});
newLi.addEventListener("dblclick", () => {
newLi.remove();
saveItemsFn();
});
if (storageData?.complete) {
newLi.classList.add("complete");
}
newSpan.textContent = todoContents;
newLi.appendChild(newBtn);
newLi.appendChild(newSpan);
todoList.appendChild(newLi);
todoInput.value = "";
saveItemsFn();
};
const keyCodeCheck = function () {
if (window.event.keyCode === 13 && todoInput.value.trim() !== "") {
createTodo();
}
};
const deleteAll = function () {
const liList = document.querySelectorAll("li");
for (let i = 0; i < liList.length; i++) {
liList[i].remove();
}
saveItemsFn();
};
const saveItemsFn = function () {
const saveItems = [];
for (let i = 0; i < todoList.children.length; i++) {
const todoObj = {
contents: todoList.children[i].querySelector("span").textContent,
complete: todoList.children[i].classList.contains("complete"),
};
saveItems.push(todoObj);
}
saveItems.length === 0
? localStorage.removeItem("saved-items")
: localStorage.setItem("saved-items", JSON.stringify(saveItems));
};
if (savedTodoList) {
for (let i = 0; i < savedTodoList.length; i++) {
createTodo(savedTodoList[i]);
}
}
const weatherDataActive = function ({ location, weather }) {
const weatherMainList = [
"Clear",
"Clouds",
"Drizzle",
"Rain",
"Snow",
"Thunderstorm",
];
weather = weatherMainList.includes(weather) ? weather : "Fog";
const locationNameTag = document.querySelector("#location-name-tag");
locationNameTag.textContent = location;
document.body.style.backgroundImage = `url('./images/${weather}.jpg')`;
if (
!savedWeatherData ||
savedWeatherData?.location !== location ||
savedWeatherData?.weather !== weather
) {
localStorage.setItem(
"saved-weather",
JSON.stringify({ location, weather })
);
}
};
const weatherSearch = function ({ latitude, longitude }) {
fetch(
`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid={개인API}`
)
.then((res) => {
return res.json();
})
.then((json) => {
const weatherData = {
location: json.name,
weather: json.weather[0].main,
};
weatherDataActive(weatherData);
})
.catch((err) => {
console.log(err);
});
};
const accessToGeo = function ({ coords }) {
const { latitude, longitude } = coords;
// shorthand property
const positionObj = {
latitude,
longitude,
};
weatherSearch(positionObj);
};
const askForLocation = function () {
navigator.geolocation.getCurrentPosition(accessToGeo, (err) => {
console.log(err);
});
};
askForLocation();
if (savedWeatherData) {
weatherDataActive(savedWeatherData);
}
navigator객체 안에는 geolocation객체가 존재한다.
getCurrentPosition(함수자리(성공), 함수자리(에러)) : 사용자의 현재 위치를 얻을 수 있다.
함수의 정리하기
const askForLocation = function () {
navigator.geolocation.getCurrentPosition((position) => {
console.log(position);
});
};
아래와 같이 간단하게 정리한다.
const accessToGeo = function(position){
console.log(position);
}
const askForLocation = function () {
navigator.geolocation.getCurrentPosition(accessToGeo);
});
};
API
프로그램에서 제공하는 기능을 사용자가 활용할수 있도록 만들어 둔 인터페이스
Promise객체의 3가지 상태
1.fulfilled : 요청이 성공한 상태
2.pending : 요청에 대한 응답을 기다리고 있는 상태
3.rejected : 요청이 실패한 상태
Promise(콜백함수)
Promise((resolver, reject)=>{ })
then(콜백함수) 메서드 : pending된 경우 응답이 fulfill가 될때까지 기다림
JSON.parse() : 응답 바디만 존재할때 사용가능, 응답 헤더가 존재하는 경우 사용X
res.json(): 응답 헤더, 바디에 모두 사용가능
catch(콜백함수) : 에러가 발생한 경우 실행되는 함수로 에러를 표시 해준다.
구조분해할당
구조화 되어 있는 배열, 객체와 같은 데이터를
destrucring 시켜, 각각의 변수에 담는 것
1.배열의 구조분해할당 [ ]
let arr = [1,2, 3]
let [ one, two, three ] = arr
console.log(one, two, three) // 1 2 3
2.객체의 구조분해 할당
let obj = { name: 'otter', gender:'male'}
let {name, gender } =obj
→ "키"의 순서는 중요하지X
console.log(name, gender)
// otter male
3.객체의 구조 분해 할당 심화
let obj = { name: 'otter', gender:'male'}
구조분해를 이용해서 key에 value를 재할당
let { name:'kim', gender:'female'} = obj
console.log(name, gender)
//kim female
spread 연산자
spread 연산자→...
하나로 뭉쳐있는 값들의 집합을 펼쳐주는 연산자
let arr =[1, 2, 3, 4, 5]
console.log(arr) // [1, 2, 3, 4, 5]
console.log(...arr)// 1, 2, 3, 4, 5 ←대괄호를 한꺼풀 벗겨줌
spread 연산자→문자열에도 사용가능
let str = "Hello"
console.log(str) //"Hello"
console.log(...str) // "H" "e" "l" "l" "o"
const copyObj = ...obj
→에러
참조타입의 경우 원본데이터의 변화가 복사된 데이터에 영향o
const copyObj = { ...obj}
→얕은 복사가 이루어짐 (bad)
얕은 복사
주소값까지만 복사하는 얕은 복사
깊은 복사
실제 데이터까지 복사하는 깊은 복사
참조타입의 복사
let origin = { name: "otter", age: 25 };
let copy = origin
origin.name // "otter"
copy.name = "rabbit" 복사본데이터 변경시
origin.name // "rabbit" 원본데이터에도 영향O→ bad
const 키워드 : 재선언X, 재할당X
const obj = {
name: "otter"
}
obj.name = "rabbit"
→const 키워드에 재할당X
→const 키워드로 선언된 obj(안에 담긴 주소값 ex.100번지)는 변환되지 않았다.
→주소값이 재할당되지 않았다.
→Heap area의 존재하고 있는 객체(내용)만 바꾸어 주었다.
얕은 복사
let origin = { name : "otter", age: 25 };
let copy = {...origin}
console.log(copy)
//{name: "otter", age: 25 }
얕은 복사
let arr = [1, 2, 3, 4, 5]
let secArr = [6, 7, 8]
let copy = [ ...arr, ...secArr ]
console.log(copy)
//[1, 2, 3, 4, 5, 6, 7]
//원본 배열과 연결이 끊어져 있다.
얕은 복사의 한계
let origin = {
name: "otter",
age: 25,
favoriteFood: { first: "sushi", second: "hamburger"
}
};
let copy = {...origin}
copy.favoriteFood.first = "cold noodle"
console.log(origin.favoritFood)
//{ first: "cold noodle", second: "hamburger" }
※원본 객체 안에 또 다른 객체가 존재하는 경우 제대로 복사가 이루어지지 않음
깊은 복사
※원본 객체 안에 또 다른 객체가 존재하는 경우 완전 복사하고 싶은 경우
let origin = {
name: "otter",
age: 25,
favoriteFood: { first: "sushi", second: "hamburger"
}
};
const copy =JSON.stringify(origin)
해당 객체(또는 배열)을 stringify() 메서드는 JSON포맷(문자열형태)로 변환시켜줌
const deepCopy =JSON.parse(copy)
parse() 메서드는 JSON 포맷의 데이터를 본래의 객체 또는 배열으로 변환시켜 준다.
결론 : 깊은 복사를 하고 싶은 경우
const copy =JSON.stringify(origin)
const deepCopy =JSON.parse(copy)
Rest Parameter
let ogirin = { name: "otter", age : 25, petName: "cherry",
hobby: "playing game" };
const { petName, hobby, ...rest} = origin
console.log(petName) // cherry
console.log(hobby) // playing game
console.log(rest) // { name: "otter", age: 25 } 객체
변수는 rest이외에 다른 것도 사용가능
매개변수가 객체인 경우 구조분해할당 가능
구조 분해 할당 전
const accessToGeo = function (position) {
const positionObj = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
};
weatherSearch(positionObj);
};
구조 분해 할당 후
const accessToGeo = function (coords) { //구조분해할당 1회
const positionObj = {
latitude: coords.latitude,
longitude: coords.longitude,
};
weatherSearch(positionObj);
};
const accessToGeo = function (coords) {
const { latitude, longitude } = coords; //구조분해할당 2회
const positionObj = {
latitude: latitude,
longitude: longitude,
};
weatherSearch(positionObj);
};
객체의 키와 value가 같은 경우 key만 사용해도 된다
const accessToGeo = function (coords) {
const { latitude, longitude } = coords; //구조분해할당 2회
const positionObj = {
latitude,
longitude,
};
weatherSearch(positionObj);
};
'훈훈한 javascript' 카테고리의 다른 글
함수 선언의 종류 (0) | 2023.01.09 |
---|---|
D - Day Counter (0) | 2023.01.09 |
훈훈한 javascript 요약 (0) | 2023.01.09 |
querySelector★★ (0) | 2023.01.09 |