방명록
- [개발자의품격][부트캠프][1기][13차시] JavaScript 주요 포인트 #18 | DOM 패턴 - 추가, 저장, 삭제 페이지2022년 02월 22일 17시 44분 38초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
JavaScript 주요 포인트 #18
DOM 패턴
Step 1
<!-- dom_add.html --> <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> .normal-table { border: 1px solid black; border-collapse: collapse; width: 100%; } .normal-table th, .normal-table td { border: 1px solid black; padding: 5px 10px; } .normal-table thead tr { background-color: yellow; } .hover tbody tr:hover { background-color: yellow; } .error { border: 2px dotted red; } </style> </head> <body> <button onclick="addLine();">추가</button> <button onclick="doSave();">저장</button> <button onclick="doDelete();" id="btnDelete" disabled>삭제</button> <table class="normal-table striped"> <thead> <tr> <th><input type="checkbox" id="chks" onchange="checkAll()" /></th> <th>Name</th> <th>Company</th> <th>Gender</th> <th>Email</th> <th>Phone</th> <th>Address</th> </tr> </thead> <tbody id="tbBody"></tbody> </table> <script> // 행 추가 function addLine() { const h = []; h.push("<tr>"); h.push( `<td><input type="checkbox" name="chk" onchange="isChecked();" /></td>` ); h.push(`<td><input type="text" name="name" /></td>`); h.push(`<td><input type="text" name="company" /></td>`); h.push( `<td><select name="gender"><option value="male">남자</option><option value="female">여자</option></select></td>` ); h.push(`<td><input type="text" name="email" /></td>`); h.push(`<td><input type="text" name="phone" /></td>`); h.push(`<td><input type="text" name="address" /></td>`); h.push("</tr>"); document .querySelector("#tbBody") .insertAdjacentHTML("beforeend", h.join("")); // beforeend는 #tbBody의 끝부분 전에 추가하라! } // 행 삭제 async function doDelete() { const chks = document.querySelectorAll("[name=chk]:checked"); // console.log(chks); chks.forEach((chk) => { let tr = chk; // while문을 써야 행 전체를 삭제할 수 있다. 그렇게 하지 않으면 체크박스만 삭제된다. while (tr.tagName !== "TR") { tr = tr.parentNode; // parentNode은 현재 node의 상위 node } // let tr = chk.parentNode.parentNode; // 깊이를 알수 있을 때는 while문 대신 이렇게도 쓸 수 있다. // console.log(tr); tr.remove(); }); } // 저장 async function doSave() { const names = document.querySelectorAll("[name=name]"); const companies = document.querySelectorAll("[name=company]"); const genders = document.querySelectorAll("[name=gender]"); const emails = document.querySelectorAll("[name=email]"); const phones = document.querySelectorAll("[name=phone]"); const addresses = document.querySelectorAll("[name=address]"); const customers = []; const len = names.length; for (let i = 0; i < len; i++) { customers.push({ name: names[i].value, company: companies[i].value, gender: genders[i].value, email: emails[i].value, phone: phones[i].value, address: addresses[i].value, }); } if (confirm("정말 저장하시겠습니까?")) { for (const customer of customers) { const res = await fetch("http://localhost:3000/customers", { method: "POST", body: JSON.stringify(customer), headers: { "content-type": "application/json;charset=UTF-8", }, }); } } } function checkAll() { const checkValue = document.querySelector("#chks").checked; const chks = document.querySelectorAll("[name=chk]"); for (const chk of chks) { chk.checked = checkValue; } isChecked(); } function isChecked() { const chks = document.querySelectorAll("[name=chk]:checked"); if (chks.length > 0) { document.querySelector("#btnDelete").disabled = false; } else { document.querySelector("#btnDelete").disabled = true; } } </script> </body> </html>
Step 2
// dom_add.html <body> <button onclick="addLine();">추가</button> <button onclick="doSave();">저장</button> <button onclick="doDelete();" id="btnDelete" disabled>삭제</button> <table class="normal-table striped"> <thead> <tr> <th><input type="checkbox" id="chks" onchange="checkAll()" /></th> <th>Name</th> <th>Company</th> <th>Gender</th> <th>Email</th> <th>Phone</th> <th>Address</th> </tr> </thead> <tbody id="tbBody"></tbody> </table> <script> function addLine() { const h = []; h.push("<tr>"); h.push( `<td><input type="checkbox" name="chk" onchange="isChecked();" /></td>` ); h.push(`<td><input type="text" name="name" /></td>`); h.push(`<td><input type="text" name="company" /></td>`); h.push( `<td><select name="gender"><option value="male">남자</option><option value="female">여자</option></select></td>` ); h.push(`<td><input type="text" name="email" /></td>`); h.push(`<td><input type="text" name="phone" /></td>`); h.push(`<td><input type="text" name="address" /></td>`); h.push("</tr>"); document .querySelector("#tbBody") .insertAdjacentHTML("beforeend", h.join("")); } async function doDelete() { const chks = document.querySelectorAll("[name=chk]:checked"); // console.log(chks); chks.forEach((chk) => { let tr = chk; while (tr.tagName !== "TR") { tr = tr.parentNode; } // let tr = chk.parentNode.parentNode; // console.log(tr); tr.remove(); }); } async function doSave() { const names = document.querySelectorAll("[name=name]"); const companies = document.querySelectorAll("[name=company]"); const genders = document.querySelectorAll("[name=gender]"); const emails = document.querySelectorAll("[name=email]"); const phones = document.querySelectorAll("[name=phone]"); const addresses = document.querySelectorAll("[name=address]"); const customers = []; const len = names.length; let passRequired = true; // 통과 조건 for (let i = 0; i < len; i++) { // 비어있는 값 체크 if ( names[i].value === "" || companies[i].value === "" || emails[i].value === "" || phones[i].value === "" || addresses[i].value === "" ) { passRequired = false; } customers.push({ name: names[i].value, company: companies[i].value, gender: genders[i].value, email: emails[i].value, phone: phones[i].value, address: addresses[i].value, }); } // 비어있는 값이 존재할 때 if (!passRequired) { return alert("비어있는 값이 존재합니다. 모든 값을 입력하세요."); } if (confirm("정말 저장하시겠습니까?")) { for (const customer of customers) { const res = await fetch("http://localhost:3000/customers", { method: "POST", body: JSON.stringify(customer), headers: { "content-type": "application/json;charset=UTF-8", }, }); } } } function checkAll() { const checkValue = document.querySelector("#chks").checked; const chks = document.querySelectorAll("[name=chk]"); for (const chk of chks) { chk.checked = checkValue; } isChecked(); } function isChecked() { const chks = document.querySelectorAll("[name=chk]:checked"); if (chks.length > 0) { document.querySelector("#btnDelete").disabled = false; } else { document.querySelector("#btnDelete").disabled = true; } } </script> </body>
Step 3
<!-- dom_add.html --> <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> .normal-table { border: 1px solid black; border-collapse: collapse; width: 100%; } .normal-table th, .normal-table td { border: 1px solid black; padding: 5px 10px; } .normal-table thead tr { background-color: yellow; } .hover tbody tr:hover { background-color: yellow; } .error { border: 2px dotted red; } </style> </head> <body> <button onclick="addLine();">추가</button> <button onclick="doSave();">저장</button> <button onclick="doDelete();" id="btnDelete" disabled>삭제</button> <table class="normal-table striped"> <thead> <tr> <th><input type="checkbox" id="chks" onchange="checkAll()" /></th> <th>Name</th> <th>Company</th> <th>Gender</th> <th>Email</th> <th>Phone</th> <th>Address</th> </tr> </thead> <tbody id="tbBody"></tbody> </table> <script> function addLine() { const h = []; h.push("<tr>"); h.push( `<td><input type="checkbox" name="chk" onchange="isChecked();" /></td>` ); h.push(`<td><input type="text" name="name" /></td>`); h.push(`<td><input type="text" name="company" /></td>`); h.push( `<td><select name="gender"><option value="male">남자</option><option value="female">여자</option></select></td>` ); h.push(`<td><input type="text" name="email" /></td>`); h.push(`<td><input type="text" name="phone" /></td>`); h.push(`<td><input type="text" name="address" /></td>`); h.push("</tr>"); document .querySelector("#tbBody") .insertAdjacentHTML("beforeend", h.join("")); } async function doDelete() { const chks = document.querySelectorAll("[name=chk]:checked"); // console.log(chks); chks.forEach((chk) => { let tr = chk; while (tr.tagName !== "TR") { tr = tr.parentNode; } // let tr = chk.parentNode.parentNode; // console.log(tr); tr.remove(); }); } async function doSave() { const names = document.querySelectorAll("[name=name]"); const companies = document.querySelectorAll("[name=company]"); const genders = document.querySelectorAll("[name=gender]"); const emails = document.querySelectorAll("[name=email]"); const phones = document.querySelectorAll("[name=phone]"); const addresses = document.querySelectorAll("[name=address]"); const regexpEmail = /^([a-z]+\d*)+(\.?[a-z]*)+@[a-z]+(\.[a-z]{2,3})+$/; // Email 형식 정규식 const regexpPhone = /^010-\d{3,4}-\d{4}$/; // phone 형식 정규식 const customers = []; const len = names.length; const blankRows = []; // 잘못된 형식의 입력 값 행 배열 const wrongEmails = []; // 잘못된 형식의 Email 행 배열 const wrongPhones = []; // 잘못된 형식의 phone 행 배열 let passEmail = true; // 기본값 true let passPhone = true; // 기본값 true let passRequired = true; // 기본값 true for (let i = 0; i < len; i++) { if ( names[i].value === "" || companies[i].value === "" || emails[i].value === "" || phones[i].value === "" || addresses[i].value === "" ) { passRequired = false; blankRows.push(i + 1); // alert 메시지에 행 번호 넣기. index가 0부터 시작하므로 +1 } // Email 정규식 체크 if (!regexpEmail.test(emails[i].value)) { passEmail = false; wrongEmails.push(i + 1); } // phone 정규식 체크 if (!regexpPhone.test(phones[i].value)) { passPhone = false; wrongPhones.push(i + 1); } customers.push({ name: names[i].value, company: companies[i].value, gender: genders[i].value, email: emails[i].value, phone: phones[i].value, address: addresses[i].value, }); } // alert 메시지에 행 번호 넣기. if (!passRequired) { return alert( `${blankRows.join( "," )}행에 비어있는 값이 존재합니다. 모든 값을 입력하세요.` ); } // Email행 alert 메시지 if (!passEmail) { return alert( `${wrongEmails.join( "," )}행에 잘못된 형식의 이메일 값이 존재합니다. 올바른 형식으로 입력하세요.` ); } // phone행 alert 메시지 if (!passPhone) { return alert( `${wrongPhones.join( "," )}행에 잘못된 형식의 전화번호 값이 존재합니다. 올바른 형식으로 입력하세요.` ); } const failData = []; // 여러건을 선택해서 한꺼번에 저장할 경우 저장이 누락될 수도 있다. 이를 고려해야 한다. if (confirm("정말 저장하시겠습니까?")) { for (const customer of customers) { const res = await fetch("http://localhost:3000/customers", { method: "POST", body: JSON.stringify(customer), headers: { "content-type": "application/json;charset=UTF-8", }, }); // 정상적인 경우는 201이다. 실패한 데이터를 push 한다. if (res.status !== 201) { failData.push(customer); } // 저장되지 않은 데이터가 있음을 알림 if (failData.length > 0) { alert(`저장되지 않은 데이터가 ${failData.length}개 있습니다.`); } else { alert("정상적으로 저장 되었습니다."); } } } } function checkAll() { const checkValue = document.querySelector("#chks").checked; const chks = document.querySelectorAll("[name=chk]"); for (const chk of chks) { chk.checked = checkValue; } isChecked(); } function isChecked() { const chks = document.querySelectorAll("[name=chk]:checked"); if (chks.length > 0) { document.querySelector("#btnDelete").disabled = false; } else { document.querySelector("#btnDelete").disabled = true; } } </script> </body> </html>
Step 4
<!-- dom_add.html --> <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> .normal-table { border: 1px solid black; border-collapse: collapse; width: 100%; } .normal-table th, .normal-table td { border: 1px solid black; padding: 5px 10px; } .normal-table thead tr { background-color: yellow; } .hover tbody tr:hover { background-color: yellow; } /* 잘못된 형식에 대한 스타일 */ .error { border: 2px dotted red; } </style> </head> <body> <button onclick="addLine();">추가</button> <button onclick="doSave();">저장</button> <button onclick="doDelete();" id="btnDelete" disabled>삭제</button> <table class="normal-table striped"> <thead> <tr> <th><input type="checkbox" id="chks" onchange="checkAll()" /></th> <th>Name</th> <th>Company</th> <th>Gender</th> <th>Email</th> <th>Phone</th> <th>Address</th> </tr> </thead> <tbody id="tbBody"></tbody> </table> <script> function addLine() { const h = []; h.push("<tr>"); h.push( `<td><input type="checkbox" name="chk" onchange="isChecked();" /></td>` ); h.push(`<td><input type="text" name="name" /></td>`); h.push(`<td><input type="text" name="company" /></td>`); h.push( `<td><select name="gender"><option value="male">남자</option><option value="female">여자</option></select></td>` ); h.push(`<td><input type="text" name="email" /></td>`); h.push(`<td><input type="text" name="phone" /></td>`); h.push(`<td><input type="text" name="address" /></td>`); h.push("</tr>"); document .querySelector("#tbBody") .insertAdjacentHTML("beforeend", h.join("")); } async function doDelete() { const chks = document.querySelectorAll("[name=chk]:checked"); // console.log(chks); chks.forEach((chk) => { let tr = chk; while (tr.tagName !== "TR") { tr = tr.parentNode; } // let tr = chk.parentNode.parentNode; // console.log(tr); tr.remove(); }); } async function doSave() { // 잘못된 Email, phone 스타일을 본래대로 돌리기. 클래스명이 error인 경우만 찾아서 처리. document .querySelectorAll(".error") .forEach((item) => (item.className = "")); const names = document.querySelectorAll("[name=name]"); const companies = document.querySelectorAll("[name=company]"); const genders = document.querySelectorAll("[name=gender]"); const emails = document.querySelectorAll("[name=email]"); const phones = document.querySelectorAll("[name=phone]"); const addresses = document.querySelectorAll("[name=address]"); const regexpEmail = /^([a-z]+\d*)+(\.?[a-z]*)+@[a-z]+(\.[a-z]{2,3})+$/; const regexpPhone = /^010-\d{3,4}-\d{4}$/; const customers = []; const len = names.length; const blankRows = []; const wrongEmails = []; const wrongPhones = []; let passEmail = true; let passPhone = true; let passRequired = true; for (let i = 0; i < len; i++) { if ( names[i].value === "" || companies[i].value === "" || emails[i].value === "" || phones[i].value === "" || addresses[i].value === "" ) { passRequired = false; blankRows.push(i + 1); } if (!regexpEmail.test(emails[i].value)) { passEmail = false; wrongEmails.push(i + 1); } if (!regexpPhone.test(phones[i].value)) { passPhone = false; wrongPhones.push(i + 1); } customers.push({ name: names[i].value, company: companies[i].value, gender: genders[i].value, email: emails[i].value, phone: phones[i].value, address: addresses[i].value, }); } if (!passRequired) { return alert( `${blankRows.join( "," )}행에 비어있는 값이 존재합니다. 모든 값을 입력하세요.` ); } // 잘 못된 형식의 Email인 경우 스타일 적용 if (!passEmail) { wrongEmails.forEach((item) => { document.querySelector( `#tbBody tr:nth-child(${item}) [name=email]` ).className = "error"; }); return alert( `${wrongEmails.join( "," )}행에 잘못된 형식의 이메일 값이 존재합니다. 올바른 형식으로 입력하세요.` ); } // 잘 못된 형식의 phone인 경우 스타일 적용 if (!passPhone) { wrongPhones.forEach((item) => { document.querySelector( `#tbBody tr:nth-child(${item}) [name=phone]` ).className = "error"; }); return alert( `${wrongPhones.join( "," )}행에 잘못된 형식의 전화번호 값이 존재합니다. 올바른 형식으로 입력하세요.` ); } const failData = []; if (confirm("정말 저장하시겠습니까?")) { for (const customer of customers) { const res = await fetch("http://localhost:3000/customers", { method: "POST", body: JSON.stringify(customer), headers: { "content-type": "application/json;charset=UTF-8", }, }); if (res.status !== 201) { failData.push(customer); } if (failData.length > 0) { alert(`저장되지 않은 데이터가 ${failData.length}개 있습니다.`); } else { alert("정상적으로 저장 되었습니다."); } } } } function checkAll() { const checkValue = document.querySelector("#chks").checked; const chks = document.querySelectorAll("[name=chk]"); for (const chk of chks) { chk.checked = checkValue; } isChecked(); } function isChecked() { const chks = document.querySelectorAll("[name=chk]:checked"); if (chks.length > 0) { document.querySelector("#btnDelete").disabled = false; } else { document.querySelector("#btnDelete").disabled = true; } } </script> </body> </html>
728x90반응형'영광의 시대! > 2022 개발자의 품격 부트캠프 1기' 카테고리의 다른 글
다음글이 없습니다.이전글이 없습니다.댓글