[JS] 바닐라 자바스크립트를 이용하여 Rich-Text-Editor 만들기

2021. 1. 18. 00:17Javascript/응용

Rich Text Editor

 Rich Text Editor란, 위지윅 편집 영역을 제공하여 브라우저 내에서 서식있는 텍스트를 편집하기 위한 인터페이스를 말한다. 바닐라 자바스크립트를 이용하였으며, 기능한 구현은 다음과 같다.

 

  • 왼쪽 정렬

  • 가운데 정렬

  • 오른쪽 정렬

  • 수평 정렬

  • 텍스트 진하게(Bold)

  • 텍스트 기울이기(Italic)

  • 텍스트 언더라인(Underline)

  • order, unorder 리스트

  • 하이퍼 링크

  • 이미지 첨부

  • HTML로 변환

 

 구현 영상은 다음과 같다.

 

완성본


 HTML 코드는 다음과 같다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Text Editor</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" integrity="sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w==" crossorigin="anonymous" />
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <form class="btns-form">
        <button type="button" data-cmd="justifyLeft">
            <i class="fas fa-align-left"></i>
        </button>
        <button type="button" data-cmd="justifyCenter">
            <i class="fas fa-align-center"></i>
        </button>
        <button type="button" data-cmd="justifyFull">
            <i class="fas fa-align-justify"></i>
        </button>
        <button type="button" data-cmd="justifyRight">
            <i class="fas fa-align-right"></i>
        </button>
        <button type="button" data-cmd="bold">
            <i class="fas fa-bold"></i>
        </button>
        <button type="button" data-cmd="italic">
            <i class="fas fa-italic"></i>
        </button>
        <button type="button" data-cmd="underline">
            <i class="fas fa-underline"></i>
        </button>
        <button type="button" data-cmd="insertOrderedList">
            <i class="fas fa-list-ol"></i>
        </button>
        <button type="button" data-cmd="insertUnorderedList">
            <i class="fas fa-list-ul"></i>
        </button>
        <button type="button" data-cmd="insertImage">
            <i class="far fa-image"></i>
        </button>
        <button type="button" data-cmd="createLink">
            <i class="fas fa-link"></i>
        </button>
        <button type="button" data-cmd="showCode" name="active">
            <i class="fas fa-code"></i>
        </button>
    </form>
    <iframe id="output" name="textField"></iframe>
    <script src="./index.js"></script>
</body>
</html> 

 

 섹션은 크게 두 개의 영역으로 나뉜다. 첫 번째 영역버튼을 담고 있는 form 영역, 두 번째 영역은 텍스트를 수정할 iframe 영역이다. 각 버튼에는 data-cmd라는 속성을 추가해주었다. data 속성은 의미론적 표준 HTML 요소에 추가적인 정보를 저장할 수 있도록 해준다. 데이터 속성을 추가해준 이유는 execCommand 명령어를 각 버튼에 저장하기 위해서다.

 

 

 execCommand는 문서 편집을 가능하게 해주는 메서드로, designMode로 전환된 HTML 태그contentEditable이 적용된 div 태그안에서 선택된 영역을 변경할 수 있게 해준다. 이에 적용된 자바스크립트 코드는 다음과 같다.

 

/* iframe 태그의 name 속성은 자바스크립트 요소를 참조하는데 사용할 수 있다. */
// https://www.youtube.com/watch?v=fOKaD4NXHIs
const buttons = document.querySelectorAll('button');
const btnForm = document.querySelector(".btns-form");
let showCode = true;

textField.document.designMode = "On";

btnForm.addEventListener("click", async(e) => {
    const cmd = e.target.closest('button').getAttribute('data-cmd');
    switch(cmd){
        case 'insertImage':
        case 'createLink':
            const url = await prompt('Enter Link Here: ', "");
            textField.document.execCommand(cmd, false, url);

            const atags = textField.document.querySelectorAll("a");
            atags.forEach((atag) => {
                atag.target = "_blank";
                atag.addEventListener("mouseover", (e) => {textField.document.designMode = "Off"; });
                atag.addEventListener("mouseout", (e) => {textField.document.designMode = "On"; });
            });
            break;
        case 'showCode':
            const textFieldBody = textField.document.querySelector("body"); 
            if(showCode){
                textFieldBody.textContent = textFieldBody.innerHTML;
                showCode = false;
            }
            else{
                textFieldBody.innerHTML = textFieldBody.textContent;
                showCode = true;
            }
            break;
        default:
            textField.document.execCommand(cmd, false, null);
            break;
    }
});

 

 

 버튼을 담고 있는 Form 태그를 querySelect를 이용하여 불렀다. 그리고 버튼에 대해서 이벤트 위임을 해주었다. getAttributes를 이용하여 data-cmd 속성 값을 따로 저장하였으며, 이에 대해 switch-case 문을 사용하여 각 명령이 적용되도록 하였다. execCommand의 매개변수는 aCommandName, aShowDefaultUI, aValueArgument이다.

 

 

여기서 aCommandName는 명령어를 의미하며, aShowDefaultUI는 기본 사용자 UI가 나타나야하는지 보여주는 Boolean 값이다. 솔직히 이게 어떤 것을 의미하는지는 모르겠다. 마지막으로 aValueArgument입력 변수가 필요한 명령어(insertImage와 같이 삽입할 이미지의 URL이 필요한)의 경우 이 DOMString으로 정보를 전달한다. 변수가 필요하지 않으면 null을 표기한다.

 

 

참고 자료