JavaScript를 사용하여 텍스트를 부모 크기에 맞게 만들기

* 표지 사진: Amador Loureiro Unsplash
텍스트를 부모 컨테이너로 자동 조정하는 것은 CSS만 사용하려면 거의 불가능한 작업일 수 있습니다.
이 질문의 "선호도"를 반영하기 위해 이 질문들을 살펴보면 거의 동일한 결과를 얻을 수 있습니다.
  • https://stackoverflow.com/questions/4165836/javascript-scale-text-to-fit-in-fixed-div
  • https://stackoverflow.com/questions/16056591/font-scaling-based-on-width-of-container
  • https://stackoverflow.com/questions/14431411/pure-css-to-make-font-size-responsive-based-on-dynamic-amount-of-characters
  • https://stackoverflow.com/questions/17001302/dynamically-resize-font-size-to-fit-container-size
  • 일부 도구는 자동으로 텍스트 크기를 조절할 수 있다


    다행히도, 많은 일을 도와줄 수 있는 자원과 도구가 있습니다.
  • https://css-tricks.com/fitting-text-to-a-container/
  • https://github.com/davatron5000/FitText.js
  • https://github.com/STRML/textFit
  • https://github.com/rikschennink/fitty
  • https://github.com/jquery-textfill/jquery-textfill.github.io
  • https://github.com/simplefocus/FlowType.JS
  • 그래, 일은 이렇다. 나는 몇 가지를 시도했지만, 진정으로 완벽하게 나의 코드에 집적된 것은 하나도 없다.적어도 더 큰 지출은 없다.그래서 나는 통합의 시간과 번거로움을 절약하고 이 문제를 스스로 처리하고 싶다.결과는 내가 상상한 것보다 쉽다.

    우리끼리 해보자.


    나는 네 가지 용례를 만났는데, 나는 잠재적인 실현을 보여주고, 하나하나에 대해 추가적인 해석을 하고 싶다.
    만약 당신이 어찌할 바를 모르거나, 내가 사용하는 단축키가 잘 설명되지 않은 것을 발견한다면, 개선할 수 있도록 평론을 남겨 주십시오.가장 좋은 것은 온라인 편집기, 예를 들어 jsFiddle이나CodePen이 있는데, 이 편집기를 켜서 setep을 상호작용으로 추적하는 것이다.
    제가 소개하고 싶은 용례는요.
  • Container with fixed height and fixed width
  • Container with fixed width and auto height
  • Container with auto width and fixed height
  • Container, which can be resized by users
  • 다음 섹션에서는 CSS에 따라 다른 모든 용례에 대해 동일한 간단한 HTML 예를 사용합니다.

    1. 높이와 폭이 고정된 용기

    For this use case we simply have to check, whether the text-wrapping element (a <span> ) overflows on the height and while not, simple increase font-size by 1px.

    Consider the following two panels:

    <div class="parent">
      <div class="text-container" data-id=1>
        <span class="text">
          This Text is a bit longer
          and should be wrapped correctly
        </span>
      </div>
    </div>
    
    <div class="parent">
      <div class="text-container" data-id=2>
        <span class="text">
          This text
        </span>
      </div>
    </div>
    

    Consider the following CSS for them:

    .parent {
      margin: 2%;
      width: 300px;
      height: 50px;
      padding: 15px;
      background: grey;
      color: white;
      display: block;
    }
    
    .text-container {
      width: 100%;
      height: 100%;
    }
    
    .text {
      font-size: 12px;
      display: block;
    }
    

    The default sized texts in the panels currently looks like this:


    우리는 텍스트를 이용하여 용기 ((text-container 클래스가 있는div) 의 '넘침' 을 사용할 수 있습니다.CSS를 조금 더 시각적으로 표시하려면 다음과 같이 하십시오.
    .text-container {
      border: 1px solid;
      width: 100%;
      height: 100%;
    }
    
    .text {
      font-size: 32px;
      display: block;
    }
    
    body {
      background: #33A;
    }
    
    텍스트가 컨테이너에서 뚜렷하게 오버플로우되었습니다.

    오버플로우 계산


    DOM 요소의 오버플로우를 계산할 수 있는 경우 이를 더 활용할 수 있습니다.
    const isOverflown = ({ clientHeight, scrollHeight }) => scrollHeight > clientHeight
    
    이 경우 텍스트 크기 조정 기능을 위한 알고리즘 로직을 설계할 수 있습니다.
    글꼴 크기를 1픽셀씩 늘린 다음 요소가 부모 요소를 넘치는지 다시 테스트할 수 있습니다.
    만약 원소가 넘친다면, 이전 단계 (픽셀 하나 부족) 는 넘치지 않았기 때문에, 우리의 가장 좋은 일치입니다.

    첫번째 실현


    상기 논리는 원소와 부원소를 받아들여 최소값(12, 대12px에서 최대값(예를 들어 128)으로 교체하고 style.fontSize 속성을 현재 교체 인덱스로 설정하여 넘침이 발생할 때까지 하는 함수를 의미한다.그리고 마지막으로 교체된 색인을 다시 분배합니다.
    간단한 구현은 다음과 같습니다.
    const resizeText = ({ element, parent }) => {
      let i = 12 // let's start with 12px
      let overflow = false
      const maxSize = 128 // very huge text size
    
      while (!overflow && i < maxSize) {
        element.style.fontSize = `${i}px`
        overflow = isOverflown(parent)
        if (!overflow) i++
      }
    
      // revert to last state where no overflow happened:
      element.style.fontSize = `${i - 1}px`
    }
    
    첫 번째text 요소 및 상위 요소에 대해 이 함수를 호출하면 공정한 결과가 생성됩니다.
    resizeText({
      element: document.querySelector('.text'),
      parent: document.querySelector('.text-container')
    })
    

    추가 옵션 추가


    물론, 우리는 유연성을 통해 기능을 더욱 구성할 수 있기를 희망한다.
  • querySelector 또는querySelectorAll만 추가하고 부모를 자동으로 해석할 수 있음
  • 사용자 정의 최소값과 최대값을 전달할 수 있음
  • 부동 값을 사용하여 보다 정확한 어셈블을 위해 1와는 다른 단계를 사용할 수 있음
  • 기기 사용 허용px
  • 최종 코드는 다음과 같습니다.
    const isOverflown = ({ clientHeight, scrollHeight }) => scrollHeight > clientHeight
    
    const resizeText = ({ element, elements, minSize = 10, maxSize = 512, step = 1, unit = 'px' }) => {
      (elements || [element]).forEach(el => {
        let i = minSize
        let overflow = false
    
            const parent = el.parentNode
    
        while (!overflow && i < maxSize) {
            el.style.fontSize = `${i}${unit}`
            overflow = isOverflown(parent)
    
          if (!overflow) i += step
        }
    
        // revert to last state where no overflow happened
        el.style.fontSize = `${i - step}${unit}`
      })
    }
    
    모든 .text 요소를 호출하고 정밀도를 높이기 위해 0.5 절차를 사용합니다.
    resizeText({
      elements: document.querySelectorAll('.text'),
      step: 0.5
    })
    
    그것은 최종적으로 두 가지 요소에 적용된다.

    2. 고정 너비와 자동 높이가 있는 용기

    Consider the same html but a differnt CSS now:

    body {
      background: #A33;
    }
    
    .parent {
      margin: 2%;
      width: 150px;
      height: auto;
      min-height: 50px;
      padding: 15px;
      background: grey;
      color: white;
      display: block;
    }
    
    .text-container {
      width: 100%;
      height: 100%;
      border: 1px solid;
    }
    
    .text {
      font-size: 12px;
      display: block;
    }
    

    The containers now have a fixed width, a minimal height but can grow dynamically ( height: auto ) if the content overflows. The yet untouched text looks like this:


    우리가 수동으로 글꼴 크기를 늘리면 어떻게 되는지 봅시다.
    .text {
      font-size: 48px;
      display: block;
    }
    

    수평 오버플로우 검사 추가


    고도로 성장했지만, 우리가 지금 얻은 폭은 넘쳐흐른다.
    다행히도, 우리는 조금만 수정하면 이전의 코드를 사용할 수 있다.현재 수직 오버플로우(높이 값 사용)만 확인하고 수평 오버플로우 체크만 추가하면 됩니다.
    const isOverflown = ({ clientWidth, clientHeight, scrollWidth, scrollHeight }) => (scrollWidth > clientWidth) || (scrollHeight > clientHeight)
    
    그렇습니다.결과도 지금 보기 좋다.
    resizeText({
      elements: document.querySelectorAll('.text'),
      step: 0.25
    })
    

    3. 고정 높이와 자동 너비가 있는 용기

    For this case we only need to change our CSS, the functions already do their work for use here.

    The default looks like so:

    body {
      background: #3A3;
    }
    
    .parent {
      margin: 2%;
      width: auto;
      min-width: 50px;
      height: 50px;
      min-height: 50px;
      padding: 15px;
      background: grey;
      color: white;
      display: inline-block;
    }
    
    .text-container {
      width: 100%;
      height: 100%;
      border: 1px solid;
    }
    
    .text {
      font-size: 12px;
      display: block;
    }
    

    글꼴 크기를 수동으로 변경하면 다음과 같은 결과가 발생합니다.
    .text {
      font-size: 48px;
      display: block;
    }
    

    우리의 기능을 사용하여 우리는 마침내 해냈다.
    resizeText({
      elements: document.querySelectorAll('.text'),
      step: 0.25
    })
    

    여기에는 별도의 코드가 필요 없다.🎉

    4. 사용자가 크기를 조절할 수 있는 컨테이너

    This is the trickiest part, but thanks to CSS3 and new web standards we can tackle it with just a few lines of extra code. Consider the following CSS:

    body {
      background: #333;
    }
    
    .parent {
      margin: 2%;
      width: 150px;
      height: 150px;
      padding: 15px;
      background: grey;
      color: white;
      overflow: auto;
      resize: both;
    }
    
    .text-container {
      width: 100%;
      height: 100%;
      border: 1px solid;
      display: block;
    }
    
    .text {
      font-size: 12px;
      display: block;
    }
    

    The resize property allows us to resize the most upper-level parent containers:


    크기 조정 기능은 본기implemented by (most) modern browsers와 용기 오른쪽 아래에 표시된 손잡이입니다.
    이제 컨테이너의 크기를 자유롭게 조절할 수 있으므로 우리의 논리에 몇 가지 변화가 생겼습니다.
  • 크기 조정 사건
  • 으로 인한 용기 변화 관찰
  • 변경이 발생하면 함수를 호출합니다. 이 함수는 텍스트의 크기를 조정합니다
  • 절전 메커니즘을 사용하여 초당 크기 조정 횟수를 줄일 수 있음
  • MutationObserver를 사용하여 변경 내용 살펴보기


    관찰 부분에서 우리는 현지인Mutation Observer implementationall modern browsers do support을 사용했다.
    그러나 우리는 .text에서 변화를 관찰할 수 없고 가장 바깥쪽의 용기에서만 변화를 관찰할 수 있다. 이것이 바로 우리의 예.parent이다.또한 MutationObserver 단일 노드를 관찰해야 하기 때문에 우리는 여러 요소를 지원하기 위해 모든 .parent 용기를 교체해야 한다.
    const allParents = document.querySelectorAll('.parent')
    allParents.forEach(parent => {
      // create a new observer for each parent container
      const observer = new MutationObserver(function (mutationList, observer) {
          mutationList.forEach( (mutation) => {
            // get the text element, see the html markup
            // at the top for reference
            const parent = mutation.target
            const textContainer = parent.firstElementChild
            const text = textContainer.firstElementChild
    
            // resize the text
            resizeText({ element: text, step: 0.5 })
        });
      })
    
      // let's observe only our required attributes
      observer.observe(parent, {
        attributeFilter: ['style']
      })
    })
    
    이것은 당시에 매우 잘했다.

    조심해!큰 시간을 조정하는 데 아직도 몇 가지 작은 문제가 존재한다.

    서로 다른 overflow CSS 속성을 적용하면 99.9%의 문제를 실제로 해결할 수 있습니다.
    .parent {
      margin: 2%;
      width: 150px;
      height: 150px;
      padding: 15px;
      background: grey;
      color: white;
      overflow-x: auto;
      overflow-y: hidden;
      resize: both;
    }
    
    100% 장애를 해결할 수 있는 더 좋은 방법이 있다면 다음과 같이 설명합니다. -)

    옵션: 스로틀 추가


    전체 기능이 완성되면 throttle functionality를 추가하여 resizeText 방법에 대한 호출 횟수를 줄일 수 있습니다.
    const throttle = (func, timeFrame) => {
      let lastTime = 0
      return (...args) => {
          const now = new Date()
          if (now - lastTime >= timeFrame) {
              func(...args)
              lastTime = now
          }
      }
    }
    
    const throttledResize = throttle(resizeText, 25)
    
    관찰자 중에서resizetText:
    // ...
    const parent = mutation.target
    const textContainer = parent.firstElementChild
    const text = textContainer.firstElementChild
    
    throttledResize({ element: text, step: 0.5 })
    // ...
    

    요약


    나는 텍스트의 크기를 동태적으로 조정하는 데 있어서의 첫 경험을 반영했고 사람들이 주제를 깊이 있게 이해하고 메커니즘을 이해하여 기존의 라이브러리를 평가하는 데 도움을 줄 수 있기를 바란다.
    지금까지 이것은 아직 충분히 통용되는 방법이 아니어서 일률적인 해결 방안이 될 수 없다.그러나 이 글은 제3자 코드가 필요하지 않아도 실현할 수 있다는 것을 보여준다. 현대 브라우저는 이미 충분한 기능을 제공했기 때문에 약 50줄의 코드로 자신의 크기 조정 도구를 구축할 수 있다.
    어떤 개선 건의도 매우 환영을 받는다. 나는 독자들이 본문에서 얻은 것이 있기를 바란다.

    저자가 본문에서 사용한 자원

  • https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
  • https://developer.mozilla.org/en-US/docs/Web/CSS/resize
  • https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
  • https://caniuse.com
  • https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_throttle
  • https://stackoverflow.com/a/9541579/3098783
  • https://stackoverflow.com/questions/5712596/how-can-i-let-a-div-automatically-set-it-own-width
  • https://jsfiddle.net/
  • https://codepen.io/
  • 좋은 웹페이지 즐겨찾기