pages/plugins/nuxt-html.ts
2024-09-24 05:27:13 -05:00

68 lines
2.1 KiB
TypeScript

// taken from https://www.trpkovski.com/2024/03/24/is-it-possible-to-use-nuxt-link-in-content-rendered-with-v-html
const idsWithListeners = new Set<string>();
function generateUuid() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0,
v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
function assignAnchorsIds(html: string) {
const domParser = new DOMParser();
const doc = domParser.parseFromString(html, "text/html");
const anchors = doc.querySelectorAll("a");
anchors.forEach((anchor) => {
if (!anchor.hasAttribute("id")) {
anchor.setAttribute("id", generateUuid());
}
});
return doc.documentElement.outerHTML;
}
function convertAnchorToNuxtLink(html: HTMLElement) {
const anchors = html.querySelectorAll("a");
anchors.forEach((anchor) => {
if (anchor.id && !idsWithListeners.has(anchor.id)) {
const link = document.getElementById(anchor.id);
link?.addEventListener("click", (event) => {
event.preventDefault();
const to = anchor.getAttribute("href");
if (to) {
useNuxtApp().$router.push(to);
}
});
// Add the ID to the Set
idsWithListeners.add(anchor.id);
}
});
}
function removeListeners() {
idsWithListeners.forEach((id) => {
const link = document.getElementById(id);
link?.removeEventListener("click", () => {});
idsWithListeners.delete(id);
});
}
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive("nuxtHtml", {
mounted(el: HTMLElement, binding) {
el.innerHTML = assignAnchorsIds(binding.value);
convertAnchorToNuxtLink(el);
},
updated(el, binding) {
el.innerHTML = assignAnchorsIds(binding.value);
convertAnchorToNuxtLink(el);
},
unmounted() {
removeListeners();
},
});
});