💨

【Vue】v-html部分のaタグをルーター遷移に変える小技

2022/06/13に公開

Vue.jsでCMSなどから内容をHTMLとして表現する場合、v-htmlを用いて実装することが多いと思います。v-html内のリンクはたとえ同一サイト内であっても<a>タグとして出力されるので通常のページ遷移(非SPA挙動)になります。

このようなv-html<a>タグもSPA的なルーター遷移に変更したい場合の方法です。どっちかというと小技のたぐいなので、万能ではないと思います。しかし、多くの場面では有効でしょう。

▼テンプレート

<template>
  <div v-html="hoge" ref="vHtmlElement"></div>
</template>

▼スクリプト(setup記法で書いています)

const vHtmlElement = ref();
const router = useRouter();
onMounted(() => {
  vhtmlElement.value?.querySelectorAll("a").forEach((a) => {
    const href = a.getAttribute("href");
    if (!href) {
      return;
    }
    const regex = /^https?:\/\//;
    // 外部リンクかどうか?
    const isExternalLink = regex.test(href);
    a.addEventListener("click", (event) => {
      if (!isExternalLink) {
        event.preventDefault();
        router.push(href);
      }
    });
  });
});

仕組みは単純で、v-htmlで生成されたコンテンツに対しquerySelectorAll("a")<a>タグを探します。event.preventDefault()でデフォルト挙動をキャンセルし、代わりにrouter.push()でルーティングさせています。

そのままだと外部リンクも同様の処理が施されてしまうので、href属性の中身をみてhttp(s)://で始まるものは弾いています。この条件だとサイト内リンクでも絶対パスで出力されるCMSでは効きません。追加でサイト内リンクであることを確かめる処理と目的のURLを抜き出す処理が必要です。また、#などから始まるページ内リンクにも無効です。

細かい調整をしだすと少したいへんですが、うまくコンポーザブル関数(フック関数)に切り出せば再利用も可能です。

▼コンポーザブル関数

export const useConvertRouting = (target) => {
  const router = useRouter();
  onMounted(() => {
    target.value?.querySelectorAll("a").forEach((a) => {
      const href = a.getAttribute("href");
      if (!href) {
        return;
      }

      // ルーティング遷移にしたい判定処理
      const doRouting = ...

      a.addEventListener("click", (event) => {
        if (doRouting) {
          event.preventDefault();
          router.push(href);
        }
      });
    });
  });
};

▼テンプレート側

const vHtmlElement = ref();
useConvertRouting(vHtmlElement); // v-html内のaタグを変換する

v-html内の<a>タグをルーター遷移にしたくなったら、試してはどうでしょうか?

Discussion