skip to Main Content

I am trying to work on a project where we allow the user to create HTML elements and attach the appropriate event listener code to it (https://codesandbox.io/s/interactive-objects-tjvvhv?file=/src/App.js)

In the following code, I want to read the formData which takes in three fields, 1)element-type, 2)event-listener-type, and 3)JS code that will go into the event listener. I know that I can use createElement and attach to the DOM, but I am not sure on how to add the event listener code from the form that DOM element.

import "./styles.css";

export default function App() {
  function handleFormSubmit(e) {
    e.preventDefault();
    console.log(e);
    //create the a new element with the event listener code and append it div#display
  }
  return (
    <div className="App">
      <form onSubmit={handleFormSubmit}>
        <label for="element-type">element-type</label>
        <input type="text" name="element-type" />
        <br />
        <label for="event-listener">event-listener</label>
        <input type="text" name="event-listener" />
        <br />
        <label for="code">code</label>
        <textarea name="code" rows="7" cols="50" />
        <br />
        <input type="submit" />
      </form>
      <div id="display"></div>
    </div>
  );
}

Any ideas in this direction are appreciated. Thanks a lot.

3

Answers


  1. Chosen as BEST ANSWER

    I found that eval() executes the code immediately so,I solved the above problem through "new Function" as follows:

    import { createElement } from "react";
    import "./styles.css";
    
    export default function App() {
      function handleFormSubmit(e) {
        e.preventDefault();
        var element = document.getElementById("element-type").value;
        var eventListener = document.getElementById("event-listener").value;
        var code = document.getElementById("code").value;
    
        var domElement = document.createElement(element);
        var eventFunction = new Function(code);
    
        domElement.addEventListener(eventListener, eventFunction);
    
        document.getElementById("display").appendChild(domElement);
      }
      return (
        <div className="App">
          <form onSubmit={handleFormSubmit}>
            <label for="element-type">element-type</label>
            <input type="text" name="element-type" id="element-type" />
            <br />
            <label for="event-listener">event-listener</label>
            <input type="text" name="event-listener" id="event-listener" />
            <br />
            <label for="code">code</label>
            <textarea name="code" rows="7" cols="50" id="code" />
            <br />
            <input type="submit" />
          </form>
          <div id="display"></div>
        </div>
      );
    }
    
    

    credits : @Konrad


  2. Here’s a version that uses iframe safely:

    import "./styles.css";
    import { useState } from "react";
    
    export default function App() {
      const [srcDoc, setSrcDoc] = useState("");
    
      function handleFormSubmit(e) {
        e.preventDefault();
    
        const form = new FormData(e.target);
    
        console.log(form);
        const type = form.get("element-type");
        const content = form.get("element-inner");
        const eventName = form.get("event-listener");
        const eventCode = form.get("code");
    
        setSrcDoc(`
        <script>
        const element = document.createElement("${type}")
        element.innerHTML = "${content}"
        element.addEventListener("${eventName}", (event) => {
          ${eventCode}
        })
        document.documentElement.appendChild(element)
        </script>
        `);
      }
      return (
        <div className="App">
          <form onSubmit={handleFormSubmit}>
            <label for="element-type">element-type</label>
            <input type="text" name="element-type" />
            <br />
            <label for="element-inner">element-inner</label>
            <input type="text" name="element-inner" />
            <br />
            <label for="event-listener">event-listener</label>
            <input type="text" name="event-listener" />
            <br />
            <label for="code">code</label>
            <textarea name="code" rows="7" cols="50" />
            <br />
            <input type="submit" />
          </form>
          <div id="display">
            <iframe srcdoc={srcDoc} />
          </div>
        </div>
      );
    }
    
    
    Login or Signup to reply.
  3. Since you are using React (If I am not wrong)

    I would suggest making use the hooks in React to get your job done, I did not spend time much on the problem but ideally. First suggestion since you are doing something of this sort that has some complexity is to use TypeScript or get the code type checked to get rid of those edge cases.

    Some things I would keep in mind to get this problem started with :

    • Use TypeScript

      • The elements field can be type safe ensuring right input from users
      • The event type can be type safe making it easy to map it to the right event
        listener
    • Make use of React Hooks

      • useRef and useState hooks to accept

            const [formData,setFormData] = 
            useState({element="",eventListener="",code=""});
            const {element,eventListener,code} = formData;
        
        
            //Use the onSubmit method to create a react element
            //Inside the method add a similar logic below 
            //Add a useRef to display element 
            const newElementGenerated = React.createElement(element, 
            {ref:newElement});
            displayRef.current!.appendChild(newElementGenerated);  
        
            newElement.current!.addEventListener( eval(generated code) );
        
    • Use JavaScript eval() method to convert string to JavaScript code

      const userReturnedString = "console.log("Run the code")";
      eval(userRetrunedString); //Runs the string as code
      
    • Use React in built methods to manipulate the ReactDOM rather than using the native JavaScript methods since they are mutable unlike React, The output can be unpredictable in the React environment.

    I hope that provides some idea to actually put together a logic based on TypeScript and React to generate the output you desire.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search