Guide on generating virtual nodes from a string containing HTML tags using Vue 3

Investigation

I stumbled upon this solution for converting a simple SVG with one path layer into Vue2 vnode list and wanted to adapt it for Vue3.

After scouring resources, including the MDN docs, I couldn't find any pre-existing solutions. So, I decided to piece together my own based on my knowledge of Vue.

My Objective

I am looking for a method to translate an HTML string that contains a hierarchy of nodes into Vue Nodes.

Answer №1

My Solution

I implemented a recursive algorithm to traverse through the string, extract all nodes, and then add them to an array of vNodes that eventually bind together to form a single root node.

const parseHTML = (props, context) => {
  let {content} = props

  const div = document.createElement('div')
  div.innerHTML = content
const getAttributes = (node) => {const attrs = {}; for (const attr of node.attributes) attrs[attr.name] = attr.value; return attrs;}
  const convertToNodes = (nodes) => {
    return Array.from(nodes).map(element => {
      if (element.nodeType === 3) return h('span', element.nodeValue)

      if (element.childNodes.length)
        return h(element.tagName, getAttributes(element), convertToNodes(element.childNodes))

      return h(element.tagName, getAttributes(element), element.innerHTML)
    })
  }
  const vNodes = convertToNodes(div.childNodes)

  return h(`div`, {...context.attrs}, vNodes)
}

This function is enclosed within a functional component that can be included in a template:

// parseHTML.ts
import { h } from 'vue'

const parseHTML = (props, context) => {
  let {content} = props

  const div = document.createElement('div')
  div.innerHTML = content
const getAttributes = (node) => {const attrs = {}; for (const attr of node.attributes) attrs[attr.name] = attr.value; return attrs;}
  const convertToNodes = (nodes) => {
    return Array.from(nodes).map(element => {
      if (element.nodeType === 3) return h('span', element.nodeValue)

      if (element.childNodes.length)
        return h(element.tagName, getAttributes(element), convertToNodes(element.childNodes))

      return h(element.tagName, getAttributes(element), element.innerHTML)
    })
  }
  const vNodes = convertToNodes(div.childNodes)

  return h(`div`, {...context.attrs}, vNodes)
}

parseHTML.props = ['content']

export default parseHTML
<template>
<parseHTML :content="myContent"/>
</template>
<script setup>
const myContent = "<div><p>Hello <button>Click!</button> some more text</p><p><em>cursive <strong>and bold</strong> stuff</em></p></div>"
</script>

The parseHTML render function allows you to manipulate nodes as needed. In my scenario, I transformed a basic button generated by Tiptap into a customized Vuetify VBtn.

By inserting the following code after checking for a #text node, I successfully added a Vuetify button based on specific criteria:

...
      if (element.attributes.getNamedItem('data-id')?.value === 'richTextBtn')
        return h(VBtn, {onClick($event) { alert('hello!')}}, [element.innerHTML])
...

Looking Ahead

I trust this information will aid others facing similar challenges. Your feedback and alternative solutions are welcomed and appreciated!

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

Leveraging AJAX to call upon a designated php file

I have a selection of menu items on my webpage, each with different options: option1, option2, and option3. Additionally, I have corresponding PHP files on my server for each of these menu items: option1.php, option2.php, and option3.php. For simplicity, ...

Mastering the A-Frame Game Loop: Tips for Separating Logic and Rendering

Currently, I am experimenting with A-Frame and my Quest 2 headset in order to create a simple VR game. One particular challenge I am facing is understanding how to separate logic from rendering and establish a proper game loop. After discovering this tutor ...

What is the process for selecting the Node version when installing with .nvm?

I am having an issue with nvm in the terminal. When I try to run npm install <something>, it always installs the package in node version 9.4.0, regardless of the version set using nvm. Even after switching to node version 10.15.3 using nvm use v10.1 ...

After closing the box, make sure to hold it securely

After clicking the button and closing my box with jQuery (Toggle), I want the state of the box to be remembered after refreshing the page. If the box was closed, it should remain closed upon refresh, and if it was open, it should stay open. I am looking ...

Determining special characters within a string using VueJS

As a newcomer to VueJS, I am currently developing an application that requires a signup and login system. My goal is to inform the user if they have entered a special character such as /[&^$*_+~.()\'\"!\-:@]/. In order to achieve th ...

Vue.js: The href attribute in a link is different from the data

Having <a> href tag below, clicking will redirect to www.duckduckgo.com, with the value of page.publicWebsite being displayed as well. {{page.publicWebsite}} shows www.duckduckgo.com. Is everything functioning correctly? https://i.stack.imgur.com/ ...

The PHP server is having trouble accepting a JavaScript object sent via AJAX

Hey there, I'm currently facing an issue with sending fields from a form saved in a JavaScript object to a PHP server. I have set up the AJAX communication, but when I try to access the object in PHP, I notice that the length is showing as 0 during de ...

What method can I use in webpage coding to achieve this special highlight effect, and what is the official term for it?

Need help figuring out how to make an icon change from blue to white when selected. I've searched through Bootstrap, CSS, and HTML, but haven't found the solution yet. Any suggestions would be appreciated! https://i.stack.imgur.com/RK1PD.png ...

Navigate to a new route in Vue Router and pass parameters as part of the navigation

I have a component that handles programmatic routing based on external data retrieved in the App.vue component and passed down to child components as props. In the child component, the external data is accessed like this: props: { externalData: Array ...

Tips for reducing the amount of repetitive code in your reducers when working with redux and a plain old JavaScript object (

When it comes to writing reducers for Redux, I often encounter challenges. My struggle usually lies in wrapping a lot of my state manipulation in functions like .map and .slice. As the structure of my state object becomes more complex, the reducers become ...

Is there a way to adjust the transparency of individual words in text as you scroll down a page, similar to the effect on https://joincly

Is there a way to achieve a text filling effect on page scroll similar to the one found here: . The specific section reads: "Deepen customer relationships. Own the brand experience. Add high margin revenue. Manage it all in one place. Get back your pr ...

Implement an event listener on the reference obtained from the React context

Within the React context provider, a ref is established to be utilized by another component for setting a blur event listener. The issue arises when the blur event fails to trigger the listener. The following is a snippet of code from the context provider ...

Sending target information as a property argument

I have encountered an issue with my app's two Bootstrap modals. It seems that many people are facing problems with opening the second modal. Is it possible to pass data-target and modal id properties as props in this manner: data-target={props.da ...

Dealing with ASP.NET forms that involve a javascript plugin generating substitute input

I'm facing an issue with my ASP.NET MVC webpage where I submit a form using AJAX in the following way: function ValidateFormAndAjaxSubmit(formId, callingElement) { if (IsNotDblClick(callingElement.id)) { var _form = $("#" + formId); ...

Tips for organizing a Material UI DataGrid table effectively while keeping the total sum rows within the DataGrid unaffected

Check out my codeSandbox link here: https://codesandbox.io/s/making-sum-of-column-in-datagrid-mui-zjzfq6?file=/demo.js I've noticed that when I sort by ascending, the subtotal, total, and tax rows move up, but when I sort by descending, they move dow ...

Establish the predefined date for the air-datepicker

I am currently utilizing the air-datepicker inline feature. My objective is to establish the starting date for it. Below is the script detailing my attempt: export function load_datepickers_inline():void { const search_legs_0_datepicker = $("#search_leg ...

Is it possible to verify with Jest that ReactDOM.render has been called if it's enclosed in a conditional statement?

I have a React component that conditionally calls ReactDOM.render when root === true to satisfy flow: import React from 'react' import ReactDOM from 'react-dom' import App from './App' const root = document.getElementById(& ...

Breaking down a lengthy series of items into several smaller lists

I have created a <ul> using the code below: function ListItem(props) { return <li>{props.value}</li>; } function ListLinks() { const listItems = footerLinks.map(section => { return section.data.map(({id, name, to}) => { ...

Error encountered with select2 when using a remote JSONP dataset

When attempting to query the Geonames data using select2, everything seems to work fine with formatting the results. However, an error occurs once the results are populated, which I suspect is preventing the formatSelection function from running properly. ...

What steps should I take to resolve npm start issues within my Node.js application?

Upon completing the npm install, I attempted to run npm start for my project. Unfortunately, an error was displayed. What steps can be taken to resolve this issue?view image of the error here ...