こんにちは、22新卒のGMOアドマーケティングの天河です。
さっそく本題の結論を言っちゃいましょう。
結論
HTML
1 |
<textarea class="auto-adjust-sample-textarea"></textarea> |
CSS
1 2 3 4 5 |
.auto-adjust-sample-textarea { width: 100%; resize: none; overflow:hidden; } |
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
document.querySelectorAll(".auto-adjust-sample-textarea").forEach((targetArea) => { // 初期値によってtextarea高さをadjust let lineHeight = Number(targetArea.rows); while (targetArea.scrollHeight > targetArea.offsetHeight){ lineHeight++; targetArea.rows = lineHeight; } // 入力値によってtextarea高さをadjust this.addEventListener('input', (e) => { e.srcElement.style.height = 0 e.srcElement.style.height = e.srcElement.scrollHeight+"px" if (e.srcElement.offsetHeight < 40) { e.srcElement.style.height = 40 + "px"; } }) }) |
See the Pen 動的テキストエリア by 10K (@10JII_K) on CodePen.
以上です。ありがとうございました。
だけだと寂しいので解説します。
概要
巷に溢れている動的テキストエリアの実装では主にCSSでゴリゴリ書いていくケースが多いですが、CSSは最小限に、JavaScriptメインで実装していく方針で行きます。jQueryなんか使いません。パワーで押し切ります。大きく二つのことを考慮します。
- 初期値によってtextareaの大きさを調節する
- 入力値によってtextareaの大きさを調節する
まず 1. の実装をしていきます。
初期値によって大きさを調節する
まずテキストエリアを全て取得します。
1 |
document.querySelectorAll(".auto-adjust").forEach((targetArea) => { |
宗教上の理由でquerySelectorAllを使えない人は getElementsByClassNameを使われてると思いますが、
1 |
const targetAreas = document.getElementsByClassName("auto-adjust"); |
ここで取得したtextAreasにforEachを使おうとすると
1 |
Uncaught TypeError: targetAreas.forEach is not a function |
というエラーが出てしまいます。HTMLCollectionは配列っぽいやつなだけで配列ではないのでforEachは使えないという謎仕様です。
なのでこちらで対応してください。
1 2 3 |
Array.prototype.forEach.call(targetAreas, (targetArea) => { .... }) |
次に、初期値に対応する高さを計算します。ここでoffsetHeightとscrollHeightを調整します。
offsetHeightとscrollHeightの違いってなんぞ???
公式サイトがすごくわかりやすく図解してくれています。
offsetHeightはいわゆる「見えている分の要素の高さ」ですね。
参考:MDN Web Docs:HTMElement.offsetHeight
それに対して、scrollHeightは「見えていないところも含めた要素の高さ」です。
参考:MDN Web Docs:Element.scrollHeight
もうおわかりですよね?
つまり、全部見える=offsetHeightがscrollHeight以上の値になるようにします。
ただ初期値設定の際に直接値をぶっこむのではうまくいかなかったので、offsetHeight > scrollHeight になるまでtextareaのrowsを増やしていきます。
1 2 3 4 5 6 7 8 9 |
document.querySelectorAll(".auto-adjust").forEach((targetArea) => { // 初期値によってtextarea高さをadjust let lineHeight = Number(targetArea.rows); while (targetArea.scrollHeight > targetArea.offsetHeight){ lineHeight++; targetArea.rows = lineHeight; } ... } |
これで初期値による高さの調整は完了です。次に2. の入力値によって高さが変わるようにしていきます。
入力値によって大きさを調節する
input eventが起こった際に計算を行うようにします。いったんtextareaの高さを0にしないとscrollHeightがいい感じになりません(ここ結構ハマった)。
texareaの初期高さ 40px を下回らないように条件付けします。
1 2 3 4 5 6 7 |
this.addEventListener('input',(e) => { e.srcElement.style.height = 0 e.srcElement.style.height = e.srcElement.scrollHeight+"px" if (e.srcElement.offsetHeight < 40) { e.srcElement.style.height = 40 + "px"; } }) |
これでJavaScriptの記述は完成です。
スクロールバーを非表示にする
もうスクロールすることから解放された我々は、スクロールバーとお別れを告げることになりました。CSSでtextareaに対して以下のプロパティを追加します。
1 |
overflow: hidden; |
リサイズを禁止にする
勝手にリサイズしてくれるんだし、手動リサイズとも縁を切りましょう。CSSでtextareaに対して以下のプロパティを追加します。
1 |
resize: none; |
完成
おまけ
この実装の背景:1. 自社プロダクトの開発をしている際に、文字数の如何を問わず、テキストエリアのサイズが一緒だったのが気になったから
2. いちいち自分でテキストエリアを広げてみるのもチョー面倒だなと思ったから
3. ここでアピールしとかないとチームのメンバーにこの気の利いた実装を気づいてもらえないと思ったから(褒めて)
ここまで読んでくださりありがとうございました!
お役に立てれば嬉しいです(はてブしてくれたらもっと嬉しいです)。
明日はT.Nさんによる「JavaのStructured Concurrency(Incubator)について」です。
引き続き、GMOアドマーケティング Advent Calendar 2022 をお楽しみください!
■学生インターン募集中!
https://note.gmo-ap.jp/n/nc42c8a60afaf
■エンジニア採用ページはこちら!
https://note.gmo-ap.jp/n/n02cbeb6edb0d
■GMOアドパートナーズ 公式noteはこちら!
https://note.gmo-ap.jp/