나만의 아고라스테이츠 만들기 ing...

2023. 1. 11. 18:37구현 결과물

구현화면

새로운 디스커션을 만드는데에는 성공!! 물론..새로고침하면 미국가버림


구현미구현

Bare Minimum Requirement

  • 디스커션 나열 기능
    • script.js를 수정하여 agoraStatesDiscussions 배열의 데이터를 나열할 수 있게 구현합니다.
  • CSS
    • 아고라 스테이츠 질문 리스트가 중앙으로 와야 합니다.
    • style.css를 수정하여 멋지고 아름답게 나만의 아고라 스테이츠를 꾸밉니다.
    • colorhunt, dribbble에서 적절한 색 조합, 디자인을 참고합니다.
  • 디스커션 추가 기능
    • 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을 조작한다는게 생각보다 쉽지않았다.

이전 날 학습을 했음에도 불구하고 실제 프로젝트에서 써먹는것이 굉장히 힘들었다 ㅠㅠ

이렇게 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이 페이지를 새로고침해도 유지되도록 제작합니다.