admin管理员组

文章数量:1344227

I want to create a dynamic HTML template on click of a canvas.

This is what I tried:

var btn = document.createElement("div");
btn.classList.add("custom-signature-btn-row");
btn.classList.add("d-flex");
btn.innerHTML = `
    <div class="btn-grid d-flex mr-2">
        <span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
        <button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
            Signature
        </button>
        <div class="signature-dropdow" v-bind:class="{ active: isActive }">
            <ul>
                <li>
                    <div class="md-layout-item">
                        <div class="md-field">
                            <label for="movie">Assigned to Anyone</label>
                            <select name="movie" class="md-select" id="movie">
                                <option class="md-option" value="fight-club">Fight Club</option>
                                <option class="md-option" value="godfather">Godfather</option>
                            </select>
                        </div>
                    </div>
                </li>
            </ul>
        </div>
    </div>
`;

document.addEventListener('click', function(event) {
    if (event.target && event.target.classList.contains("pdf-canvas")) {
        console.log(event.target.parentNode);
        event.target.parentNode.appendChild(btn);
    }
});

The problem with above code is v-bind and v-on won't work with this approach.

I want to create a dynamic HTML template on click of a canvas.

This is what I tried:

var btn = document.createElement("div");
btn.classList.add("custom-signature-btn-row");
btn.classList.add("d-flex");
btn.innerHTML = `
    <div class="btn-grid d-flex mr-2">
        <span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
        <button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
            Signature
        </button>
        <div class="signature-dropdow" v-bind:class="{ active: isActive }">
            <ul>
                <li>
                    <div class="md-layout-item">
                        <div class="md-field">
                            <label for="movie">Assigned to Anyone</label>
                            <select name="movie" class="md-select" id="movie">
                                <option class="md-option" value="fight-club">Fight Club</option>
                                <option class="md-option" value="godfather">Godfather</option>
                            </select>
                        </div>
                    </div>
                </li>
            </ul>
        </div>
    </div>
`;

document.addEventListener('click', function(event) {
    if (event.target && event.target.classList.contains("pdf-canvas")) {
        console.log(event.target.parentNode);
        event.target.parentNode.appendChild(btn);
    }
});

The problem with above code is v-bind and v-on won't work with this approach.

Share Improve this question edited Dec 10, 2020 at 8:14 Dan Knights 8,3884 gold badges27 silver badges54 bronze badges asked Dec 10, 2020 at 5:29 AbhimanuSharmaAbhimanuSharma 4531 gold badge8 silver badges23 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

You can programmatically render the ponent.

First, move the element as a ponent, e.g:

Button.vue

<template>
    <div class="custom-signature-btn-row d-flex btn-grid d-flex mr-2">
        <span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
        <button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
            Signature
        </button>
        <div class="signature-dropdow" v-bind:class="{ active: isActive }">
            <ul>
                <li>
                    <div class="md-layout-item">
                        <div class="md-field">
                            <label for="movie">Assigned to Anyone</label>
                            <select name="movie" class="md-select" id="movie">
                                <option class="md-option" value="fight-club">Fight Club</option>
                                <option class="md-option" value="godfather">Godfather</option>
                            </select>
                        </div>
                    </div>
                </li>
            </ul>
        </div>
    </div>
</template>

<script>
export default {
  props: {
    active: false,
    toggleSignature: {
      type: Function,
      default: () => {}
    }
  }
}
</script>

On the canvas click handler, programmatically create a vue Button instance, render, and mount it into the target element.

import Vue from 'vue';
import Button from './Button.vue';

...

document.addEventListener('click', function(event) {
    if (event.target && event.target.classList.contains("pdf-canvas")) {
        const ButtonClass = Vue.extend(Button);
        const buttonInstance = new ButtonClass({
            propsData: { 
                isActive: this.isActive, // pass any data you need here
                toggleSignature: this.toggleSignature, // callback
            }
        });
        buttonInstance.$mount();
        event.target.parentNode.appendChild(buttonInstance.$el);
    }
});

You can refactor your code into an ordinary Vue ponent:

Vue.ponent('dynamic-btn', {
  data: () => ({
    isActive: true
  }),
  methods: {
    toggleSignature() {
      console.log('Clicked!')
    }
  },
  template: `
    <div class="custom-signature-btn-row d-flex">
    <div class="btn-grid d-flex mr-2">
        <span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
        <button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
            Signature
        </button>
        <div class="signature-dropdow" v-bind:class="{ active: isActive }">
            <ul>
                <li>
                    <div class="md-layout-item">
                        <div class="md-field">
                            <label for="movie">Assigned to Anyone</label>
                            <select name="movie" class="md-select" id="movie">
                                <option class="md-option" value="fight-club">Fight Club</option>
                                <option class="md-option" value="godfather">Godfather</option>
                            </select>
                        </div>
                    </div>
                </li>
            </ul>
        </div>
    </div>
    </div>
`
});

new Vue({
  el: '#app',
  data: () => ({
    btnCount: 1,
  }),
});

Vue.config.productionTip = false;
Vue.config.devtools = false;
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <button @click="btnCount++">Create Component</button>
  <dynamic-btn v-for="(btn, i) in btnCount" :key="i" />
</div>

This way v-bind and v-on will work as normal.


Using SFC syntax:

<template>
  <div class="custom-signature-btn-row d-flex">
    <div class="btn-grid d-flex mr-2">
      <span class="element-icon drag-icon md-icon sign-btn">drag_indicator</span>
      <button class="md-button md-primary md-theme-default button-custom-regular" v-on:click="toggleSignature()">
        Signature
      </button>
      <div class="signature-dropdow" v-bind:class="{ active: isActive }">
        <ul>
          <li>
            <div class="md-layout-item">
              <div class="md-field">
                <label for="movie">Assigned to Anyone</label>
                <select name="movie" class="md-select" id="movie">
                  <option class="md-option" value="fight-club">Fight Club</option>
                  <option class="md-option" value="godfather">Godfather</option>
                </select>
              </div>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'DynamicBtn',

  data: () => ({ isActive: true }),

  methods: {
    toggleSignature() {
      console.log('Clicked!')
    }
  }
}
</script>
<template>
  <div>
    <button @click="btnCount++">Create Component</button>
    <DynamicBtn v-for="(btn, i) in btnCount" :key="i" />
  </div>
</template>

<script>
import DynamicBtn from './DynamicBtn';

export default {
  name: 'Parent',

  ponents: { DynamicBtn },

  data: () => ({
    btnCount: 1,
  }),
}
</script>

Vue internally pre-piles all the templates into "render functions", so they can build the Virtual DOM tree. During this process all the "v-on" and "v-bind" magic happens - that's why it doesn't work when you simply add new node to the DOM directly

What you can do is write your own render function - Vue provides you with the "createElement" function(AKA "h" function, so-called by convention). There you can specify all the Children, Classes, select the HTML tag, handle events etc.

Or a bit simpler solution will be the Dynamic ponents (<ponent v-bind:is="myCompName">), probably in bination with "v-html" to handle the "dynamic" part

本文标签: javascriptCreate an element dynamically in VuejsStack Overflow