💨
【Vue】v-html部分のaタグをルーター遷移に変える小技
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