[HTML/JS/CSS] contenteditable을 이용한 에디터 폰트 변경하기

2021. 11. 12. 10:31WEB.DEV

반응형

안녕하세요.

이번 포스팅에서는 이전 포스팅에서 폰트 변경에 대해서 여쭤보시는 분이 계셔서 추가로 정보를 드리기 위한 포스팅입니다.

 

[HTML/JS/CSS] contenteditable을 이용한 editor 만들기

안녕하세요. 이번 포스팅은 HTML에 있는 contenteditable을 이용해서 editor를 만들기입니다. 만드는 방법은 아주 간단합니다. input, textarea와 같은 텍스트 입력 태그가 아닌 div와 같은 태그에 contenteditabl

dev-bak.tistory.com

 

먼저 기존의 코드에서 폰트 사이즈와 폰트 글꼴을 선택하기 위한 Select와 폰트를 추가를 해줍니다.(기존 코드는 이전 포스팅을 참고해주세요)

폰트 추가(저는 일단 구글 폰트에 등록된 폰트를 추가했습니다)

<head>
  ...
  <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=Black+Han+Sans&family=Nanum+Gothic:wght@400;700;800&family=Nanum+Myeongjo:wght@400;700;800&family=Nanum+Pen+Script&family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap"
      rel="stylesheet">
</head>

 

select 추가
폰트 사이즈
의 경우에는 contenteditable에 나와있는 값으로 1 ~ 7까지 넣었습니다. 폰트 값에 따른 px 값은 아래와 같습니다.
1 : 10px / 2 : 13px / 3 : 16px / 4 : 18px / 5 : 24px / 6 : 32px / 7 : 48px(참고 : stackoverflow)

폰트 이름의 경우에는 css font-family 이름으로 사용을 하시면 됩니다.
저는 구글 폰트에 있는 Black Han Sans와 Noto Sans KR, Nanum Gothic, Nanum Myeongjo, Nanum Pen Script를 일단 넣어봤습니다.

 

<div class="editor-menu">
  ...
  <select id="select-font-size">
    <option value="">폰트 사이즈</option>
    <option value="1">10px</option>
    <option value="2">13px</option>
    <option value="3">16px</option>
    <option value="4">18px</option>
    <option value="5">24px</option>
    <option value="6">32px</option>
    <option value="7">48px</option>
  </select>
  <select id="select-font-name">
    <option value="">폰트</option>
    <option value="Black Han Sans">Black Han Sans</option>
    <option value="Noto Sans KR">Noto Sans</option>
    <option value="Nanum Gothic">Nanum Gothic</option>
    <option value="Nanum Myeongjo">Nanum Myeongjo</option>
    <option value="Nanum Pen Script">Nanum Pen Script</option>
  </select>
</div>

여기 까지 한 다음 script를 추가해줍니다.
먼저 폰트 사이즈와 이름을 선택하는 select를 추가해주고 contenteditable의 폰트 값은 px로 되어 있지 않아서 px에 해당하는 값을 배열(contenteditable 폰트 사이즈에 따른 px 값은 위에 적혀있습니다)을 추가해줍니다.
그리고 폰트 사이즈와 이름 변경 이벤트와 fontName, fontSize 에 대한 execCommand를 추가해줍니다.
그리고 현재 폰트에 대한 정보를 가져오는 코드를 추가해줍니다.(참고 : stackoverflow)

<script>
  ...
  const fontSizeSelector = document.getElementById('select-font-size');
  const fontNameSelector = document.getElementById('select-font-name');

  // 폰트 사이즈에 따른 값을 찾기 위해서 넣은 배열
  const fontSizeList = [10, 13, 16, 18, 24, 32, 48];
  
  ...
  
  fontSizeSelector.addEventListener('change', function () {
    changeFontSize(this.value);
  });
  fontNameSelector.addEventListener('change', function () {
    changeFontName(this.value);
  });
  
  ...
  
  function checkStyle() {
    ...
    
    reportFont();
  }
  
  ...
  
  function changeFontName(name) {
    document.execCommand('fontName', false, name);
    focusEditor();
  }

  function changeFontSize(size) {
    document.execCommand('fontSize', false, size);
    focusEditor();
  }
  
  // 폰트 정보 가져오는 코드 참고 : https://stackoverflow.com/questions/8770008/contenteditable-get-current-font-color-size
  function getComputedStyleProperty(el, propName) {
    if (window.getComputedStyle) {
      return window.getComputedStyle(el, null)[propName];
    } else if (el.currentStyle) {
      return el.currentStyle[propName];
    }
  }

  function reportFont() {
    let containerEl, sel;
    if (window.getSelection) {
      sel = window.getSelection();
      if (sel.rangeCount) {
        containerEl = sel.getRangeAt(0).commonAncestorContainer;
        if (containerEl.nodeType === 3) {
          containerEl = containerEl.parentNode;
        }
      }
    } else if ((sel = document.selection) && sel.type !== 'Control') {
      containerEl = sel.createRange().parentElement();
    }

    if (containerEl) {
      const fontSize = getComputedStyleProperty(containerEl, 'fontSize');
      const fontName = getComputedStyleProperty(containerEl, 'fontFamily');
      const size = parseInt(fontSize.replace('px', ''));
      fontSizeSelector.value = fontSizeList.indexOf(size) + 1;
      // fontName이 문자열 "폰트명"으로 오기 때문에 "를 제거해주는 코드 추가
      fontNameSelector.value = fontName.replaceAll('"', '')
    }
  }
</script>

추가로 폰트 색상 변경 및 폰트에 백그라운드 색상을 넣는 방법에 대해서 알아보겠습니다.

먼저 폰트 색상과 폰트 백그라운드 색상을 선택할 select를 먼저 추가해줍니다.

...

<div class="editor-menu">
  ...
  
  <select id="select-font-color">
    <option value="">폰트 색상</option>
    <option value="#000000">검정</option>
    <option value="#FFFFFF">흰색</option>
    <option value="#CCCCCC">회색</option>
    <option value="#F03E3E">빨강</option>
    <option value="#1971C2">파랑</option>
    <option value="#37B24D">녹색</option>
  </select>
  <select id="select-font-background">
    <option value="rgba(0, 0, 0, 0)">폰트 백그라운드</option>
    <option value="#000000">검정</option>
    <option value="#FFFFFF">흰색</option>
    <option value="#CCCCCC">회색</option>
    <option value="#F03E3E">빨강</option>
    <option value="#1971C2">파랑</option>
    <option value="#37B24D">녹색</option>
  </select>
</div>

...

여기 까지 한 다음 script를 추가해 폰트 색상과 백그라운드 색상 선택에 대한 처리를 해주면 됩니다.
먼저 폰트 색상과 백그라운드 색상 선택에 대한 이벤트를 넣어줍니다.
그리고 fontColor(폰트 색상)와 hiliteColor(백그라운드 색상)에 대한 execCommand를 추가해줍니다.
위에 select에 value 값을 hex 값으로 넣었지만 style에서 color와 backgroundColor값을 가지고 오면 rgb값으로 되어 있어서 value값과 일치시키기 위해서 rgb값을 hex값으로 변환 시켜줍니다.
참고로 rgb에서 hex로 변환하기 귀찮을 경우 value 값을 rgb값으로 넣어주면 되고 형광펜 효과 처럼 넣고 싶으면 rgba로 넣어서 alpha값을 조정하면 됩니다.

<script>
  ...
  
  const selectFontColor = document.getElementById('select-font-color');
  const selectFontBackground = document.getElementById('select-font-background');
  
  ...
  
  selectFontColor.addEventListener('change', function () {
    setFontColor(this.value);
  });
  
  selectFontBackground.addEventListener('change', function () {
    setFontBackground(this.value);
  });
  
  function setFontColor(color) {
    document.execCommand('foreColor', false, color);
    focusEditor();
  }

  function setFontBackground(color) {
    document.execCommand('hiliteColor', false, color);
    focusEditor();
  }

  ...

  function reportFont() {
    ...

    if (containerEl) {
      ...
      
      const fontColor = getComputedStyleProperty(containerEl, 'color');
      const backgroundColor = getComputedStyleProperty(containerEl, 'backgroundColor');
      
      ...
      
      fontColorSelector.value = rgbToHex(fontColor).toUpperCase();
      // 기본 배경색은 rgba(0, 0, 0, 0)
      if (backgroundColor === 'rgba(0, 0, 0, 0)') {
        fontBackgroundSelector.value = backgroundColor;
      } else {
        fontBackgroundSelector.value = rgbToHex(backgroundColor).toUpperCase();
      }
    }
  }
  
  // 폰트 색상 rgb to hex, hex to rgb
  // 참고 : https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
  function componentToHex(c) {
    const hex = parseInt(c).toString(16);
    console.log(hex);
    return hex.length == 1 ? '0' + hex : hex;
  }

  function rgbToHex(color) {
    // rgb(r, g, b)에서 색상값만 뽑아 내기 위해서 rgb() 제거
    const temp = color.replace(/[^0-9,]/g, '');
    // r,g,b만 남은 값을 ,로 [r,g,b] 배열로 변환
    const rgb = temp.split(',');
    return '#' + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]);
  }
}

 

 

너무 급하게 적은 포스팅이라 부족한 부분이 많지만 여기까지 읽어주셔서 감사합니다.

도움이 되셨다면 아래에 공감과 댓글 부탁드립니다💛

728x90
반응형