나만의 아고라스테이츠 만들기 ing...
2023. 1. 11. 18:37ㆍ구현 결과물
구현화면
구현미구현
Bare Minimum Requirement
- 디스커션 나열 기능
- script.js를 수정하여 agoraStatesDiscussions 배열의 데이터를 나열할 수 있게 구현합니다.
- CSS
- 디스커션 추가 기능
- script.js를 수정하여 디스커션 추가 기능을 구현합니다.
- section.form__container 요소에 새로운 아고라 스테이츠 질문을 추가할 수 있는 입력 폼을 제작합니다. 형식은 자유입니다.
- 아이디, 본문을 입력하고 버튼을 누르면 실제 화면에 디스커션이 추가되어야 합니다.
- agoraStatesDiscussions 배열에 추가한 데이터가 실제 쌓여야 합니다.
- Github Page 배포
- Github Page 배포 기능을 이용하여 누구나 볼 수 있게 배포합니다.
- 코드스테이츠 fe-sprint-my-agora-states 리포지토리로 Pull Request
- 나만의 아고라 스테이츠를 코드스테이츠 깃허브에 Pull request합니다.
- 주어진 Pull request 형식에 따라주세요.
Advanced Challenge
- 현지 시간 적용
- 샘플 시간을 잘 변형하여, 현지 시간에 맞게 표현합니다. (ex. 오전 10:02:17)
- 페이지네이션 기능
- 페이지네이션에 대해서 스스로 학습합니다.
- 한 페이지에 10개씩 디스커션이 보여야 합니다.
- 다음 페이지로 넘어갈 수 있어야 합니다.
- 이전 페이지로 돌아올 수 있어야 합니다.
- 다음 페이지가 없거나, 이전 페이지가 없는 경우 페이지를 유지해야 합니다.
- 디스커션 유지 기능
- LocalStorage에 대해서 스스로 학습하고, 새롭게 추가하는 Discussion이 페이지를 새로고침해도 유지되도록 제작합니다.
HTML
<!DOCTYPE html>
<html lang="en">
<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>My Agora States</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.3.8/purify.js"
integrity="sha512-QaF+0tDlqVmwZaQSc0kImgYmw+Cd66TxA5D9X70I5V9BNSqk6yBTbyqw2VEUsVYV5OTbxw8HD9d45on1wvYv7g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<main>
<h1>My Agora States</h1>
<section class="form__container">
<form action="" method="get" class="form">
<div class="form__input--wrapper">
<div class="form__input--name">
<label for="name">Enter your name: </label>
<input type="text" name="name" id="username" autocomplete="off"required>
</div>
<div class="form__input--title">
<label for="name">Enter your title: </label>
<input type="text" name="name" id="titlename" autocomplete="off" required>
</div>
<div class="form__textbox">
<label for="story">Your question: </label>
<textarea id="story" name="story" required></textarea>
</div>
</div>
<div class="form__submit">
<button type="submit" value="submit" id ="button">subbit</button>
</div>
</form>
</section>
<section class="discussion__wrapper">
<ul class="discussions__container">
</ul>
<nav class="page-button-container">
<button class="page-button page-button-1">1</button>
<button class="page-button page-button-2">2</button>
<button class="page-button page-button-3">3</button>
<button class="page-button page-button-4">4</button>
<button class="page-button page-button-">5</button>
</nav>
</section>
</main>
<script src="pagenation.js"></script>
<script src="data.js"></script>
<script src="script.js"></script>
</body>
</html>
CSS
/* TODO: 보기 좋은 나만의 아고라 스테이츠를 위해서 CSS를 수정하세요. */
@import url('https://fonts.googleapis.com/css2?family=Dancing+Script:wght@500&family=Gowun+Batang:wght@700&family=Noto+Sans+KR:wght@500&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Dancing+Script:wght@500&family=Gowun+Batang:wght@700&family=Noto+Sans+KR:wght@500&family=Rubik+Bubbles&family=Single+Day&display=swap');
*{
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Single Day', cursive;
}
body {
font-family: 'Roboto Slab', serif;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background-position: center;
background: url("https://user-images.githubusercontent.com/104641096/168153681-3de36068-1631-4ec2-a179-6ae02200edb1.jpg") no-repeat center;
background-size: cover;
list-style: none
}
main {
display: flex;
flex-direction: column;
position: relative;
z-index: 2;
border:1px solid red;
height: 100%;
width: 560px;
padding: 24px;
background: rgba(178, 191, 248, 0.15);
box-shadow: rgba(93, 163, 243, 0.4) 5px 5px, rgba(125, 157, 247, 0.3) 10px 10px, rgba(64, 109, 255, 0.2) 15px 15px, rgba(86, 59, 243, 0.1) 20px 20px, rgba(81, 91, 233, 0.05) 25px 25px;
backdrop-filter: blur( 0px );
-webkit-backdrop-filter: blur( 0px );
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.2);
}
p {
font-size:25px;
}
a{
font-size:15px;
text-decoration: none;
}
a:link {
color : rgb(15, 15, 15);
text-decoration: none;
}
a:visited {
color : black;
text-decoration: none;
}
a:hover {
color : rgb(0, 47, 255);
text-decoration: underline;
}
a:active {
color : green;
text-decoration: none;
}
h1 {
font-size: 48px; color: #8bbcfc;
text-align: center;
font-family: 'Rubik Bubbles', cursive;
}
img{
align-items: center;
width: 48px;
border : 0.1px solid black;
border-radius : 100px;
}
/*-------------------------------------------------*/
.form{
width:540px
}
.form__container{
display: flex;
flex-direction: row;
justify-content: center;
}
#username{
display: flex;
height: 3vh;
width: 510px;
border: 2px solid black;
border-radius: 10px;
}
#titlename{
display: flex;
height: 3vh;
width: 510px;
border: 2px solid black;
border-radius: 10px;
}
#story{
display: flex;
height: 10vh;
width: 510px;
border: 2px solid black;
border-radius: 10px;
}
/*-------------------------------------------------*/
#button {
width: 100%;
justify-content: center;
margin-top : 10px;
--glow-color: rgb(167, 199, 241);
--glow-spread-color: rgba(227, 226, 238, 0.781);
--btn-color: rgb(252, 250, 252);
border: .15em solid var(--glow-color);
padding: 1em 3em;
color: var(--glow-color);
font-size: 15px;
font-weight: bold;
background-color: var(--btn-color);
border-radius: 1em;
outline: none;
box-shadow: 0 0 1em .25em var(--glow-color),
0 0 4em 1em var(--glow-spread-color),
inset 0 0 .75em .25em var(--glow-color);
text-shadow: 0 0 .5em var(--glow-color);
position: relative;
transition: all 0.3s;
}
#button::after {
pointer-events: none;
content: "";
position: absolute;
top: 120%;
left: 0;
height: 100%;
width: 100%;
background-color: var(--glow-spread-color);
filter: blur(2em);
opacity: .7;
transform: perspective(1.5em) rotateX(35deg) scale(1, .6);
}
#button:hover {
color: var(--btn-color);
background-color: var(--glow-color);
box-shadow: 0 0 1em .25em var(--glow-color),
0 0 4em 2em var(--glow-spread-color),
inset 0 0 .75em .25em var(--glow-color);
}
#button:active {
box-shadow: 0 0 0.6em .25em var(--glow-color),
0 0 2.5em 2em var(--glow-spread-color),
inset 0 0 .5em .25em var(--glow-color);
}
nav.page-button-container {
display: flex;
justify-content: center;
gap: 14px;
margin: 40px 0 20px;
}
nav.page-button-container button {
border: none;
padding: 6px 10px;
background: #b9cdf8;
border-radius: 30px;
}
nav.page-button-container button:hover {
background-color: #54a6f3;
transform: scale(1.2);
}
/*-------------------------------------------------*/
.discussion__wrapper{
margin-top: 20px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.discussions__container{
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
padding: 0;
}
.discussion__container{
margin-top: 10px;
height: 80px;
width: 540px;
justify-content: space-between;
display: flex;
flex-direction: row;
align-items: center;
border-radius: 10px;
background: rgba(243, 243, 255, 0.15);
box-shadow: rgba(8, 15, 31, 0.6) 2px 2px, rgba(137, 194, 247, 0.3) 6px 6px, rgba(91, 112, 233, 0.2) 8px 8px, rgba(69, 168, 214, 0.1) 10px 10px, rgba(240, 46, 170, 0.05) 14px 14px;
backdrop-filter: blur( 0px );
-webkit-backdrop-filter: blur( 0px );
border-radius: 10px;
border: 5px solid rgba(17, 17, 17, 0.18);
}
.discussion__avatar--image{
align-items: center;
width: 48px;
border : 0.1px solid black;
}
.discussion__avatar--wrapper{
display: flex;
align-items: center;
flex:0 0 0;
margin-right: 10px;
margin-left: 10px;
}
.discussion__content{
display: flex;
flex-direction: column;
align-items: center;
flex: 1 0 0;
}
.discussion__content > div{
font-weight: bold;
font-size: 12px;
}
Javascript
// index.html을 열어서 agoraStatesDiscussions 배열 요소를 확인하세요.
const check_Mark = "☑";
const uncheck_Mark = "☒";
// convertToDiscussion은 아고라 스테이츠 데이터를 DOM으로 바꿔줍니다.
const convertToDiscussion = (obj) => {
const li = document.createElement("li"); // li 요소 생성
li.className = "discussion__container"; // 클래스 이름 지정
const avatarWrapper = document.createElement("div");
avatarWrapper.className = "discussion__avatar--wrapper";
//<div class="discussion__avatar--wrapper"></div>
const discussionAnswered = document.createElement("div");
discussionAnswered.className = "discussion__answered";
//<div class="discussion__answered"></div>
// TODO: 객체 하나에 담긴 정보를 DOM에 적절히 넣어주세요.
const photo = document.createElement('img');
//photo = <img></img>
photo.src = obj.avatarUrl;//photo = <img src ="obj.avatarUrl"></img>
photo.className = "discussion__avatar--image"//photo = <img class ="discussion__avatar--image" src ="obj.avatarUrl"></img>
photo.alt = "avatar of" + obj.author;
avatarWrapper.append(photo);
//<div class="discussion__avatar--wrapper">
//<img class ="discussion__avatar--image" src ="obj.avatarUrl"></img>
//</div>
/*-----------------------------------프로필사진-------------------------------------*/
const discussionContent = document.createElement("div");
discussionContent.className = "discussion__content";
//<div class="discussion__content"></div>
const discussionTitle = document.createElement("h2");
//<h2></h2>
const titlelink = document.createElement('a');
//<a></a>
titlelink.href = obj.url // <a href ="obj.url"></a>
titlelink.textContent = obj.title; // <a href ="obj.url">'obj.title'</a>
discussionTitle.append(titlelink); // <h2><a href ="obj.url">'obj.title'</a></h2>
discussionContent.append(discussionTitle);
/*<div class="discussion__content">
<h2>
<a href ="obj.url">'obj.title'</a>
</h2>
</div>
*/
/*-----------------------------------제목-------------------------------------*/
const discussionInfo = document.createElement("div");
//<div></div>
discussionInfo.className = "discussion__info"
//<div class = "discussion"></div>
discussionInfo.textContent = `${obj.author} / ${new Date(obj.createdAt).toLocaleTimeString()}`
//<div class = "discussion">obj.author / 시간</div>
discussionContent.append(discussionTitle, discussionInfo);
/*<div class="discussion__content">
<h2>
<a href ="obj.url">'obj.title'</a>
</h2>
</div>
<div class = "discussion">
obj.author / 시간
</div>
*/
/*-----------------------------------날짜/글쓴이-------------------------------------*/
const checked = document.createElement('p');
//<p></p>
checked.textContent = obj.answer ? check_Mark : uncheck_Mark ;
//<p>uncheck_Mark or check_Mark</p>
discussionAnswered.append(checked);
/*
<div class = "discussion__answered">
<p>uncheck_Mark or check_Mark</p>
</div>
*/
/*-----------------------------------체크박스-------------------------------------*/
li.append(avatarWrapper, discussionContent, discussionAnswered);
return li;
/*
<li class = "discussion__container">
<div class="discussion__avatar--wrapper">
<img class ="discussion__avatar--image" src ="obj.avatarUrl"></img>
</div>
<div class="discussion__content">
<h2>
<a href ="obj.url">'obj.title'</a>
</h2>
</div>
<div class="discussion__content">
<h2>
<a href ="obj.url">'obj.title'</a>
</h2>
</div>
<div class = "discussion">
obj.author / 시간
</div>
<div class = "discussion__answered">
<p>uncheck_Mark or check_Mark</p>
</div>
</li>
*/
};
// agoraStatesDiscussions 배열의 모든 데이터를 화면에 렌더링하는 함수입니다.
const render = (element) => {
for (let i = 0; i < agoraStatesDiscussions.length; i += 1) {
element.append(convertToDiscussion(agoraStatesDiscussions[i]));
}
return;
};
// ul 요소에 agoraStatesDiscussions 배열의 모든 데이터를 화면에 렌더링합니다.
const ul = document.querySelector("ul.discussions__container");
render(ul);
//------------------------------------------------------------------//
function InputInform(name,title,story) {
this.id = name;
this.author = name;
this.createdAt = new Date().toLocaleDateString();
this.title = title;
this.story = story;
this.avatarUrl = "https://avatars.githubusercontent.com/u/104641096?v=4"
}
const submitBtn = document.querySelector('.form')
submitBtn.addEventListener("submit", (event) => {
event.preventDefault();
const newuser = document.querySelector("#username");
const newtitle = document.querySelector("#titlename");
const newtext = document.querySelector("#story");
const newObj = new InputInform(newuser.value,newtitle.value,newtext.value);
agoraStatesDiscussions.unshift(newObj);
newuser.value = null
newtitle.value = null
newtext.value = null
const discussion = convertToDiscussion(agoraStatesDiscussions[0]);
ul.prepend(discussion);
})
//------------------------------------------------------------------//
DOM을 이용하여 HTML을 조작한다는게 생각보다 쉽지않았다.
이전 날 학습을 했음에도 불구하고 실제 프로젝트에서 써먹는것이 굉장히 힘들었다 ㅠㅠ
const convertToDiscussion = (obj) => {
const li = document.createElement("li"); // li 요소 생성
li.className = "discussion__container"; // 클래스 이름 지정
const avatarWrapper = document.createElement("div");
avatarWrapper.className = "discussion__avatar--wrapper";
>
const discussionAnswered = document.createElement("div");
discussionAnswered.className = "discussion__answered";
const photo = document.createElement('img');
photo.src = obj.avatarUrl;//photo = <img src ="obj.avatarUrl"></img>
photo.className = "discussion__avatar--image"//photo = <img class ="discussion__avatar--image" src ="obj.avatarUrl"></img>
photo.alt = "avatar of" + obj.author;
avatarWrapper.append(photo);
/*-----------------------------------프로필사진-------------------------------------*/
const discussionContent = document.createElement("div");
discussionContent.className = "discussion__content";
const discussionTitle = document.createElement("h2");
const titlelink = document.createElement('a');
titlelink.href = obj.url // <a href ="obj.url"></a>
titlelink.textContent = obj.title; // <a href ="obj.url">'obj.title'</a>
discussionTitle.append(titlelink); // <h2><a href ="obj.url">'obj.title'</a></h2>
discussionContent.append(discussionTitle);
/*-----------------------------------제목-------------------------------------*/
const discussionInfo = document.createElement("div");
discussionInfo.className = "discussion__info"
discussionInfo.textContent = `${obj.author} / ${new Date(obj.createdAt).toLocaleTimeString()}`
discussionContent.append(discussionTitle, discussionInfo);
/*-----------------------------------날짜/글쓴이-------------------------------------*/
const checked = document.createElement('p');
checked.textContent = obj.answer ? check_Mark : uncheck_Mark ;
discussionAnswered.append(checked);
/*-----------------------------------체크박스-------------------------------------*/
li.append(avatarWrapper, discussionContent, discussionAnswered);
return li;
위의 DOM 메서드들이 자바스크립트에서 실행되면
<li class = "discussion__container">
<div class="discussion__avatar--wrapper">
<img class ="discussion__avatar--image" src ="obj.avatarUrl"></img>
</div>
<div class="discussion__content">
<h2>
<a href ="obj.url">'obj.title'</a>
</h2>
</div>
<div class="discussion__content">
<h2>
<a href ="obj.url">'obj.title'</a>
</h2>
</div>
<div class = "discussion">
obj.author / 시간
</div>
<div class = "discussion__answered">
<p>uncheck_Mark or check_Mark</p>
</div>
</li>
이러한 코드가 생성되어 HTML에 추가가 된다.
이것을 실제로 HTML코드에 붙여넣으면 어떻게 되냐면요 ~!
자바스크립트에 작성한 DOM을 이용해 HTML을 제어하는 함수를 이용하여 내가 직접 작성하지 않아도 HTML코드에 코드가 추가되는 신기한 경험을 했다.
앞으로 구현해야 할 기능
Advanced Challenge
- 현지 시간 적용
- 샘플 시간을 잘 변형하여, 현지 시간에 맞게 표현합니다. (ex. 오전 10:02:17)
- 페이지네이션 기능
- 페이지네이션에 대해서 스스로 학습합니다.
- 한 페이지에 10개씩 디스커션이 보여야 합니다.
- 다음 페이지로 넘어갈 수 있어야 합니다.
- 이전 페이지로 돌아올 수 있어야 합니다.
- 다음 페이지가 없거나, 이전 페이지가 없는 경우 페이지를 유지해야 합니다.
- 디스커션 유지 기능
- LocalStorage에 대해서 스스로 학습하고, 새롭게 추가하는 Discussion이 페이지를 새로고침해도 유지되도록 제작합니다.
'구현 결과물' 카테고리의 다른 글
API로 부터 항공편을 받아와 조회하기 (0) | 2023.02.04 |
---|---|
축하받고 싶어서 급하게 만든 나의 축하 게시판! (0) | 2023.02.02 |
React state & props (0) | 2023.01.28 |
React Tweettler Bare Minimum Requirement 및 useNavigate 구현 (0) | 2023.01.24 |
JS/CSS DOM을 이용한 [회원가입 유효성검사] (0) | 2023.01.07 |