Home > Software engineering >  Vue 3 replacing the HTML tags where v-html is called with the provided HTML
Vue 3 replacing the HTML tags where v-html is called with the provided HTML

Time:09-28

This is about a Vue 3 app with Vite, not webpack.

For now, as you can see from this issue on vite's issue page, vite doesn't have a convenient way of inlining SVGs without using external plugins. Vite does however, support importing files as raw text strings. As such, I had an idea to use this feature and to inline SVG's by passing the raw SVG strings into an element's v-html.

It actually works great, the SVG shows up on the page as expected and I can do the usual CSS transforms (the whole purpose of inlining them like this), but it's not perfect. As it currently stands, the element that receives the v-html directive simply places the provided HTML nested as a child. For example, if I do <span v-html="svgRaw" />, the final HTML comes out something like this

<span>
  <svg>
    <!-- SVG attributes go here -->
  </svg>
</span>

Is there any way for me to essentially replace the parent element on which v-html is declared with the top-level element being passed to it? In the above example, it would mean the <span> just becomes an <svg>

CodePudding user response:

You could create a custom directive that hoists the wrapper element's contents to the parent node:

  1. Use app.directive() to create a global directive, named v-hoist:

    // main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    
    createApp(App)
      .directive('hoist', el => {
        // move child to parent
        if (el.tagName === 'TEMPLATE') {
          el.parentNode?.appendChild(el.content)
        } else {
          ;[...el.children].forEach((child) => el.parentNode?.appendChild(child))
        }
    
        // remove wrapper
        el.remove()
      })
      .mount('#app')
    
  2. In your component, include v-hoist on the v-html wrapper element (also works on <template> in Vue 3):

    <span v-html="svgRaw" v-hoist />
    <!-- OR -->
    <template v-html="svgRaw" v-hoist />
    

demo

CodePudding user response:

Coming from Vue2. I think this still works:
Instead of span you can use the special Vue tag template:

<template v-html="svgRaw" />

This will not render <template /> as a tag itself, but render the elements given in v-html without a parent element.

  • Related