프론트엔드 데브코스 TIL #DAY25~28

에구마·2023년 10월 26일
0

23.10.23 ~ 26

📌 노션 클로닝 과제

  • 보너스 요구사항 및 리팩토링

보너스 요구사항

  • div와 contentEditable을 조합해서 좀 더 Rich한 에디터를 만들기
  • 현재 편집 중인 Document의 하위 Document 링크 추가
  • 편집기 내에서 다른 Document name을 적은 경우, 자동으로 해당 Document의 편집 페이지로 이동하는 링크
  • 그외 개선사항 구현

contentEditable

일반 텍스트 형태로 입력만 받는 < input>의 활용은 제한적이다!
입력 내에서 태그를 사용하거나, 이미 < h2>등의 태그로 되어있는 내용에 편집도 할 수 있게 하려면? contentEditable 속성값을 이용하자!

<div contenteditable=“true”>

Input이나 textarea와 다르게 값이 inner로 들어가기 때문에 e.target.value로 접근하지 않는다!

  • contentEditable속성값은 상속된다! 즉 부모가 contentEditable 하면 자식에서도 contentEditabl이다.

contentEditable를 이용해서 본문 에디터 구현하기

아이디어1) render에서 innerHTML로 그리기 전에 본문에 대해서 Rich작업을 거쳐보자.

  • 입력값을 바꾸면 setState가 일어나고 setState는 내부에서 state갱신 후 render를 호출한다. 그러면 다시 innerHTML을 그리기 때문에 커서가 빠진다 ㅠ
    ⇒ 커서를 강제로 두려면 .focus()
  • 강제한 커서가 맨앞에 존재한다
    ⇒ 맨뒤로 두려면 다음과 같은 로직이 필요하다
  const range = document.createRange();
  range.selectNodeContents($editor.querySelector(".editor_content"));
  range.collapse(false);
  var sel = window.getSelection();
  sel.removeAllRanges();
  sel.addRange(range);
  • 이젠 맨뒤에만 존재한다 (ㅋㅋㅋ)
    맨뒤에서 계속 추가할땐 좋지만, 중간에서 추가를할수도 있잖아? 근데 한글자 입력하자마자 맨뒤로 가버리는 커서...
    ⇒ 지금 커서가 가리키는 곳을 기억해두어야한다!

여기까지 수정을 거치다가 근본적인 다른 방법을 떠올렸다.

아이디어2) 저장할때부터 태그로 바꾸자!
아이디어1이 마크다운으로 저장을 하고 그릴때 태그로 바꿨다면, 애초에 입력을 감지해서 태그로 저장해보자.

  • 엔터가 입력되면 해당 줄의 텍스트가 ##으로 시작하는지 확인하고, 시작한다면 h2태그로, 아니라면 div태그로 감싸고 PUSH한다.
    ⇒ 방향키 이동에 제한이 있었고, 바로 화면에 반영이되지 않았다.

다시 생각하면서 엎으려다가, 팀원분이 그러기엔 아깝다하셔서,,,, 다시 머리를 벅벅

아이디어2+) 저장할때부터 태그로 바꾸자!

##가 입력되면 입력창을 < div>에서 < h2>로 바꿔주는 형식!
로직을 정리해보자!

  • 엔터키를 치면 새로운 입력줄이 생겨야 한다.
  • 새로운 입력줄인 newEditor를 div로 만들고 contenteditable속성 및 이벤트 핸들러 또한 지정해준다!
    • 엔터키 입력이 일어난 현재 요소와 같은 레벨로 추가해준다
    • 새로만든 그 입력줄에 focus를 줘야한다.
  • 뒤로가기가 입력되었는데 현재 InnerHTML이 비었으면 그 줄 없애고 위로 가야한다.
    • previousElementSibling를 찾고 거기에 focus
    • 그리고선 현재 타겟은 지운다
  • 방향키 인식해서 focus 변경
  • #이 인식되는 경우 h태그로 변경
    • 위의 키들 말고 다른 일반 문자키들이 입력되면
    • 그 입력발생한 타겟의 innerHTML이 # , ## , 등으로 시작한다면
    • 새로운 입력줄을 #갯수에 맞게 h태그로서 생성하면된다!!
    • innerHTML은 #없앤 내용만 넣어주면 되고 after로 붙이고 타겟은 지운다.(왜냐면 현상태는 타겟이div이고 h만 남으면 되는거니까)
  let allHTML = e.currentTarget.parentNode.innerHTML;

  if (e.currentTarget.innerHTML.indexOf("#&nbsp;") === 0) {
    const txt = e.currentTarget.innerHTML.substring(7);
    const newline = document.createElement("h1");
    
    newline.className = "editor_content_block r";
    newline.setAttribute("contenteditable", true);
    newline.addEventListener("keyup", (e) => handleChangeContent(e));
    newline.innerHTML = txt;
    e.currentTarget.after(newline);
    newline.focus();
    
    allHTML = e.currentTarget.parentNode.innerHTML;
    e.currentTarget.remove();
  }

  const nextState = {
    ...this.state,
    content: allHTML,
  };
  console.log(nextState);
  await onEditing(nextState, "content");
  }

성공 🥰


☄️ 트러블 슈팅

const blocks = document.getElementsByClassName("editor_content_block");
  for (let block of blocks) {
    block.addEventListener("keyup", (e) => handleChangeContent(e));
  }

이렇게 요소들을 찾아서 각각에 이벤트 리스너를 심었다.
근데!? 이렇게 하면 한 페이지 내에서 새로고침을 했을 때 위의 캡쳐처럼 요소들을 못찾는다!!!! 콘솔에 코드를 입력하면 찾는다. 렌더링 순서의 문제일까.
일단, 해결방법은 .editor_content_block를 document에서 찾지 않았다.

// $editor.appendChild($editor_content);해놓은 $editor_content에 붙였다.
$editor_content.getElementsByClassName("editor_content_block");


after VS append

정리한 글
요약 : after는 해당 요소 "뒤"에! 즉, 형제관계
append는 해당 요소 "하위"에! 즉, 자식관계



console.dir( )

https://developer.mozilla.org/ko/docs/Web/API/console/dir

주어진 JavaScript 객체의 모든 속성을 콘솔에서 볼 수 있는 방법으로서 이를 사용하면 개발자가 객체의 속성을 쉽게 확인할 수 있습니다.


그외 구현사항

  • 방향키 인식 및 focus
  • documents 목록 토글 여닫기
  • 트라이탐색
  • @입력시 다른 페이지로 링크(a 태그)
    • a 태그에서 백스페이스 입력시 한번에 삭제
  • @ 이후 입력한 페이지 이름에 대해 트라이 탐색결과

구현할 것

  • shift + Enter
  • focusin, focustout에 따라 placeholder
  • 탐색 트라이 같은 제목 분리
  • 배포


🤔 회고

기본 요구사항 구현은 큰 무리없이 끝냈는데, 이번주 보너스 요구사항에서 꽤나 힘이 들었다. 아직 모르는 부분이 많았고 고려해야 할 부분이 정말 정말 많았다..
어느정도 마무리가 된 후에 갑자기 의욕이 많이 꺾였었다. 내 결과물에 100%만족할 수 없는데, 여기서 다 갈아엎기엔 시간도 힘도 부족하게 느껴지고 그게 또 힘에 부치는 딜레마였다.

profile
코딩하는 고구마 🍠 Life begins at the end of your comfort zone

0개의 댓글