Welcome to attention[Front-end Acura github]Read more original technical articles

Business requirements (in the VUE project)

  • The page displays SVG content
  • Listen for click events inside SVG
  • Dynamically change the attributes and values of elements inside SVG

HTML tags

After many experiments, all the above functions (especially the internal click events in SVG) could not be realized by using tags like EMBED and img to change SRC attributes. Finally, ** vue.extend ()** method was adopted to complete the implementation, and the code was relatively simple. The HTML structure was as follows:

<template>
  <div>
    <div id="svgTemplate"></div>
  </div>
</template>
Copy the code

It is possible to copy and paste the contents of an SVG file directly into a. Vue file by adding the @click event to the tag. This is an easy way to do this, but it will make the file too long

Implementation approach

1. Create an XHR object

const xhr = new XMLHttpRequest();
this.svgUrl = ... ;// The absolute address of the SVG, which you can see when you open it in a browser
xhr.open("GET".this.svgUrl, true);
xhr.send();
Copy the code

2. Listen to XHR object (get SVG DOM -> add event -> modify DOM -> convert virtual DOM and mount)

xhr.addEventListener("load", () = > {// ① Obtain the DOM of SVG
    const resXML = xhr.responseXML;
    this.svgDom = resXML.documentElement.cloneNode(true);     // console.log(this.svgDom);
    
    // add the click event
    let btn = this.svgDom.getElementById("...");
    btn.setAttribute("v-on:click"."this.handleClick()");
    Note: the native event handleClick is now in the window layer

    // change the dom
    this.svgDom.getElementById("...").childNodes[0].nodeValue = ...
	this.svgDom.getElementById("...").setAttribute("style".`....; fill:The ${this.photoResult.resultColor}; . `);
    // use js to manipulate THE DOM syntax and dynamically set the properties and values of SVG widgets
   
    // (4) Convert the svgDom object into a vue virtual DOM, create an instance and mount it to the element
    var oSerializer = new XMLSerializer();
    var sXML = oSerializer.serializeToString(this.svgDom);
    var Profile = Vue.extend({
        template: "<div id='svgTemplate'>" + sXML + "</div>"
    });
    new Profile().$mount("#svgTemplate");
});
Copy the code

3. Bind the methods event to the window for external (newly added handleClick event) calls

async mounted() {
    window["handleClick"] = (a)= > {
       this.takePhoto();
    };
},
methods:{ takePhoto(){ ... }}Copy the code

Here are the basic requirements: Render SVG dynamically, modify attributes and values of SVG widgets using JAVASCRIPT syntax for DOM manipulation, add the event handleClick to SVG widgets dynamically, and bind the takePhoto() event to the Window object handleClick. Feel free to write what you want to execute in takePhoto()!

Special attention to

  • When adding an event to an SVG DOM widget: 2. SetAttribute does not support @click (non-native event). 3. AddEventListener and onclick are both intercepted by vUE

  • When converting svgDom objects into vue virtual DOM: 1

import Vue from "vue"
import Vue from "vue/dist/vue.esm.js"
The Template component template can have only one root element
The original DOM will be replaced when mounted
<div id="svgTemplate"></div>
The outermost div inside the template also has an id attribute:

var Profile = Vue.extend({
     template: "<div id='svgTemplate'>" + sXML + "</div>" 
     // The outermost id of ↑ cannot be omitted, otherwise the #svgTemplate will not be found after the first rendering
});
new Profile().$mount("#svgTemplate"); 
// The original #svgTemplate will be replaced by the template of the Profile
Copy the code

The complete code

<template>
  <div>
    <div id="svgTemplate"></div>
  </div>
</template>
Copy the code
<script>
import Vue from "vue/dist/vue.esm.js";

// window.handleClick = () => {
   // The original handleClick event is window
// };

export default {
  name: "svg-drawing",
  data() {
    return {
	  / * * / global
      svgUrl: ""./ / SVG's url
      svgDom: null.// Get the SVG element
      /* SVG variable */
      photoResult: {
        resultVal: 0.// Test result - value
        resultMsg: "Not detected".// Test result - field
        resultColor: "#dcdee2" // Test results-field background color}}; },async mounted() {
    // Bind the takePhoto method under the window for external calls
    window["handleClick"] = (a)= > {
      this.takePhoto();
    };
  },
  created() {
    this.getSvg();
  },
  methods: {
    // Initialize SVG
    getSvg() {
	  /* Create an XHR object */
      const xhr = new XMLHttpRequest();
      this.svgUrl = this.baseUrl + "/svgs/" + "test.svg";
      xhr.open("GET".this.svgUrl, true);
      xhr.send();

	  /* Listen on the XHR object */
      xhr.addEventListener("load", () = > {/* 1. Obtain dom */
        const resXML = xhr.responseXML;
        this.svgDom = resXML.documentElement.cloneNode(true);

        /* 2.SVG objects add the click event */
        let btnTakePhotoDom = this.svgDom.getElementById("...");
        btnTakePhotoDom.setAttribute("v-on:click"."this.handleClick()");

        /* 3. Modify dom */
		this.svgDom.getElementById("...").childNodes[0].nodeValue = ... ;this.svgDom.getElementById("...").setAttribute("style".`....; fill:The ${this.photoResult.resultColor}; . `);

        /* 4. Convert svgDom objects into vue virtual DOM */
        var oSerializer = new XMLSerializer();
        var sXML = oSerializer.serializeToString(this.svgDom);
        var Profile = Vue.extend({
          template: "<div id='svgTemplate'>" + sXML + "</div>"
        });
        // Create an instance and mount it to the element
        new Profile().$mount("#svgTemplate");
      });
    },
    / / event
    takePhoto() { ... },
  },
  beforeDestroy() {
    this.svgDom = null;
  },
  watch: {
    photoResult: {
      handler(newVal, oldVal) {
        this.getSvg();
      },
      deep: true}}};</script>
Copy the code