728x90
index.html
<!DOCTYPE html>
<html lang="ko">
<link rel="stylesheet" href="/web/style.css">
<head>
<meta charset="UTF-8">
<title>웹 SPA 개발</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap" rel="stylesheet">
<script type="module" src="/web/src/index.js"></script>
</head>
<body>
<div class="app"></div>
<script type="module" src="/web/src/App.js"></script>
</body>
</html>
index.js
import App from './App.js';
new App({$target: document.querySelector('.App')});
App.js
import Header from './components/Header.js';
import HomePage from './page/HomePage.js';
import SignupPage from './page/SignupPage.js';
import {setPersonalInfo} from './components/Storage.js'
class App {
constructor() {
this.$body = document.body;
this.render();
}
async render() {
const header = new Header(this.$body);
header.render();
const main = document.createElement("main");
main.setAttribute("id", "page_content");
this.$body.appendChild(main);
const homePage = new HomePage(main);
const signupPage = new SignupPage(main);
homePage.render();
console.log('prev setPersonalInfo')
await setPersonalInfo();
console.log('after setPersonalInfo')
document.addEventListener("urlchange", (e) => {
let pathname = e.detail.href;
switch(pathname) {
case "/web/":
homePage.render();
break;
case "/web/signup":
signupPage.render();
break;
default:
}
});
}
}
export default App;
header.js
class Header {
constructor($body) {
this.$body = $body;
}
createMenuElem(divClass, spanClass, spanId, menuText) {
const div = document.createElement("div");
div.setAttribute("class", divClass);
const span = document.createElement("span");
span.setAttribute("class", spanClass);
span.setAttribute("id", spanId);
span.appendChild(document.createTextNode(menuText));
div.appendChild(span);
return div;
}
render() {
const header = document.createElement("header");
const home_menu =
this.createMenuElem("header header_left", "menu_name", "menu_home", "HOME");
const signup_menu =
this.createMenuElem("header header_right", "menu_name", "menu_signup", "SIGNUP");
header.appendChild(home_menu);
header.appendChild(signup_menu);
this.$body.appendChild(header);
// HOME 메뉴 클릭 이벤트
home_menu.addEventListener("click", () => {
window.history.pushState("", "", "/web/");
const urlChange = new CustomEvent("urlchange", {
detail: { href: "/web/" }
});
document.dispatchEvent(urlChange);
});
// SIGNUP 메뉴 클릭 이벤트
signup_menu.addEventListener("click", () => {
window.history.pushState("", "", "/web/signup");
const urlChange = new CustomEvent("urlchange", {
detail: { href: "/web/signup" }
});
document.dispatchEvent(urlChange);
});
}
}
export default Header;
contentTitle.js
class ContentTitle {
constructor($main, $title) {
this.$main = $main;
this.$title = $title;
}
render() {
const div = document.createElement("div");
div.setAttribute("class", "content_title");
const h1 = document.createElement("h1");
h1.appendChild(document.createTextNode(this.$title));
div.appendChild(h1);
this.$main.appendChild(div);
}
}
export default ContentTitle;
style.css
body {
margin: 0;
padding: 0;
}
/* GNB */
header {
display: flex;
height: 60px;
align-items: center;
font-size: 1.5em;
font-family: 'Bebas Neue', cursive;
background: #29323C;
background: linear-gradient(198deg, rgba(2,0,36,1) 0%, rgba(99,116,136,1) 0%, rgba(41,50,60,1) 100%);
color: white;
}
.header {
padding: 0px 20px;
}
.header_left {
}
.header_right {
}
#page_content {
padding: 20px;
}
.content_title {
text-align: center;
}
/* 인사 정보 페이지 */
#cards_container {
width: 80%;
margin: 10px auto;
padding: 10px 10px;
}
.card {
margin: 20px 10px;
width: 100px;
height: 100px;
border-radius : 25px;
transform-style: preserve-3d;
transform-origin: center right;
transition: transform 1s;
box-shadow: 5px 5px 10px rgba(0, 0, 0, .2);
}
.card.is-flipped {
transform: translateX(-100%) rotateY(-180deg);
}
.card_plane {
width: 100%;
height: 100%;
border-radius : 25px;
text-align: center;
font-weight: 500;
font-size: 20px;
color: black;
backface-visibility: hidden;
}
.card_plane--front {
background: #F2F2F2;
}
.card_plane--back {
transform: rotateY(180deg);
}
/* 인사 정보 등록 페이지 */
#form_container {
align-items: center;
text-align: center;
margin: 30px 0px;
}
.form_elem {
display: block;
width: 250px;
margin: 0.75em auto;
}
.form_elem input {
width: 100%;
padding: 1em;
border: solid 1.5px #9E9E9E;
border-radius: 1rem;
box-sizing: border-box;
background: none;
}
.form_elem select {
width: 100%;
padding: 1em;
border: solid 1.5px #9E9E9E;
border-radius: 1rem;
box-sizing: border-box;
background: none;
}
.form_elem button {
width: 100%;
padding: 1em;
border: 0;
border-radius: 1rem;
box-sizing: border-box;
background: #dedede;
}
.header {
position: absolute;
padding: 0px 20px;
}
.header_left {
left: 15px;
}
.header_right {
right: 15px;
}
.menu_name:hover {
cursor: pointer;
}
#cards_container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-content: space-around;
}
.card {
width: 200px;
height: 260px;
cursor: pointer;
}
.card_plane {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-weight: 700;
font-size: 40px;
color: white;
}
.card_plane--front {
background: #0D0D0D;
}
.card_plane--back {
background: #F2F2F2;
color: #3098F2;
}
storage.js
export const setPersonalInfo = async () => {
// /web/src/data/new_data.json
console.log('hi!!')
const response = await fetch('/web/src/data/new_data.json');
const data = await response.json();
console.log('data >>> ', data)
if (!localStorage.getItem("personalInfo")) {
localStorage.setItem("personalInfo", JSON.stringify(data));
}
}
export const setCardStatus = () => {
if(!localStorage.getItem("cardStatus")) {
localStorage.setItem("cardStatus", JSON.stringify([]));
}
}
Homepage.js
import ContentTitle from '../components/ContentTitle.js';
import CardView from '../components/CardView.js';
import {setPersonalInfo, setCardStatus} from '../components/Storage.js'
class HomePage {
constructor($main) {
this.$main = $main;
}
async render() {
const title = new ContentTitle(this.$main, "Great PeoPle");
title.render();
await setPersonalInfo();
const cardView = new CardView(this.$main);
cardView.render();
}
}
export default HomePage;
CardView.js
import {cardDiv, cardPlane} from './Card.js'
import {setCardStatus} from './Storage.js'
class CardView {
constructor($main) {
this.$main = $main;
}
render() {
const containerDiv = document.createElement("div");
containerDiv.setAttribute("id", "cards_container");
this.$main.appendChild(containerDiv);
const personalInfo = JSON.parse(localStorage.getItem("personalInfo"))
for(let i = 0; personalInfo.length; i++) {
const card = cardDiv(i); // 카드의 레이아웃 요소
card.appendChild(cardPlane("front", personalInfo[i].nickname))
card.appendChild(cardPlane("back", personalInfo[i].nickname)) // 카드 뒷면의 요소
containerDiv.appendChild(card);
}
}
}
export default CardView;
Card.js
export const cardDiv = (index) => {
const card_div = document.createElement("div");
card_div.setAttribute("idx", index);
card_div.setAttribute("class", "card");
card_div.addEventListener("click", (e) => {
card_div.classList.toggle("is-flipped");
})
return card_div;
}
export const cardPlane = (side, data) => {
const cardPlane_div = document.createElement("div");
cardPlane_div.setAttribute("class", "card_plane card_plane--" + side);
cardPlane_div.appendChild(document.createTextNode(data));
return cardPlane_div;
}
728x90
'웹 개념' 카테고리의 다른 글
[FE & WEB] 웹 개념 - cs, js[질&답식] - [1] (0) | 2023.07.31 |
---|---|
[Web] 기술 인터뷰 회고 - 답을 제대로 못했던거 정리 - 버블링, 클린업 코드, useRef vs useState, 비동기 코드를 자바스크립트에서 쓸 수 있는 이유 (0) | 2023.07.24 |
[web] 멀티파트 설명/의의 (0) | 2023.06.15 |
[web] ajax , axios 차이 (0) | 2023.06.09 |
[Web]아이폰 datePicker 확대되는 현상 해결 코드 (0) | 2023.05.09 |