お題
下図のような、入力したテキストにあわせて、高さが自動で伸縮される textarea
を、Blazor で作ってみたいと思います。
実装例
以下のように Razor コンポーネントを実装してみました。
<div class="container">
<div class="behind-textarea">@_text
</div>
<textarea @bind="_text" @bind:event="oninput"></textarea>
</div>
@code {
private string _text = "";
}
textarea
要素に、string
型のフィールド変数 _text
を双方向バインドし、バインドを発火するイベントに "oninput" を指定しています。つまり、textarea
に対する何かしら入力が発生すると即時に、 フィールド変数 _text
にその入力内容が反映されます。
そうしてリアルタイムに入力が反映されるフィールド変数 _text
の内容を、div
要素内のコンテンツとして表示用に単方向バインドしています。
このように実装することで、フィールド変数 _text
を媒介に、textarea
に対する入力内容が、兄弟要素の div
要素のテキストコンテンツとして、リアルタイムで反映されるようになります。
そこでさらに、以下のように CSS スタイルを定義します。
.container {
display: inline-block;
/* コンテナ要素の幅は何か適当な値に設定しておく */
width: 200px;
/* このコンテナ要素が position: absolute の座標の基準となるようにする */
position: relative;
}
/*
コンテナ要素内の、textarea 要素と div 要素の両者が、
ピクセルレベルでまったく同じテキスト描画となるよう、
フォントや枠線、パディング、折り返しなどを、
両者に同じく適用する
*/
.behind-textarea, textarea {
font-family: monospace;
font-size: 13.3px;
padding: 2px;
border-width: 1px;
border-style: solid;
white-space: pre-wrap;
word-break: break-word;
margin: 0;
}
/* コンテナ要素内の div 要素は、見た目上は非表示にする */
.behind-textarea {
visibility: hidden;
}
/* textarea 要素が、コンテナ要素内めいっぱいに貼り付くようにする */
textarea {
position: absolute;
inset: 0;
}
この CSS は何をしているかというと、まず、フィールド変数 _text
の内容を表示する div
要素と textarea
要素とで、テキストの表示上のピクセルが完全に一致するように、フォントやパディング、枠線、折り返し表示を両要素に適用しています。
その上で、textarea
要素を、この div
要素とまったく同じサイズ・同じ位置になるよう絶対座標配置したのです。
この div
要素は、そのテキストコンテンツは textarea
とまったく同じようにテキストを折り返し表示しつつ、高さ方向に自動で伸縮しますよね。textarea
要素を、この div
要素と同じサイズ・同じ位置で表示されるように、CSS の position: absolute;
を使って重ね合うようにレイアウトすることで、入力にあわせて高さが自動で伸縮する textarea
を実現しました。
こんな感じで、CSS だけはちょっと凝った感じになりましたけれども、それ以外の Razor コンポーネント上の実装は、フィールド変数へのバインドがあるだけのシンプルな実装で、高さ自動伸縮の複数行テキスト入力が実装できました。
出典
このような、テキストコンテンツとサイズを揃えて textarea
要素を重ね合わせ配置する技法は、当然のことながら (?) 私のオリジナルの発案ではなく、どこかで見聞きしたもので、それを今回、Blazor で実践してみた次第です。StackOverflow の質問と答えで見たような記憶があるのですが、正確な出所は確認していません。