2
2

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 3 years have passed since last update.

Vue3 で contenteditable な要素を v-model でバインドする

Last updated at Posted at 2021-07-19

はじめに

基本的には以前書いた記事Vue3 化したものですが、以下の2点手を入れています。

  • placeholder に対応
  • 更新時に余計な空白を削除

TL;DR;

出来上がりのコードはこちら。

<template lang="pug">
.text-input(
  ref = "input"
  :contenteditable = "true"
  @input = "update"
  @focus = "focus"
  @blur = "blur"
)
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, watch } from "@vue/runtime-core";

export default defineComponent({
  name: "MultilineInput",
  props: {
    modelValue: String,
  },
  emits: {
    "update:modelValue": null,
  },
  setup(props, ctx) {
    const input = ref<HTMLElement>();
    const focusIn = ref<boolean>();

    const updateValue = () => {
      if (!focusIn.value) {
        let t = "";
        if (props.modelValue) {
          const elem: string[] = props.modelValue.split("\n");
          for (const e of elem) {
            t += "<div>" + (e !== "" ? e : "<br>") + "</div>";
          }
        }
        if (input.value) input.value.innerHTML = t;
      }
    };

    watch(() => props.modelValue, updateValue);

    const update = (e: Event) => {
      const target: HTMLElement = e.target as HTMLElement;
      const breakdown = target.innerHTML
        .replaceAll("<div><br></div>", "\n")
        .replaceAll("<div>", "\n")
        .replaceAll("</div>", "")
        .replaceAll("<br>", "\n").\
        .replaceAll(/<("[^"]*"|'[^']*'|[^'">])*>/g, "");
      ctx.emit("update:modelValue", breakdown);
    };

    const focus = () => {
      focusIn.value = true;
    };

    const blur = () => {
      focusIn.value = false;
    };

    onMounted(() => {
      updateValue();
    });

    return {
      input,
      update,
      focus,
      blur,
    };
  },
});
</script>

<style lang="stylus">
[contenteditable=true]:empty:before
  content: attr(placeholder);
  pointer-events: none;
  display: block; /* For Firefox */
  color rgba(255, 255, 255, 0.5)
</style>

改良点

placeholderに対応

こちらの記事で一発対応できました。

以前は無かった style タグの中に該当するコードを書いています。

更新時に余計な空白を削除

久々に使って悩んだのですが、contenteditable 内で Enter を入れると、

<div><br></div>

といった要素が挿入されます。(shift + Enter なら <br> のみ)
以前の記事では innerText で取ってきた値をそのまま使用していたのですが、この状態では余分な改行コードが入った string が取得されます。
ここを、innerHTML で取得した値から 力業で 正しい値を抜き出すように変更しました。

入り込んだHTMLコードを削除(追記 2021/10/19)

運用中、なぜか <span style="font-size:12pt">やっほー</span> みたいな文字列が入ってきてしまった事があったので、これも 力業で ねじ伏せます。

        .replaceAll(/<("[^"]*"|'[^']*'|[^'">])*>/g, "");

おわりに

正しい値を取得するところでかなり無理やりな事をしており、きれいな実装とは言えませんが、この記事が同じ事でお困りの方の一助になれれば幸いです。

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?