17
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Textarea高さ可変&改行制限のやり方(Vue.js) - IME変換対応

Last updated at Posted at 2019-06-08

はじめに

あるプロジェクトで、inputタグを使って実装していた箇所に対して「改行できるようにして欲しい」と言われました。「そうだなぁ、複数行であれば、textareaタグを使うしかないな。」となるのですが、ここで問題が。改行できるようにするのは、タグを変えるだけだけど、可変で制限かけるって難しくね?

結構苦戦したので、その備忘録

やり方

やり方は2段階になります。
まず、 「テキストエリアの高さを可変&制限をかける」そして「改行を制限する」の二つです。では、一つずつ見てみましょう。

1.テキストエリアの高さを可変&制限をかける

デフォルトのテキストエリアであれば、 高さは可変ではありません。
rowsを指定したり、スタイルから高さを指定することで固定になります。
このようにEnterを押すことで改行していきますがスクロールが出現します。

textarea.gif

まずはこの高さを仮に3行で制限してみましょう。

<template>
 <textarea
   v-model="text"
   class="textarea"
   :style="{height:`${getTextareaHeight}px`}"
 />
</template>

<script>
export default {
 data() {
  return {
    text: ''
  }
 },
 computed: {
   getTextareaHeight () { // テキストエリアの高さ制限
    let rowCount = (`${this.text}\n`).match(/\n/g).length
    if (rowCount > 3) rowCount = 3 // ここで3行で制限をかける
    const LINEHEIGHT = 20 * 1.5 // 1行あたりの高さ(font-size:20pxとline-height: 1.4を考慮)
    const PADDING_TOP_AND_BOTTOM = 5 * 2 // padding上下の5pxを考慮
    const BORDER_TOP_AND_BOTTOM = 1 * 2 // border上下の1pxを考慮
    return LINEHEIGHT * rowCount + PADDING_TOP_AND_BOTTOM + BORDER_TOP_AND_BOTTOM
   },
 }
}
</script>

<style scoped>
.textarea {
  padding: 5px;
  border: 1px solid #000;
  font-size: 20px;
  line-height: 1.5;
}
</style>

高さに関しては、JSで動的に指定するのでインラインスタイルになります。
テキストエリアにかかっているスタイルを考慮しなければならないので、少し面倒な記述になりますね。

textarea2.gif

うまく、3行以上の高さにならないように可変でありながらも制限はかけることができました。

2.改行を制限する

次にここが、ネックとなりました。テキストエリアの改行は、Enterを押すことで行われます。
なので、enterキーを押した時に処理を発火させ、制限をかければ良いと思っていました。
@keydownとか@keyupイベントを使って、そのキーコードなり、キー名で判別すれば良いと考えます。
しかし、それはうまくいきません。

というのも、日本語入力変換時の確定Enterと改行のEnterの判別ができないからです。

@keydown="handleKeydown"として、console.warn(e.key)を仕込んでみました。
textarea3.gif

このように、変換確定でもEnterが取れてしまいます。

これの解決方法が、compositionイベントを使うという方法です。
https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
compositionstart: IMEテキスト編集開始時に発火
compositionupdate: IME編集中のテキストが変更された時に発火
compostionend: IMEテキスト編集終了時に発火

compositionstartcompositionendを使ってみます。

<template>
 <textarea
   v-model="text"
   class="textarea"
   @compositionstart="composing=true"
   @compositionend="composing=false"
   @keydown="handleKeydown"
   :style="{height:`${getTextareaHeight}px`}"
 />
</template>

<script>
export default {
 data() {
  return {
    text: '',
    composing: false
  }
 },
 computed: {
   //(略)
 },
 methods: {
   handleKeydown() {
     if (this.composing) return // IME対応(日本語変換中のEnterを止める)
     console.warn(e.key)
   }
 }
}
</script>

<style scoped>
.textarea {
  ...(略)
}
</style>

textarea4.gif

無事に日本語変換確定時のEnterは弾いています。

これで準備は整いました。

先ほどのconsole.warn(e.key)を置き換えましょう。


methods: {
   handleKeydown() {
     if (this.composing) return // IME対応(日本語変換中のEnterを止める)
-    console.warn(e.key)
+    const rowCount = (`${this.text}\n`).match(/\n/g).length
+    if (rowCount >= 3 && e.key === 'Enter') {
     // テキストエリアからフォーカスを外すことで、Enterの連打を防ぐ
+    document.activeElement.blur()
   }
 }

これで、すでにテキストエリア内が3行に達している場合にEnterを押しても改行できなくなりました。

textarea5.gif

警告メッセージなど出してあげるとわかりやすいですね! :clap:

最後に

これで終わりなのですが、こちらコピペなどで貼り付けると3行を越してしまいます。

やり方は色々あると思うのですが、watchtextを監視して制限処理を入れるなど方法があります。

computed: {
  //(略)
},
watch: {
 text (val) {
    if (val.length >= 30) val = val.slice(0, 30) // 30文字制限
    // pasteに対応した改行制限
    let rows = val.split(/\n/)
    this.text = rows.slice(0, 3).join('\n') // textを更新して改行を入れない
  }
},
methods: {
  //(略)
}

他にも、ご自分でtextareaにwrap="off"maxLengthなどをかけて、制限を追加してみると面白いですね!

参考記事

17
19
0

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
17
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?