logo
/
文字に完全にフィットしてサイズが変わるテキストボックスをつくる
2021-07-13
調べると縦方向に対しては文字を追加しても削除しても動的に変化するテキストボックスの例はたくさん出てくるが、横方向は削除したときについてこない例しか無い。 element.scrollWidthelement.clientWidthの関係性では、scrollWidthでのほうが小さくなることは無いからだ。 scrollWidth →内部テキストの幅
clientWidth→要素の幅
overflow: hidden;
white-space: nowrap;
上のようなcssを当てて内部テキストが要素の幅を超えている場合、scrollWidth > clientWidthには成り得るが、内部テキストが要素の幅を超えていない場合はscrollWidth < clientWidthとならずにscrollWidth == clientWidthとなる。 これが最適かはわからないが、ダミーのテキストエリアを作りそれを監視して幅を適用することで解決することが出来た。
 <html>
 <body>
   <!--  監視対象のダミーテキストエリアをおいておく  -->
   <textarea placeholder="" id='dummyTextArea'></textarea>
   <div class='container'>
     <textarea placeholder="" id='textArea'></textarea>
   </div>
 </body>
 </html>
 window.addEventListener("DOMContentLoaded", () => {
   const textArea = document.querySelector("#textArea");
   const dummyElement = document.querySelector("#dummyTextArea");

   const widthBoundary = 400;
   const fontPixel = 16;

   textArea.oninput = (e) => {
     let t = e.target;
     // ダミーテキストエリアに文字を移植
     dummyElement.textContent = t.value;
     // 幅を一旦0にする
     dummyElement.style.width = 0;

     if (t.value.length != 0) {
       isDummyobserve = true;
       observeElement = t;
     }

     if (widthBoundary > t.scrollWidth + fontPixel) {
       t.style.width = "auto";
       t.style.whiteSpace = "nowrap";
       t.style.width = t.scrollWidth + fontPixel + "px";
     } else {
       // 規定値超え
       t.style.whiteSpace = "normal";
       t.style.width = widthBoundary + fontPixel + "px";
     }
     t.style.height = "auto";
     t.style.height = t.scrollHeight + "px";
   };

   const minWidth = 160;
   let observeElement = null;
   let isDummyobserve = false;

   const observer = new MutationObserver(() => {
     if (isDummyobserve) {
       dummyElement.style.width = dummyElement.scrollWidth + fontPixel + "px";
       observeElement.style.width =
         // 既定値と現在で大きい方を採用
         Math.max(
           // dummyと本物で小さい方を採用
           Math.min(observeElement.scrollWidth, dummyElement.scrollWidth),
           minWidth
         ) + "px";
     }
     // リセット
     isDummyobserve = false;
     observeElement = null;
   });

   // ダミーの幅に対してObserverを追加
   observer.observe(dummyElement, {

     attriblutes: true,
     childList: true,
     subtree: true,
     attributeFilter: ["width"]
   });
 });
 #dummyTextArea {
   position: fixed;
   white-space: nowrap;
   width: 0;
   border: 1px solid red;
   background: red;
   color: white;
   opacity: 0.5;
   /* visibility:hidden */
 }

 .container {
   display: flex;
   justify-content: center;
   padding-top: 100px;
 }

 #textarea {
   overflow: hidden;
 }

 * {
   margin: 0;
   padding: 0;
 }
Loading...
※これはteratailで回答したものを整理したコードです。