RYO.dev

最終更新日:

【JSたったの6行】行数に応じてテキストエリアの高さを自動調節する超簡単な方法

  1. コード
  2. コードの説明
  3. サンプル
  4. 注意
  5. 最後に

コード

最低限のコードはこちらになります。

<textarea class="sample"></textarea>
const tareaE = document.querySelector('.sample');
const bdw = 4;
tareaE.addEventListener('input', () => {
  tareaE.style.height = "20px";
  tareaE.style.height = tareaE.scrollHeight + bdw + "px";
});

コードの説明

後々textarea要素(以下「テキストエリア」)に対してイベントを設定したりCSSを指定したりするために、まずはquerySelector()メソッドを使ってテキストエリアを取得します(querySelector()メソッドについて詳しく知る)。

const tareaE = document.querySelector('.sample');
// const bdw = 4;
// tareaE.addEventListener('input', () => {
//   tareaE.style.height = "20px";
//   tareaE.style.height = tareaE.scrollHeight + bdw + "px";
// });

次に、テキストエリアにinputイベントを指定します。これによりテキストエリアに文字が入力されるたびにイベントが発火します(inputイベントについて詳しく知る)。

// const tareaE = document.querySelector('.sample');
// const bdw = 4;
tareaE.addEventListener('input', () => {
//   tareaE.style.height = "20px";
//   tareaE.style.height = tareaE.scrollHeight + bdw + "px";
});

scrollHeightプロパティ(以下「scrollHeight」)を使うことで、ある要素の現在見えている部分の高さとスクロールしなければ見えない部分の高さを合わせた値を取得できます(scrollHeightプロパティについて詳しく知る)。つまり、scrollHeightの値はある要素を垂直スクロールせずに見るための最小の値ということになります。テキストエリアで文字が右端に到達し折り返されて行数が増えるごとに、scrollHeightの値も増えます。

scrollHeight: px

scrollHeightの値をテキストエリアの高さとして設定すればいいのですが、scrollHeightは境界線の太さを含みません。したがって、scrollHeightの値にテキストエリアの上と下の境界線の太さを加えた値を高さとして指定します。

// const tareaE = document.querySelector('.sample');
const bdw = 4;
// tareaE.addEventListener('input', () => {
  tareaE.style.height = "20px";
  tareaE.style.height = tareaE.scrollHeight + bdw + "px";
// });

境界線の太さをJSで取得する場合は下のようなコードを書きます。

const compStyles = getComputedStyle(tareaE, null);
const bdtW = compStyles.getPropertyValue("border-top-width");
const bdbW = compStyles.getPropertyValue("border-bottom-width");

ではなぜ「20px」も指定しているのでしょうか?

それを指定しないと複数行にわたる文字を一度に消したときに高さが縮んでくれません。また、文字を1文字ずつ消したときにおかしな動作をします。試してみよう!

この問題を解決するために20pxを指定します。
ではなぜ「20px」という値にしているのでしょうか?

結論から言うと、20pxである必要はありません。

テキストエリアのheightプロパティにscrollHeightの値を指定する前に、十分に小さい値を指定します。その時、scrollHeightの値は、要素の中身をスクロールなしで見るための最小の値になっています。そこで、テキストエリアのheightプロパティにscrollHeightの値を指定することで、テキストエリアはちょうどいい大きさになるわけです。

「十分に小さい値」を説明するのが難しいです...。もし分からなかったら「1px」と指定しておけば大丈夫です。

サンプル

下のサンプルは次のような特徴を持っています。

  • min-height: 100px;を指定してデフォルトで数行分の高さを持たせています。
  • max-height: 200px;を指定して高さの最大値を指定しています。
  • 変数bdwにはテキストエリアの上下境界線の太さの合計値に+1した値を代入しています。そうしないとテキストエリアの高さが100px~200pxのときに垂直スクロールバーが表示されてしまいました。

See the Pen Text area that automatically adjusts the height according to the number of lines by RYO (@Mryoo) on CodePen.

注意

もしheightプロパティにトランジションエフェクトを適用させていると、複数行を消したときの動作がおかしくなります(以下のサンプルを参照)。したがって、テキストエリアにtransitionプロパティを指定したい場合は、transition-propertyプロパティの値にallheight以外を設定してください。

最後に

もし誤った情報を見つけたり、より良いコードを知っていたり、お聞きしたいことがあったりした場合は、TwitterのDMメールにご連絡ください。