Alternative to v-html in Vue.js: Using DOM manipulation as a

Coming from my background in React with JSX, I am used to setting HTML using code like

var test = <div>i am a div</div>

It is common practice for me. Although I know that the same result can be achieved using v-html in Vue, I was curious about whether it is the best and safest approach when looking at the following code:

This is a Vue component

<template src="./templates/General.html"></template>

<script>
  export default {
      name: 'guide-general',
      data: function() {
          return {
              guides: [
                  {
                      title: "first",
                      description: "First description"
                  },
                  {
                      title: "second",
                      description: "Second description"
                  },
                  {
                      title: "third",
                      description: `<ul>
                        <li>
                            test
                        </li>
                      </ul>`
                  }
              ]
          }
      },
      methods: {

      }
  }
</script>

<style scoped>
</style>

And here is the HTML template

<article>
    <div v-for="guide in guides">
        <h4>{{ guide.title }}</h4>
        <div v-html="guide.description">
        </div>
    </div>
</article>

Answer №1

When incorporating HTML rendering from a vue property, it is necessary to utilize the v-html directive. In cases where users can alter the source of the HTML, precautions must be taken to address potential XSS vulnerabilities by sanitizing inputs before sending data back to the server (consider using a trusted external library for this purpose). However, if user modifications are not a factor, then there should be no significant security risks associated with employing v-html.

Allowing users to edit data exposes your application to security threats, so it's crucial to anticipate and mitigate these risks accordingly.


Latest Update: June 2021

To clarify, when static HTML rendering suffices and only conditional display of predetermined templates is needed, you can implement conditional rendering within your Vue template as shown below:

<script>
    export default {
      name: 'guide-general',
      data: function() {
          return {
              guides: [
                  {
                      title: "first",
                      description: "First description",
                      as_list: false
                  },
                  {
                      title: "second",
                      description: "second description",
                      as_list: false
                  },
                  {
                      title: "third",
                      description: "test",
                      as_list: true
                  }
              ]
          }
      },
      methods: {

      }
  }
</script>

<article>
    <div v-for="guide in guides">
        <h4>{{ guide.title }}</h4>
        <div v-if="guide.as_list">
            <ul>
                <li>{{guide.description}}</li>
            </ul>
        </div>
        <div v-else>{{guide.description}}</div>
    </div>
</article>

In essence, if dynamic HTML rendering is unnecessary and only conditional HTML display is required, leverage v-if along with appropriately structured data to facilitate correct conditional rendering. Alternatively, consider structuring JSON data to dynamically generate predefined components using

<component :is="component.name">
(note that this approach is intricate and not fully covered in this response).

Answer №2

For those seeking a method to display HTML content restricted to specific tags, one approach is to parse the HTML string and utilize render functions to dynamically construct the template. By adopting this method, you can develop your own secure HTML renderer and eliminate the need for using v-html and external libraries. Below is a brief example illustrating how such an HTML view component could be structured:

HtmlView.vue

<script>

import { h } from "vue";

export default {
  props: {
    html: {
      type: String,
      required: true,
    },
    tags: {
      /** @type {import("vue").PropType<String[]>} */
      type: Array,
      default: () => ["b"]
    },
  },
  setup(props) {
    const html = parseHTML(props.html);

    // return the render function
    return () => {
      const output = [];
      html.childNodes.forEach(childNode => {
        output.push(renderNode(childNode, props.tags));
      });
      if (output.length == 1) {
        return output[0];
      } else if (output.length == 0) {
        return "";
      }
      return output;
    };
  }
};

const parseHTML = (html) => {
  var template = document.createElement("template");
  template.innerHTML = html;
  return template.content;
};

/**
 *
 * @param {Node} node
 * @param {String[]} tags
 */
const renderNode = (node, tags) => {
  const nodeType = node.nodeType;
  if (nodeType == 1) {
    const nodeName = node.nodeName.toLocaleLowerCase();
    if (tags.includes(nodeName)) {
      const children = [];
      node.childNodes.forEach(childNode => {
        children.push(renderNode(childNode, tags));
      });
      return h(nodeName, children);
    }
  }
  return node.textContent;
};

</script>

The tags property of the component restricts the supported tags. In this example, only the <b> tag is supported by default.

It's worth noting that the provided sample will exclusively render DOM Nodes of type 1 (ELEMENT_NODE) and will exclude any attributes of the node. However, it can be easily expanded to include attributes if necessary.

I have included a functional demo here.

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

Having trouble running nodemon on my Windows 10 machine

Recently, I started diving into Node.js and managed to run my first node app successfully, albeit without using nodemon. To remedy this, I globally installed nodemon by running npm install -g nodemon, which went smoothly. However, upon executing nodemon in ...

Codeigniter dropdown sending incorrect value to controller

When I choose an option from the dropdown list and click the submit button in the modal, the selected value is not being posted to the controller as expected. Below is my view code: <div class="form-group"> <div class="col-md-4"> ...

JavaScript Promise Synchronization

I have a JavaScript function that returns an object using promises. The first time the function is called, it fetches the object, but for subsequent calls, it returns a cached instance. To simulate the fetching process, I've added a delay. var Promis ...

How can I pass function arguments dynamically to a nested function in Node.js?

Currently using Node 6.11.0, I am attempting to accomplish the following dynamically: const parentFunc = (arg1, arg2, arg3, arg4) => { childFunc('foo', arg1, arg2, arg3, arg4); }; I have attempted this method (without success): const pare ...

Leveraging jQuery for validating a value by comparing it to another input value

Currently, I am working on a validation task for input fields related to hour rates. The aim is to ensure that once a base hour input is added, the other input values cannot exceed the value entered in the first base rate column. Essentially, the initial i ...

Swapping out the entire vue.js container

I have a custom vue.js widget that I initialize like so: var myWidget = new Vue({ el: '#widget-container', methods: { loadData:function() { // custom functionality here } }, }); The HTML structure is as f ...

The event listener function is not functioning properly on images generated by JavaScript

I'm currently working on placing multiple images on a grid in the center of the page and would like to include a function that triggers when each individual image is clicked. The images are dynamically created using JavaScript and inserted into the do ...

Exploring the parent of an HTML element / Finding a child element with unique content

Struggling with a challenge from frontendmentor that involves JavaScript. I need help on making the script detect if a P tag contains 0 as its content and then applying a different style to the containing div. <section class="pledge-box sub-section ...

Tips for preventing Django template from showing up prior to VueJS rendering

I am currently facing an issue with rendering a Django template using VueJs through CDN. Upon loading the page, I notice that the raw Django code is displayed initially before being rendered by VueJs, which typically takes less than a second. To fetch dat ...

Automated Form Submission using Selenium

Trying to automate form submission with selenium, but encountering JavaScript issues causing exceptions. Instead of directing submit to the form action, Selenium is redirecting it to localhost. Any help on why this is happening and how to fix it? Here&apo ...

Unable to bring in the Firebase Firestore Colletion

When working on my next app, I encountered an error while trying to call a Firestore Collection. The specific error message that appeared when I ran the app was: FirebaseError: Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicat ...

I struggled to connect my Vuex actions with a component

I've been trying to connect my vuex actions, but I keep encountering the error Property or method "logout" is not defined on the instance but referenced during render.. Here's a snippet of my component code: <template> <header id="hea ...

Obtaining NodeJS from a mysterious subdirectory

-- plugins ---- myplugin1 ------ core ---- myplugin2 ------ core If this represents the directory structure, is there a method to import all core directories from plugins without specifying the specific plugin names like myplugin1? require('/plugins ...

What is the best approach to modify the model value when a controller function is triggered?

This is the controller: onSelectRow : function(id){ setTimeout(function () {$scope.getSelectedRow(); }, 0); },}; $scope.getSelectedRow = function(){ var grid = $("#patientgrid"); var rowKey = grid.jqGrid('getGridPara ...

Insert multiple text box values as a new entry in an SQL database

Currently, I am implementing a code snippet to incorporate additional text boxes within a form. The main purpose is to enable users to input multiple languages they are proficient in. <script> jQuery(function($) { var i = 0; ...

What is the best way to eliminate an iframe from inside itself using javascript?

Although I am aware of similar questions being asked before, I find myself having to raise this question again along with some attached code because I am struggling to figure it out. In my JSF project, I have two .xhtml files. The first one is mainPage.xht ...

When using the JavaScript .push method, existing array values are overwritten as the new values are duplicated

(using Angular) I am working on creating an array of arrays to be represented in a chart. The data values are being generated through the following code snippet: $scope.getData = function() { $scope.series.length = 0 $scope.allData.length = 0 var da ...

Designing Angular web elements within Angular

Although Angular natively supports Web components, I am unsure about how to style a web component with SCSS without the styles affecting the hosting page. When rules are defined in a component's .scss files, they should only apply to that specific co ...

Turning off the AngularJS dropdown multiselect functionality

I need a directive that can disable the dropdown multiselect in AngularJS. It's currently functioning properly when the checkbox is changed, but it's not working when the controller initializes. I want the dropdown to be disabled based on the val ...

How can we ensure that React state remains unaltered when modifying an array set to state?

Hope you're having a wonderful day! I'm encountering a significant problem with React. I have a state that contains an array. Within a function, I create a copy of the state in a new variable. However, any modifications made to this new variable ...