Vue.jsで,contenteditableな要素をinput要素のように扱う方法

Vue.jsでinput/textarea要素といえばv-model。

v-modelで指定されたデータは双方向バインディングされ,ユーザーが入力した文字がデータへ即反映され,逆に,データが変更されたらinput/textarea要素の表示に即反映される。

contenteditable属性をtrueに設定したHTML要素でもこれと同じ動きをするものが作れる。

以下,つくってみたものを紹介する。


contenteditableな要素でつくったinput要素っぽいコンポーネント

<template>
  <div
    contenteditable="true"
    v-text="text"
    v-on:input="OnUpdate"
    v-on:focus="OnFocus"
    v-on:blur="OnBlur"
  ></div>
</template>

<script>
export default {
  name: "Editable",
  props: {
    value: {
      type: String,
      required: true
    },
  },
  data() {
    return {
      focusIn: false,
      text: ""
    };
  },
  mounted() {
    this.text = this.value;
  },
  methods: {
    input(str) {
      this.$emit("input", str);
    },
    OnUpdate(event) {
      const target = event.target;
      this.input(target.innerText);
    },
    OnFocus() {
      this.focusIn = true;
    },
    OnBlur() {
      this.focusIn = false;
    }
  },
  watch: {
    value() {
      if (this.focusIn) return;
      this.text = this.value;
    }
  }
};
</script>

つくったコンポーネントの使い方

<template>
  <editable
    v-bind:value="editableData"
    v-on:input="OnStringUpdated"
  ></editable>
</template>

<script>
import Editable from "./Editable.vue";

export default {
  name: "MyComponent",
  data() {
    return {
      editableData: ""
    };
  },
  methods: {
    OnStringUpdated(newString) {
      this.editableData = newString;
    }
  },
  components: {
    Editable
  }
};
</script>

コメントする