Incorporate animated SVG directly into Vue templates without using any additional enclosures

I need to incorporate dynamic SVG code into my <template> without using v-html or any wrapper around it.

The desired end result should look like the following, but template does not support v-html to achieve this. If there is a way to accomplish this result with v-html or any workaround, that would be perfect.

<template>
  <svg>
     
  </svg>
<template>

Here is my current code:

<template>
  <div>
    <span v-html="svgData"></span>
  </div>
</template>

<script setup lang="ts">
  import { computed } from "vue";
  import type { mainIcon } from "tester";
  import { completeDataSet } from "tester";

  const props = defineProps<{
    icon: mainIcon;
  }>();

  const iconPassed = completeDataSet.find((item) => item.name === props.icon);
  const svgData = computed(() => iconPassed?.data);
</script>

Note: The SVG data is returned as a string by a third-party library, so I cannot modify the SVG structure.

Thank you in advance.

Answer №1

The way in which svgs are stored and retrieved determines the answer to this question. Certain libraries, like @mdi/svg, do not provide the svg file itself but only the path value. In such cases, the path value can be used directly without the need for a wrapper.

<template>
  <svg viewBox="..">
    <path :d="path" />
  </svg>
</template>

On the other hand, if physical svg files are involved, such as when they are stored in src/assets as separate files, they can be loaded and used as components. Below is an example using webpack:

<template>
  <component :is="component" />
</template>

<script>
const iconComponents = {}
const req = require.context('@/assets/', true, /\.svg$/)
req.keys().forEach((filename) => {
  const nameParts = filename.split('/')
  const name = nameParts[nameParts.length - 1].replace(/\.svg$/, '')
  iconComponents[name] = req(filename)
})

export default defineComponent({
  props: {
    iconName: {
      type: String,
      required: true
    }
  }
  computed: {
    component() {
      return iconComponents[this.iconName]
    }
  }
})
</script>   

Answer №2

Instead of using the v-html directive, the icon component has the ability to manually replace the contents of an <svg> element with the SVG markup imported from a file:

  1. Start by assigning a template ref to an <svg> element within your component template:
<template>
  <div>
    <svg ref="svgRef"></svg>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const svgRef = ref(null)
</script>
  1. Next, add a watcher that updates the inner HTML of the SVG ref with the corresponding SVG markup based on the value of the icon property. Be sure to await a nextTick to ensure the changes take effect in the DOM:
<script setup>
import { computed, nextTick, watchEffect, ref } from 'vue'
import * as icons from 'simple-icons/icons'

const props = defineProps({ icon: String })
const iconSet = Object.entries(icons)
const svgData = computed(() => iconSet.find(([iconName, icon]) => iconName === props.icon)?.[1].svg)
const svgRef = ref(null)

watchEffect(async () => {
  if (svgRef.value) {
    svgRef.value.innerHTML = svgData.value
    await nextTick()

    ⋮
})
</script>
  1. It is important to retain the existing SVG ref element in the virtual DOM for proper tracking by Vue. If the first child of the SVG ref is another <svg>, replace the contents of that inner <svg> with its children, as the SVG ref itself is already an <svg>. Make sure to copy the inner <svg>'s attributes to the SVG ref before performing the replacement:
<script setup>
⋮

const removeAttributes = (el) => {
  ;[...el.attributes].forEach((attr) => el.removeAttribute(attr.name))
}
const copyAttributes = (fromEl, toEl) => {
  ;[...fromEl.attributes].forEach((attr) => toEl.setAttribute(attr.name, attr.value))
}

watchEffect(async () => {
  if (svgRef.value) {
    ⋮

    const firstChild = svgRef.value.firstChild
    if (firstChild.tagName.toLowerCase() === 'svg') {
      removeAttributes(svgRef.value)
      copyAttributes(firstChild, svgRef.value)

      // swap inner SVG with its children, as the container is already an SVG
      firstChild.replaceWith(...firstChild.children)
    }
  }
})
</script>

check out the demo

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Attempting to establish a connection with Redis through the combination of TypeScript and Node.js

Currently, I am attempting to establish a connection with Redis using TypeScript and Node.js. However, I am encountering an issue where **error TS2693: 'RedisStore' is designated as a type but is being utilized as a value in this context.** let r ...

The ion-slide-box does not update after the active-slide has been changed using $index

I'm currently facing an issue with the controller that corresponds to this specific view. .controller('MenuCtrl', ['$rootScope','$scope','$state','$timeout','$ionicSlideBoxDelegate', functio ...

Steps for converting a tsx file into a js file in React

Currently, I am in the process of a React Project and have numerous tsx files that I aim to convert for utilization as JavaScript within my project. What would be the best approach to achieve this task? ...

How can I make a div blur as I scroll through the page

How can I make each item in my timeline focused one at a time as the user scrolls, instead of having all items in focus simultaneously? Any ideas or suggestions would be appreciated. Here is my fiddle and here is my code $(document).ready(function(){ ...

Is there a way to adjust the dimensions of Google charts within a Bootstrap tab for a more customized appearance?

I've been working on a page that features tab navigation using Twitter Bootstrap and I want to include Google charts within each tab's content. However, I've encountered an issue where the charts are appearing in different sizes despite spec ...

The error `Error occurred when rendering: Users' data cannot be mapped` indicates a problem with the rendering process

Utilizing useSWR for data retrieval from an endpoint and attempting to display all returned data, I encounter the following error: unCaught (in promise) fetchedUsers.map is not a function uncaught TypeError: fetchedUsers.map is not a function The provided ...

What could be causing the remove attribute API to not function properly only for the "is" attribute?

var divElement = document.createElement("div"); var innerHTMLText = "<div id='issue' is='if'> some content </div>"; divElement.innerHTML = innerHTMLText; document.body.appendChild(divElement); var newDivElement = document.qu ...

Is there a way to activate the final form when submitted?

I am facing an issue with React Final Form. Despite following the example in the official documentation, I am still struggling to understand why my form is not triggering the onSubmit function as in the example. I am also grappling with understanding the p ...

Executing synchronous animations in Jquery with callback logic

My jQuery plugins often rely on user-defined callbacks, like in the example below: (function($) { $.fn.myplugin = function(options) { var s = $.extend({}, options), $this = $(this); if (typeof s['initCallback'] = ...

Data loss from AngularJS multipartForm directive when redirecting to different routes

Having trouble with an Excel file uploader and data parsing in the routes? It seems like the FormData is getting lost when sent through the $http service route. Any advice or experience on how to handle this issue would be greatly appreciated! Html View: ...

Generate a custom website using React to display multiple copies of a single item dynamically

As a newcomer to React and web development, I've been pondering the possibility of creating dynamic webpages. Let's say I have a .json file containing information about various soccer leagues, structured like this: "api": { "results": 1376, ...

How can an Angular directive effectively serve as a front-facing interface for interacting with other elements?

This question delves into the realm of Web Components, with the examples being written in Angular for its versatility in handling certain issues (such as replace even though it's deprecated) and familiarity to many developers. Update After consideri ...

A guide on incorporating oAuth 2.0 into Tizen

Currently, I am in the process of incorporating Google authentication using oAuth 2.0 in Tizen. I am following the guidelines provided here. Despite successfully obtaining the user code as instructed in the link, I keep receiving an invalid request error w ...

Guide on initializing a Redux toolkit state with an array of objects or local storage using TypeScript

Currently, I am attempting to set an initial state (items) to an array of objects or retrieve the same from localStorage. However, I am encountering the following error. Type 'number' is not assignable to type '{ id: string; price: number; ...

Choosing particular contenteditable divisions using jQuery

Consider the following HTML structure for a specific type of blog post editor: <div class="entry"> <div class="title" contenteditable="true"> <h2>Title goes here</h2> </div> <div class="content" contenteditable ...

Troubleshooting TypeScript errors in a personalized Material UI 5 theme

In our codebase, we utilize a palette.ts file to store all color properties within the palette variable. This file is then imported into themeProvider.tsx and used accordingly. However, we are encountering a typescript error related to custom properties as ...

Utilizing Angular 2 for Integration of Google Calendar API

I recently attempted to integrate the Google Calendar API with Angular 2 in order to display upcoming events on a web application I am developing. Following the Google Calendar JavaScript quick-start tutorial, I successfully managed to set up the API, inse ...

Utilizing web components in React by solely relying on a CDN for integration

I have a client who has provided us with a vast library of UI elements that they want incorporated into our project. These elements are stored in javascript and css files on a CDN, and unfortunately I do not have access to the source code. All I have at my ...

I am experiencing issues with my buttons not functioning properly after implementing the fetch API in conjunction with express.js

I'm facing a peculiar issue with buttons that are meant to display templates on the client page. This code is executed on the client side. The primary function of this class is to allow the user to click a button, send a request, receive a response c ...

experimenting with implementing CSS transitions in VueJS

Check out this Vue.js transitions guide The documentation seems to be lacking clarity in this section. It doesn't specify any additional installations required, but the transition doesn't seem to work as expected. According to the doc, simply a ...