Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

jam
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした