우선 바닐라 자바스크립트란 자바스크립트 라이브러리 등을 사용하지 않고,
순수하게 자바스크립트 언어로만 프로그래밍을 하는 것이다.
워낙 제이쿼리같은 문법 라이브러리를 많이 사용하는해온 터라,
사실 처음에는 긴가민가하면서 문법을 사용하다가 차츰 익숙해지기 시작했다.
바닐라 자바스크립트를 사용하는 까닭은 속도가 빠르기 때문이라고 한다.
근데 Thymeleaf template layout은 자바스크립트와는 관련 없는
스프링부트 템플릿 엔진이니까 바닐라라고 할 수 있겠지..?
테이블 형태로 만들었고, 그래서 약간 애를 먹었다 TT
- 가벼운 TodoList -
+ 소요 시간 : 약 6시간
+ 특징
1. ul-li가 아니라 table 요소를 이용한 TodoList
(웹표준에 따르면 table 요소는 간단한 페이지처리외에는 지양을 권고한다.)
2. enter를 이용하여 이용가능
3. must와 complete로 나눠져있으며 유동적으로 주고받을 수 있다.
1. 전체적인 기능
전체적인 기능
+ add a new task에 할일을 작성하면 must로 이동하고,
왼쪽 버튼을 누르면 완료했다는 의미로 complete로 이동한다.
오른쪽 버튼을 누르면 삭제한다는 의미로 목록에서 사라진다.
complete로 이동한 할일들은 지움 상태가 되는데 다시 복원이 가능하다.(멍청이 등장 주의)
2. 전체적인 화면 구성
전체적인 화면구성은 header- contents - footer 이렇게 만들 것이고,
layout은 Thymeleaf template layout을 사용할 것이다.
한번쯤 이용해봐도 좋고 쉬운 템플릿 엔진이라서 해보시길 권해드린다.
Thymeleaf template layout 세팅 1, 2
[Spring boot+css+html(Thymeleaf)+Js] 초기 세팅하기1
단독 프론트엔드 프로젝트를 하기 위한 설렘이 시작됐다. 아는 내용이었지만 한 번 더 정리하고자 하는 마음으로 이렇게 글로 정리하도록 한다. 참고로 jsp가 아닌 Thymeleaf를 쓰는 이유는, 그동안
yulfsong.tistory.com
[Thymeleaf] Thymeleaf template layout으로 초기 세팅하기2
JSP에서 include를 써보았다면 같은 맥락으로 이해하면 좋은 라이브러리, thymeleaf template layout를 통해 편리하게 구역을 나눌 수 있다. 이 템플릿엔진을 통해 구역을 나눠주면 화면을 관리하기에 훨
yulfsong.tistory.com
3. 코드 설명
+ 우선, html 부분은 보다는 css, js 위주로 설명해볼 예정이다.
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8"/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"/>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/project.css">
<script src="/js/project.js" defer></script>
</head>
<div id="box">
<header th:replace="fragment/header :: headerFragment"></header>
<div layout:fragment="content"></div>
<footer th:replace="fragment/footer :: footerFragment"></footer>
</div>
</html>
<!DOCTYPE html>
<html
xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="~{layout/layout}">
<head>
<meta charset="UTF-8">
</head>
<th:block layout:fragment="css"></th:block>
<th:block layout:fragment="script"></th:block>
<div layout:fragment="content">
<div id="input_content">
<form class="input_form">
<input type="text" id="input" placeholder="+ add a new task" onkeypress="must()"/>
<input type="text" style="display:none">
</form>
</div>
<div>
<h5 class="must">must</h5>
<hr style="border-top: 2px solid #000;">
</div>
<div id="list_content">
<table border="1" id="must_table">
</table>
</div>
</div>
</html>
이 html은 가운데 content가 들어갈 부분을 작성한 것으로,
input의 onkeypress라는 요소는 엔터, 스페이스등 키보드 하드웨어와의
이벤트를 일으킨다. must()는 JS와 이어질 코드이므로 기억하면 좋다!
하단 id="must_table"이 바로 할일이 생성될 위치이다.
header.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
</head>
<header th:fragment="headerFragment">
<h3 id="time"></h3>
<h1>TO DO LIST</h1>
</header>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
</head>
<footer th:fragment="footerFragment">
<div>
<h5 class="complete">complete</h5>
<hr style="border-top: 2px solid #000;">
</div>
<div id="list_content">
<table border="1" id="add_table">
</table>
</div>
</footer>
</html>
하단 id="add_table"이 바로 할일이 생성될 위치이다.
project.css
@charset "UTF-8";
body {
width:100%;
height:100%;
background:#000;
background-color: lightblue;
font-family: 'Nanum Gothic', sans-serif;
}
#box {
width:300px;
height:300px;
margin:0 auto;
}
h1 {
margin-top: 5px;
margin-bottom: 10px;
}
#input_content {
height: 35px;
width: 300px;
margin-bottom: 10px;
}
#list_content {
margin-left: 10px;
margin-right: 10px;
}
.input_form {
float: left;
}
form input[type=text] {
font-size: 20px;
}
.must_todo_td, .add_todo_td {
width: 232px;
}
.add_todo_td {
text-decoration:line-through;
}
.must, .complete {
margin-bottom: -10px;
}
#time {
margin-bottom: 0px;
}
우선, 나는 html 구조를 짤때 상자안에 상자가 들어간다는 느낌으로
div를 많이 사용하는 편이고, 레이아웃을 제어할 수 있도록
body(전체)와 box(헤드, 컨텐츠, 푸터의 합)라는 요소를 만들었다.
그리고 css의 경우 정가운데로 놓는 방식을 이용하였다.
- 날짜 출력하기
(function() {
let today = new Date();
document.getElementById('time').innerText = today.toLocaleDateString() + "🌳";
}());
보통 function test() { } 이런 함수선언문의 경우,
당연히 test(); 로 함수를 사용하겠다고 호출해줘야만 쓸 수 있다.
하지만 화면이 열리고 바로 오늘날짜가 등장해야하기 때문에
즉시실행(IIFE)이 가능한 형태인 (function() {})을 써주었다.
아까 언급했던 id="time"와 연결되는 부분이다.
- 엔터를 누르면 할일 목록에 들어가기
let input = document.getElementById('input');
let must_table = document.getElementById('must_table');
let add_table = document.getElementById('add_table');
let cnt = 1;
function must() {
if(window.event.keyCode == 13) {
if(input.value != '') {
must_table.innerHTML += "<tbody id='must_tb"+cnt+"'><tr><td><button class='btn btn-outline-secondary'"
+ "type='button' onclick='add("+cnt+")'>⚪</button></td><td class='must_todo_td'>"
+ input.value +"</td><td><button class='btn btn-outline-secondary' type='button'"
+ "onclick='remove("+cnt+")'>❌</button></td></tr></tbody>"
input.value = ''
cnt++;
}
}
}
input은 내가 + add~에 작성한 할일의 내용,
must_table은 must 목록의 테이블, add_table은 complete 목록의 테이블이다.
할일 내용을 쓰고, 엔터를 누르면 must()가 호출되며
keyCode중 13은 엔터를 의미하므로 if안의 구문이 실행된다.
그리고 아무것도 쓰지 않은 채로 엔터를 누를 때 실행되는 것을 방지하기 위해서
input.value != ''를 사용하였다.
must_table.innerHTML~ 부분을 보면 이해가 되겠지만,
내가 할일을 쓰고 엔터를 딱! 누르면 테이블 안으로 HTML이 생성되는 것을 알 수 있다.
내가 헤매던 부분이 <tbody>였는데, 그냥 ul과 li를 써서 쉽게 할 걸 좀 뻘짓했다는 생각이 들었다.
하지만 다행이 깔끔하게 완성할 수 있었다.
테이블 구조는 이런식으로 되어 있는데,
<table>
<tr> // 행
<td></td> //열
</tr>
</table>
innerHTML을 사용하면 자기 마음대로 알아서 <tbody>가 생성되고,
나중에 완전히 삭제되지 않는 문제가 있어서 아예 <tbody>로 감싸는 형태로 만들었다.
cnt를 통해 한줄 한줄이 번호가 부여되며, 이 번호로 삭제되고 이동해줄 수 있다.
할일 목록에 이미 추가됐는데 그 내용이 input창에 남아있는게 싫어서 input.value= '' 를,
할일을 추가할 때마다 새로운 번호가 추가되어야 하므로 cnt++을 써주었다.
- 할일 목록에 들어오면 삭제하기
function remove(cnt) {
let tb = document.getElementById('must_tb'+cnt)
tb.remove();
// let tb = document.getElementById('must_tb'+cnt);
// tb.deleteRow(tb);
// tbody가 확실히 제거되지 않는다.
}
할일 목록에 들어갈때 remove(cnt)가 부여된다는 것을 알 수 있다.
그리고 그 숫자를 통해 삭제도 가능하다.
문제는 table을 이용하다보니 밑의 주석부분처럼 deleteRow()를 통해 삭제하면
정확히 삭제가 되지 않는다는 문제가 있어서 여러가지 적용해본뒤
tbody의 아이디로 가져와서 삭제하는 방법으로 해결하였다.
- 할일 목록에 들어오면 complete로 이동하기
function add(cnt) { //must에서 complete로
let must_tb = document.getElementById('must_tb'+cnt);
var text = must_tb.getElementsByClassName("must_todo_td")[0].innerText;
add_table.innerHTML += "<tbody id='add_tb"+cnt+"'><tr><td><button class='btn btn-outline-secondary'"
+ "type='button'>🔘</button></td><td class='add_todo_td'>"
+ text +"</td><td><button class='btn btn-outline-secondary' type='button'"
+ "onclick='again_must("+cnt+")'>👆</button></td></tr></tbody>"
let tb = document.getElementById('must_tb'+cnt)
tb.remove();
}
이번에는 삭제가 아니라, complete 목록으로 이동하고 싶을 때의 함수이다.
우선 문제해결 방식은
1. must에 있는 내용을 하나 저장해둔다.
+ must_tb에 해당 테이블의 아이디를 저장, 그 중 가운데 내용만 text에 저장
2. complete 목록에 새로운 목록을 만들어서 이동시킨다.
3. must에 있는 내용을 삭제한다.
다시 복원할 수 있는 함수는 again_must(); 라는 함수를 이용하였다.
- complete에 있는 목록을 다시 복원하여 must로 이동
function again_must(cnt) { //complete에서 다시 must로
let add_tb = document.getElementById('add_tb'+cnt);
var text = add_tb.getElementsByClassName("add_todo_td")[0].innerText;
must_table.innerHTML += "<tbody id='must_tb"+cnt+"'><tr><td><button class='btn btn-outline-secondary'"
+ "type='button' onclick='add("+cnt+")'>⚪</button></td><td class='must_todo_td'>"
+ text +"</td><td><button class='btn btn-outline-secondary' type='button'"
+ "onclick='remove("+cnt+")'>❌</button></td></tr></tbody>"
//add_tb.deleteRow(add_tb);
let tb = document.getElementById('add_tb'+cnt)
tb.remove();
}
add()와 구조가 거의 똑같고, 이동하는 함수들이 조금씩 달라졌다.
이렇게해서 todolist완성
- 시도했으나 내용도 길고, 문제가 있어서 지운 뻘짓
let cell1 = "";
let cell2 = "";
let cell3 = "";
let add_tb = document.getElementById('add_tb'+cnt);
var text = add_tb.getElementsByClassName("add_todo_td")[0].innerText;
var row = must_table.insertRow();
row.setAttribute('id', 'must_tb'+cnt);
cell1 = row.insertCell(0);
cell2 = row.insertCell(1);
cell3 = row.insertCell(2);
cell1.innerHTML = "<button class='btn btn-outline-secondary' type='button'"
+ "onclick='add("+cnt+")'>⚪</button></td>"
cell2.setAttribute('class', 'must_todo_td');
cell2.innerHTML = text;
cell3.innerHTML = "<button class='btn btn-outline-secondary' type='button'"
+ "onclick='remove("+cnt+")'>❌</button></td>";
add_tb.deleteRow(add_tb);
처음에 시도한 건 row를 새로 세팅해서 cell을 하나씩 추가하는 방향이었는데,
다시 고치길 잘 한 것 같다.
+ 깃허브 소스
github.com/yulfsong/tistory-code
yulfsong/tistory-code
Contribute to yulfsong/tistory-code development by creating an account on GitHub.
github.com
'프로그래밍 > 프론트엔드 방랑기' 카테고리의 다른 글
[Thymeleaf] Thymeleaf template layout으로 초기 세팅하기2 (0) | 2021.01.11 |
---|---|
[Spring boot+css+html(Thymeleaf)+Js] 초기 세팅하기1 (0) | 2021.01.10 |