[Javascript] 여행준비물 리스트 만들기
HTML, CSS, JS를 모두 사용하여 활용하는 연습이다.
인풋박스에 준비물이름을 작성하고 버튼을 누르면 버튼 박스 아래 리스트에 추가된다. 추가된 아이템은 각기 아이템 이름 오른편에 있는 X를 누르면 리스트에서 삭제된다.
어려울거 없다.
먼저 HTML로 뼈대를 잡자.
<div id="wrapper">
<h2>여행 준비물 점검 목록</h2>
<form>
<input type="text" id="item" />
<button type="button" id="itemAdd">추가</button>
</form>
<div id="itemList"></div>
</div>
간단하게 h2 헤더하나 두고 준비물이름을 작성 받을 인풋박스와 리스트에 더해줄 버튼, 그리고 리스트가 출력될 자리를 설정했다.
스크립트 오픈
let cnt = 0;
let itemNm = "";
let arr = [];
사용할 전역변수들을 선언한다. 사실 처음부터 세개를 모두 선언한건 아니고, 알고리즘을 짜고 시행착오를 거치며 필요한 변수를 추가하기도 했다.
document.getElementById("itemAdd").addEventListener("click", function () {
itemNm = `<li>${document.getElementById("item").value}<button type="button" class="delItems" onclick="clickEvent(${cnt})">X</button></li>`;
document.getElementById("itemList").innerHTML += `${itemNm}`;
cnt++;
});
아이템을 리스트에 추가해주는 구문들이다. 인풋박스에서 가져온 아이템이름에 버튼을 붙이고나서 li 태그를 씌우고 itemNm 변수에 담아서 출력위치로 출력시켜준다.
이 때 특이사항은 아이템이름에 붙이는 버튼이 클릭될 때 호출되는 함수의 매개변수로 cnt 변수를 줬다는거다. 아이템마다 모두 버튼이 있기 때문에 각자 다른 기능을 해야하기에 구분해주기 위함이다. 이제와서 생각하면 차다리 li 태그에 아이디값을 줬으면 편했을 것 같은데.. 암튼 이거 때문에 삭제 함수 알고리즘을 잡을 때 약간 고민이 필요했다.
function clickEvent(num) {
let plsNum = 0;
for (let i = 0; i < arr.length; i++) {
if (num >= arr[i]) {
plsNum++;
}
}
arr.push(num);
const ul = document.getElementById("itemList");
const items = ul.getElementsByTagName("li");
items[num - plsNum].remove();
}
추가한 아이템의 X 버튼을 누르면 아이템이 사라지게 해주는 함수이다.
클릭되는 버튼에 따라 다른 숫자를 매개변수로 받는다.
처음에 생각했던 구조는 다음과 같다;
간단하게, ul이라는 HTMLCollection에 아이템리스트가 출력되는 태그를 담고나서 해당 ul 안에 있는 li, 즉 아이템과 버튼이 담긴 라인을 items라 선언한다. 이후 매개변수로 받은 숫자의 인덱스값을 가진 item을 삭제하는 것이다.
문제는, 이렇게만 구성할시 삭제가 발생한 이후 삭제된 아이템의 인덱스 값보다 더 높은 인덱스 값을 갖는 아이템들의 인덱스가 1씩 내려가는 것이다. 예를들어, 0, 1, 2, 3의 아이템이 있는데 이 중 1을 삭제하면 2의 인덱스 값은 1이 되고 3의 인덱스 값은 2가 된다. 하지만 2 버튼클릭이 주는 매개변수는 여전히 2이기 때문에 2의 X버튼을 클릭하면 3이 삭제된다.
쉽게 말하자면 하나를 삭제하면 그 이후 아이템들의 순서가 하나씩 앞당겨지는 반면 버튼클릭에 지정된 숫자는 그대로라 불일치가 생긴다는 것.
해결을 위해서 먼저 배열하나를 생성했다. 먼저 배열안에 있는 수 중에 매개변수로 받은 숫자보다 작거나 같은 수가 몇개 있는지 카운트해둔다. 그리고 매개변수로 받은 숫자는 해당 배열에 추가한다.
이후 ul 선언, items 선언까진 위와 같으나 item을 삭제할 때 매개변수로 받은 숫자를 그대로 인덱스 값으로 쓰는 대신 앞서 카운트된 숫자를 빼고나서 인덱스 값으로 쓰게된다.
예를 들어, 앞선 예시와 같이 0, 1, 2, 3의 아이템이 있는데, 내가 1의 버튼을 누르면 숫자 1이 배열에 추가되고(아직 배열은 비어있기에 카운트는 발생하지 않음) 1번째 아이템인 1이 삭제된다.
다음, 내가 2의 버튼을 누르면 숫자2와 배열의 수들을 비교한다. 배열에 있는 숫자1보다 숫자2가 크기에 카운트는 1이 된다. 숫자2도 배열에 추가되고, 아이템의 인덱스값 2-1, 즉 1번 인덱스를 가진 2가 삭제된다.
잘 작동하는걸 확인하고.. 디자인도 좀 입혀주자.
* {
margin: 0;
padding: 0;
}
body {
text-align: center;
}
h2 {
font-size: 40px;
margin: 15px;
}
#item {
width: 400px;
height: 50px;
border: transparent;
margin: 0;
outline: none;
padding-left: 10px;
font-size: 20px;
}
#itemAdd {
height: 130px;
width: 150px;
border: 40px solid blue;
background-color: white;
font-weight: 900;
font-size: 20px;
}
form {
margin: 0 auto;
width: 640px;
background-color: blue;
}
li {
width: 590px;
height: 50px;
margin: 0 auto;
background-color: rgb(226, 226, 226);
list-style: none;
text-align: left;
padding-top: 20px;
padding-left: 50px;
font-weight: 800;
font-size: 20px;
}
li:nth-of-type(2n) {
width: 590px;
height: 50px;
margin: 0 auto;
background-color: rgb(194, 193, 193);
list-style: none;
text-align: left;
padding-top: 20px;
padding-left: 50px;
font-weight: 800;
font-size: 20px;
}
li > button {
border: transparent;
background-color: transparent;
float: right;
margin-right: 30px;
font-size: 20px;
font-weight: 700;
}
의도한대로 잘 구동 된다.