LoginSignup
11

More than 5 years have passed since last update.

input[type="text"]の横幅を入力された文字に合わせて可変にする

Last updated at Posted at 2019-02-25

UIの都合上、input[type="text"]を複数横に並べて、
かつスマートに横幅を変えようとしたら実装で案外苦労したのでメモ。

つくったもの

demo.gif
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の構造的には上記な感じで、

  • inputposition: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指定し、
inputabsoluteにしたときの基点になるように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で隠す。
::beforecontentは通常時は不要なので空だが、
: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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11