<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="wrapper">
<!-- action이 서버쪽 동적 페이지가 아니고 정적 html이 왔다? 이게-->
<form method="get" action="write_action.html">
<h1>JS와 LS로 만드는 게시판</h1>
<!-- disabled 처리하면 서버로 넘어가지가 않음. hidden이 필요!-->
제목 <input type="text" name="n_title" value="" required><br>
지은이<input type="text" name="n_writer" value="" required><br>
내용<br>
<textarea name="n_cont" id="" cols="30" rows="10" required></textarea><br>
관련언어(맥스 3개까지만)<br>
JS<input type="checkbox" name="n_skill" value="js" onclick="f3ck(this)">
Java<input type="checkbox" name="n_skill" value="java" onclick="f3ck(this)">
Oracle<input type="checkbox" name="n_skill" value="oracle" onclick="f3ck(this)">
HTML<input type="checkbox" name="n_skill" value="html" onclick="f3ck(this)">
CSS<input type="checkbox" name="n_skill" value="css" onclick="f3ck(this)"><br>
<!-- form 태그 안에 button속성은 자동으로 type이 submit 됨 -->
<button type="submit">전송</button> <button type="submit">다시</button>
</form>
</div>
</body>
</html>
<script>
// 체크 박스 맥스 3개까지만 체크 가능하도록 4개째 메시지와 함께, 4번째 껀 해제!
const maxCnt = 3;
let curCnt = 0;
function f3ck(pThis) {
if(pThis.checked){
console.dir(pThis);
curCnt++; // 체크가 늘었으니 카운트 증가
}
if(!pThis.checked){
curCnt--; // 체크가 줄었으니 카운트 감소
}
if(maxCnt < curCnt){
alert("체크박스는 3개까지만 체크 가능합니다.")
pThis.checked = false; // 강제 체크 풀기
curCnt--; // 늘였던 카운트도 감소
}
}
</script>
// let title = location.href.split("?")[1].split("&")[0].split("=")[1]
/*
인코딩함수 escape encodeURI encodeURIComponent
디코딩함수 unescape decodeURI decodeURIComponent
*/
// 위의 방식으로는 제대로 적용할 수 없음(case by case를 사람이 대처?)
// 위의 상황을 일반화시킴(자동화)
const request = {}; // 네임스페이스용 빈 객체 (안 빈 것이어도 상관없음)
request.getParameter = function(pName){
if(location.href.indexOf("?") == -1) return; // ?가 없으면 return
let queryString = location.href.split("?")[1]; // ? 오른쪽 문자을 쿼리스트링이라 부름
let items = queryString.split("&");
for(let i=0; i<items.length; i++){
let name = items[i].split("=")[0];
let val = items[i].split("=")[1];
if(name == pName){
val = decodeURIComponent(val);
val = val.replaceAll("+", " "); // 공백이 +로 넘어오기 때문에 재가공!
return val;
}
}
// return "name이 없습니다!" // 개발중에는 이렇게 쓰자!
return null;
}
// 같은 name이 여러개인 경우에 사용하는 메소드, 배열을 리턴
request.getParameterValues = function(pName){
if(!location.href.includes("?")) return; // ?가 없으면 return
let arr = [];
let queryString = location.href.split("?")[1];
let items = queryString.split("&");
for(let i=0; i<items.length; i++){
let name = items[i].split("=")[0];
let val = items[i].split("=")[1];
if(name == pName){
val = decodeURIComponent(val);
val = val.replaceAll("+"," "); // 공백이 +로 넘어오기 때문에 재가공!
arr.push(val) // return으로 멈추면 안되고, 끝까지 찾아야함
}
}
if(!arr.length) return null; // 아무것도 못 찾았다면 null 리턴
return arr; //배열을 리턴
}
<!DOCTYPE html>
<meta charset="UTF-8">
<script src="./jsp.js"></script>
<script>
let title = request.getParameter("n_title");
let writer = request.getParameter("n_writer");
let cont = request.getParameter("n_cont");
let skills = request.getParameterValues("n_skill");
// 1개 세트로 (관계형DB로 보자면 테이블의 1개 row로)
let gul = {
num: 0, // primary key로 쓸 속성
title:title,
writer:writer,
cont:cont,
skills:skills,
date: (new Date()).toLocaleDateString()
}
// localStorage에 저장
const tblName = "uglyGesi";
let dataArr = []; // 그냥 선언, 빈 배열 (값이 없는 경우에 쓸 예정)
if(localStorage.getItem(tblName)){
dataArr = JSON.parse(localStorage.getItem(tblName));
gul.num = dataArr[dataArr.length-1].num+1;
}
dataArr.push(gul); // 배열에 추가
localStorage.setItem(tblName, JSON.stringify(dataArr));
alert("글이 잘 입력되었습니다!");
//|location.href = "list.html"; - 캐쉬를 쓸 수도 안쓸 수도
location.replace("list.html"); // 캐쉬 안쓴다.
</script>

새로고침 할 때 마다 추가되는 것도 볼 수 있다.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#modal{
position: fixed;
left: 0px; top:0px;
width : 100vw; height: 100vh;
background-color: rgba(148, 3, 148, 0.8);
display:none; /* 크기가 없는 상태로 안보임 */
}
#mCont{
width: 60%;
height:70%;
margin: 20px auto; /* 수평 가운대 정렬 */
background-color: orange;
}
</style>
</head>
<body>
<div id="modal">
<div id="mCont">
<form method="get" action="">
<h1>상세보기</h1>
<!--사용자에겐 보여줄 필요가 없는데, 프로그램상 필요한 값 hidden 사용-->
<input type="hidden" name="n_num" value="">
<!-- disabled 처리하면 서버로 넘어가지가 않음. hidden이 필요!-->
제목 <input type="text" name="n_title" value=""><br>
지은이<input type="text" name="n_writer" value="" disabled><br>
내용<br>
<textarea name="n_cont" id="" cols="30" rows="10"></textarea><br>
관련언어(맥스 3개까지만)<br>
JS<input type="checkbox" name="n_skill" value="js" onclick="f3ck(this)">
Java<input type="checkbox" name="n_skill" value="java" onclick="f3ck(this)">
Oracle<input type="checkbox" name="n_skill" value="oracle" onclick="f3ck(this)">
HTML<input type="checkbox" name="n_skill" value="html" onclick="f3ck(this)">
CSS<input type="checkbox" name="n_skill" value="css" onclick="f3ck(this)"><br>
<!-- form 태그 안에 button속성은 자동으로 type이 submit 됨 -->
<button type="submit" onclick="fsubmit('mod')">수정</button>
<button type="submit" onclick="fsubmit('del')">삭제</button>
</form>
</div>
</div>
<div id="wrapper">
<h1>어글리 게시판</h1>
<div id="list"></div>
</div>
<script src="./jsp.js"></script>
<script>
// 그저 단순 게시판이 아니니, 한번 잘 고민해보고, 아이디어도 내보는 걸로
// 페이지 나누기 해봐요 (항상 외우지 말고, 생각하면서 흐름으로)
/*
페이지당 몇개 글 출력
전체 글 수
페이지 수
페이지별로 시작인덱스(글), 끝인덱스(글) 배열에 담아져 있기 때문
*/
const tblName = "uglyGesi"; // 로컬 스토리지 테이블 키값
let dataArr = JSON.parse(localStorage.getItem(tblName)); // 데이터 배열
/*
1 0-9
2 10-19
3 20-29
*/
let page = request.getParameter("page");
if(!page) { // 만약 page 정보가 안 넘어 왔다면...
page = 1; // 기본값 1과 무조건 첫 페이지로 지정
}
const cntPerPage = 10; // 페이지당 10개씩
let totalGulCnt = dataArr.length;
let pageCnt = Math.ceil(totalGulCnt / cntPerPage); // 간단히 올림으로 해결
let startGulInx = (page - 1)* 10;
let endGulInx = startGulInx + cntPerPage; // 반복문에 < 쓸거라 -1 필요없음
if(endGulInx > totalGulCnt){
endGulInx = totalGulCnt;
}
const myForm = document.forms[0]; // id를 안주고 요렇게 써도 됨!
function fsubmit(pAct) {
event.preventDefault(); // submit의 built-in 기능 (전송) 막기
if(pAct == 'mod') {
myForm.action = "modify_action.html";
}
if(pAct == 'del') {
myForm.action = "del_action.html";
}
myForm.submit();
}
const myModal = document.querySelector("#modal");
const gList = document.querySelector("#list");
function fread(pNum) {
event.preventDefault(); // built-in 기능 막기 (a태그의 href 기능 막기)
// 빼먹는 사람들이 있어 변칙적으로 그냥 #을 붙이라고 함 (페이지내 이동)
document.querySelector("[name=n_num]").value = pNum;
myModal.style.display = "block";
for(let i=0; i<dataArr.length; i++) {
if(dataArr[i].num == pNum) { // 찾던 글
document.querySelector("[name=n_title]").value = dataArr[i].title;
document.querySelector("[name=n_writer]").value = dataArr[i].writer;
document.querySelector("[name=n_cont]").value = dataArr[i].cont;
// 체크박스를 어캐할 건지, dataArr[i].skills 있는 내용이 자동 체크되어야 함!
// css 선택자를 잘 쓰면 프로그램이 심플하고 명확해짐
let ckArr = dataArr[i].skills;
for(let j=0; j<dataArr[i].skills.length; j++){
document.querySelector(`[value=${ckArr[j]}]`).checked=true;
}
break; // 요런 걸 안 빼먹는 게 중요 여기서 return을 써도 됨
}
}
}
function fList() {
let tblStr = `<table border=1>`;
tblStr += `<tr><th>넘버</th><th>제목</th><th>지은이</th><th>날짜</th>`;
for(let i=startGulInx; i<endGulInx; i++) {
tblStr += `<tr>`;
tblStr += `<td>${dataArr[i].num}</td>`;
tblStr += `<td><a href="//naver.com" onclick="fread(${dataArr[i].num})">${dataArr[i].title}</a></td>`;
tblStr += `<td>${dataArr[i].writer}</td>`;
tblStr += `<td>${dataArr[i].date}</td>`;
tblStr += `</tr>`;
}
tblStr += `</table>`;
// 페이지 번호 출력하는 부분 추가
let pageStr = ""
for(let i=1; i <= pageCnt; i++) {
pageStr += `<a href="list.html?page=${i}">${i}</a>   `
}
tblStr += pageStr; // 페이지 출력 리스트 추가
tblStr += `<br><a href="write.html">글쓰기</a>`;
gList.innerHTML = tblStr; // 화면에 뿌리기
}
window.addEventListener("DOMContentLoaded", function(){
fList();
}); // 태그 해석만 끝나면 발생
// window.addEventListener("load", fList); // 태그 해석 + 이미지 로딩완료에 발생
</script>
</body>
</html>

페이징 버젼
