UIの都合上、input[type="text"]を複数横に並べて、
かつスマートに横幅を変えようとしたら実装で案外苦労したのでメモ。
つくったもの
Demo: https://jamog3.github.io/input-width-to-variable/
Repository: https://github.com/jamog3/input-width-to-variable
やってること
全体的なコードについては、こちらをご参照ください。
以下、コードまわりの解説。
html
<div class="input-text__item">
<div class="input-text__dummy js-dummy-input-text" data-placeholder="文字を入力してください"></div>
<input type="text" class="input-text js-input-text" placeholder="文字を入力してください">
</div>
htmlの構造的には上記な感じで、
-
input
をposition:absolute
で配置し、親のdiv
に依存した横幅になるように指定- つまり、
.input-text__dummy
に依存した横幅になる
- つまり、
-
input
に入力された文字列を.input-text__dummy
にそのままコピー(js) -
input[placeholder]
と.input-text__dummy[data-placeholder]
に同じ文字列を指定する
ググったら「文字数に応じて横幅を動的に変更していく」みたいな参考記事はいくつかヒットしたのですが、
2バイト文字をlengthでカウントするのも面倒だし、
そもそも等幅フォントじゃないと破綻するし。。
css
.input-text__item {
position: relative;
display: inline-block;
}
親のdiv
が可変になるようにinline-block
指定し、
input
をabsolute
にしたときの基点になるようにrelative
に。
.input-text__dummy {
display: inline-block;
overflow: hidden;
min-width: 1em;
padding: 3px 5px;
white-space: nowrap;
opacity: 0;
}
.input-text__dummy::before {
content: '';
}
.input-text__dummy:empty::before {
content: attr(data-placeholder);
}
続いてダミー用div
について、input
は必ず1行なのでnowrap
を指定。
表示上は見えてる必要がないのでopacity
で隠す。
::before
のcontent
は通常時は不要なので空だが、
:empty
な時にplaceholder
と同じ文字列が入るように指定。
.input-text {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
width: 100%;
padding: 2px 4px;
}
最後に入力用input
は親のdiv
の幅に依存するようにabsolute
を指定。
width:100%
とpadding-left/padding-right
を共存させるために、box-sizing:border-box
に。
JavaScript (jQuery)
$(document).ready(function() {
var $inputText = $('.js-input-text');
var $dummyInputText = $('.js-dummy-input-text');
function updateValue(e) {
var value = $(e.target).val();
$(e.target).prev($dummyInputText).text(value);
}
$inputText.on('keyup', updateValue);
$inputText.on('compositionend', updateValue);
});
input
に入力された文字列を.input-text__dummy
にそのままコピー
ってのはhtml/cssだけじゃ無理くさかったのでjsでやってます。
$(e.target).prev($dummyInputText)
と指定してるのでDOM構造が異なる場合はご注意ください。
最後に
文字入力まわりを除けば、htmlとcssだけでレンダリングできているので、
jsが読み込み完了した時点で表示が変わってガタッとなるとかそういうのはないかと。
サンプルはjQueryで書きましたが、
ReactやVueならデータバインドでinput
と同じものを.input-text__dummy
にtextとして入れるだけかな。
参考
日本語変換中の処理まわりで参考にさせていただきました。
https://qiita.com/RikutoYamaguchi/items/f3cebd06d8904e085433