Executing Javascript dynamically in VueJS: Learn how to run code from a string efficiently

Currently, I am developing a website with VueJS that enables selected users to upload scripts for automatic execution upon page load. For instance, here is an example of the type of script a user may input:

<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.5/howler.js"></script>
<script>
    var sound = new howler.Howl({
        src: ['./sample.mp3']
    )}.play();
</script>

After fetching this text from the API backend, it is stored in a string. However, I have encountered difficulty in getting it to execute properly. Is there a feature within VueJS that can handle executing JavaScript code within strings automatically?

For your information, below is a snippet of my code:

var temp_arr = utils.preprocess(vm.chapterInfo.Content)
vm.display = temp_arr[0]
vm.control_script = console.log(temp_arr[1])

// Unfortunately, none of the methods below have been successful
eval(vm.control_script)
document.getElementsByTagName("head")[0].appendChild(control_script)

Answer №1

The issue at hand is not specific to Vue, but rather a JavaScript one.

I'm assuming you are already aware of the security risks associated with allowing users to run JavaScript; it's generally not recommended. While sites like JSFiddle manage to do it effectively, ensuring safety would require a significant amount of effort and knowledge. Therefore, if you're not completely confident in your abilities, heed the advice of @WaldemarIce and proceed with caution!

With that warning aside, here are the steps you need to follow to make this work:

1) Load the external scripts:

loadScripts() {
    return new Promise(resolve => {

      let scriptEl = document.createElement("script");
      scriptEl.src = "https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.5/howler.js";
      scriptEl.type = "text/javascript";

      // Attach script to head
      document.getElementsByTagName("head")[0].appendChild(scriptEl); 
      // Wait for tag to load before promise is resolved     
      scriptEl.addEventListener('load',() => {
        resolve();
      });
    });
  }

In this snippet, I am simply inserting the external script into the document's header and creating a load event listener that resolves the Promise once the script has loaded.

2) With the external script now loaded, we can execute the remaining script. You'll need to remove any script tags, which can be done like so:

executeScript() {
  // remove script tags from string (this has been declared globally)
  let script = string.replace(/<\/?script>/g,"")
  eval(script)
}

From a Vue standpoint, you can then execute this within the created hook:

created() {
  this.loadScripts().then(() => {
    this.executeScript();
  });
},

I'll let you handle extracting the external scripts you wish to load from user input. For reference, here's a JSFiddle: https://jsfiddle.net/49dq563d/

Answer №2

Upon encountering this issue, I found it necessary to build upon the solution provided by @craig_h. The following example demonstrates how full embed code can be transmitted as a string (inclusive of HTML elements, scripts, and inline JS). This implementation leverages DOMParser.

<div ref="htmlDump"></div>
<script>
import Vue from "vue";
export default {
  ...
  methods: {
    cloneAttributes(element, sourceNode) {
      let attr;
      let attributes = Array.prototype.slice.call(sourceNode.attributes);
      while(attr = attributes.pop()) {
        element.setAttribute(attr.nodeName, attr.nodeValue);
      }
    }
  },
  mounted(){
    if(this.embedString && this.embedString.length > 0)
    {
      //Parse the code given from the API into a new DOM so we can easily manipulate it
      var parser = new DOMParser();
      var htmlDoc = parser.parseFromString(this.embedString, 'text/html');
      //Get the contents of the new DOM body and loop through.
      //We want to add all HTML elements to the page and run / load all JS
      var kids = [...htmlDoc.body.children];
      let len = kids.length;
      for (var i = 0; i < len; i++) {
        var item = kids[i];
        if(item.tagName == "SCRIPT")
        {
          //If we have a 'src' attribute then we're loading in a script
          if(item.hasAttribute('src'))
          {
            //Create a new element within the current doc to trigger the script load
            let scriptEl = document.createElement("script");
            //Copy all attributes from the source element to the new one
            this.cloneAttributes(scriptEl, item);
            //Attach script to the DOM to trigger it to load
            this.$refs.htmlDump.appendChild(scriptEl);
          } else {
            //if we don't have a 'src' attribute then we have some code to run
            eval(item.innerText);
          }
        } else{
          this.$refs.htmlDump.appendChild(item);
        }
      }
    }
  }
  ...
}
</script>

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

What is the best way to remove current markers on google maps?

This is how I implemented it in my project. The issue I'm facing is that the clearAirports function does not seem to clear any existing markers on the map or show any errors in the Google console. googleMaps: { map: null, init: function () { ...

What are the methods to alter validation for a Formfield based on the input from other Formfields?

My aim is to create a Form where input fields are required only if one or more of them are filled out. If none of the fields have been filled, then no field should be mandatory. I came across a suggestion on a website that recommended using "valueChanges" ...

Working with Multidimensional Arrays in VueJS

Designing a virtual toaster using VueJS, where different types of bread are toasted with varying results. The toaster should be able to toast sourdough, wheat, and rye, while rejecting white and English muffins. Additionally, rye should never burn, but t ...

Struggling with sending data to a modal in AngularJS?

I am dealing with the following code snippet $scope.currentTask = undefined; $scope.openModal = function (task, size, parentSelector) { var parentElem = parentSelector ? angular.element($document[0].querySelector('.modal-d ...

Calculating JS functions before images are loaded

Following up on a previous question, I am utilizing JavaScript code from another article to position a "content" div relative to a fixed div in my project. However, the issue arises when the positioning of the "content" div is calculated only after all the ...

The counter variable does not function properly within a setInterval function

I have encountered an issue with my scroll-counter variable inside the setInterval function. It increments by 1 each time the setInterval function runs, which is supposed to happen 20 times before stopping. However, I noticed that the value of this variabl ...

A dynamic AJAX menu showcasing content in a dropdown format, away from the conventional table layout

My dropdown select menu is correctly populating with data, but the output always appears outside of the table. Can anyone spot the issue in my code? Any suggestions or ideas are greatly appreciated! Thanks in advance, select.php <?php $q = $_GET[&apos ...

Insert a division into the table following every row

I'm working with a table that can be found here: https://codepen.io/anon/pen/bjvwOx Whenever I click on a row (for example, the 1st row 'NODE ID 1'), I want the div with the id #divTemplate to appear below that particular row, just like it d ...

I am facing an issue where reducing the size of the renderer is causing my click events to be offset. What steps can I

At the moment, my rendered model (which is grey in color) stretches across the entire width of the html page with the mesh centered within that width. I want to reduce this width but when I do so, the click event on the model seems to be misaligned. Three. ...

What is the best way to add user login information to the request pipeline in Express.js?

In my current project, I've been working on a middleware that is responsible for extracting the user model and attaching it to the request pipeline. Although I have successfully implemented a token extractor middleware that attaches the token to the r ...

javascript: revealing the identity of a click event handler

Here I am looking to create a click function with a specific name and parameters for the purpose of code reusability. This will allow me to write one generic function that can be used for common tasks like enabling users to delete various types of data. I ...

Managing Dependencies in Redux: Ensuring Proper Updates for Interconnected Resources

Let's say I have a redux state structure like this: { user: null, purchases: [], } The purchases are associated with a user, so whenever the user is updated, I also want to update the purchases (although there may be other times when purchases n ...

Vue has issued a warning stating that the type check for the "eventKey" prop has failed. The expected type was a String or Number, but an Array was provided instead. Additionally, it is advised to

The code I am currently using is producing the following errors in the console output: [Vue warn]: Avoid using non-primitive value as key, use string/number value instead. [Vue warn]: Invalid prop: type check failed for prop "eventKey". Expected String, ...

Maintaining the Readability of Promise Chains

Creating promise chains with arrays is a method I've become accustomed to. It's quite simple to follow along a chain of promises when each one fits neatly on its own line like so: myArray.map(x => convertX) .filter() .whatever() .etc() ...

What is the best way to transform data from a single form into a JSON array?

explore (handlebars) {{!< main}} <form class="ui form" action="/design/add" method="POST"> <div class="order_list"&yt; <h1>Add Product</h1> <br> {{!-- <input type="f ...

JavaScript example: Defining a variable using bitwise OR operator for encoding purposes

Today I came across some JavaScript code that involves bitwise operations, but my knowledge on the topic is limited. Despite searching online for explanations, I'm still unable to grasp the concept. Can someone provide insight into the following code ...

Issue with onClick event not firing when using option tag in React

onClick event seems to have an issue with the <option> tag. How can we successfully use the onClick event with the select option tags while assigning different parameters to each option? async function setLanguage(language) { ...

Managing the Response from an Ajax Call

Currently, I am in the process of developing a user registration form for my website and have implemented ajax to manage server-side processes. My main issue lies in effectively handling the response generated by my PHP code. The potential responses from t ...

How can a Vue component be created with a template that solely consists of a property's text?

Looking for a way to incorporate plain text within a Vue component template area? The current method involves wrapping the text in a div, but this causes layout issues when concatenating components with other text fragments. This results in awkward spacing ...

How can you obtain constructor parameter from JSON directly within the constructor itself?

I decided to store the fixed parameters required for creating an object in a JSON file as an array. My goal was to have the constructor of the object fetch these parameters from the file. Although reading a JSON file is efficient, I wanted to explore othe ...