给动态生成的id标签添加html,vue动态渲染svg、添加点击事件

欢迎关注[前端小讴的github],阅读更多原创技术文章

业务需求(vue项目中)

1.页面展示svg内容

2.监听svg内部的点击事件

3.动态改变svg内部元素的属性和值

html标签

经多次实验,用embed、img等标签改变src属性的方式,均无法实现上述全部功能(尤其是svg内部点击事件),最终采用Vue.extend()方法完整实现,代码也较为简洁,html结构如下:

直接将svg文件的内容复制粘贴到.vue文件里,是可以在标签内直接添加@click事件完成需求的,方式简单但会造成文件过长,本文不多陈述

实现思路

1.创建xhr对象

const xhr = new XMLHttpRequest();

this.svgUrl = ...; // svg的绝对地址,在浏览器中打开能看到的那个

xhr.open("GET", this.svgUrl, true);

xhr.send();

2.监听xhr对象(获取svg的dom -> 添加事件 -> 修改dom -> 转成虚拟dom并挂载)

xhr.addEventListener("load", () => {

// ① 获取svg的dom

const resXML = xhr.responseXML;

this.svgDom = resXML.documentElement.cloneNode(true); // console.log(this.svgDom);

// ② 添加click事件

let btn = this.svgDom.getElementById("...");

btn.setAttribute("v-on:click", "this.handleClick()");

// ↑↑↑ 此处注意:原生事件handleClick此时在window层,解决办法见后文

// ③ 修改 dom

this.svgDom.getElementById("...").childNodes[0].nodeValue = ...

this.svgDom.getElementById("...").setAttribute("style",

`....; fill:${this.photoResult.resultColor}; ...`);

// ↑↑↑ 用js操作dom的语法,动态设置svg部件的属性和值

// ④ 将svgDom对象转换成vue的虚拟dom,创建实例并挂载到元素上

var oSerializer = new XMLSerializer();

var sXML = oSerializer.serializeToString(this.svgDom);

var Profile = Vue.extend({

template: "

" + sXML + "
"

});

new Profile().$mount("#svgTemplate");

});

3.将methods里要执行的事件绑定到window下面,供外部(刚添加的 handleClick 事件)调用

async mounted() {

window["handleClick"] = () => {

this.takePhoto();

};

},

methods:{

takePhoto(){ ... }

}

到这里就基本完成需求:动态渲染了svg、用js操作dom的语法修改svg部件的属性和值、给svg部件动态添加了事件 handleClick,最后将 takePhoto() 事件绑定给了 window 对象的 handleClick,可以放心大胆的在 takePhoto() 里写你要执行的内容了!

特殊注意

给svg的dom部件添加事件时:

1.经多次尝试,只有 setAttribute + v-on:click 写法有效

2.setAttribute 不支持 @click(非原生事件),会报语法错误

3.addEventListener 和 onclick 均会被 vue 拦截

将svgDom对象转换成vue的虚拟dom时:

1.如果报错如下

5e68b2ab85759.png

则将 import Vue from "vue" 改为 import Vue from "vue/dist/vue.esm.js"

其原因及其他解决办法本文不做探讨可自行百度。

2.vue.extend() 方法是 vue 的一个构造器,用来动态创建 vue 实例,template 组件模板只能有一个根元素

3.$mount 手动挂载到 id 为 svgTemplate的 元素上,挂载后将替换原本的dom(替换原本的

var Profile = Vue.extend({

template: "

" + sXML + "
"

// ↑↑↑ 最外层的 id 不能省略,否则首次渲染后找不到 #svgTemplate

});

new Profile().$mount("#svgTemplate");

// ↑↑↑ 原本的 #svgTemplate 将被替换成 Profile 的 template

完整代码

import Vue from "vue/dist/vue.esm.js";

// window.handleClick = () => {

// 原本的 handleClick 事件是 window 的

// };

export default {

name: "svg-drawing",

data() {

return {

/* 全局 */

svgUrl: "", // svg的url

svgDom: null, // 获取到的svg元素

/* svg的变量 */

photoResult: {

resultVal: 0, // 测试结果 - 值

resultMsg: "未检测", // 测试结果 - 字段

resultColor: "#dcdee2" // 测试结果 - 字段背景色

}

};

},

async mounted() {

// 将takePhoto方法绑定到window下面,提供给外部调用

window["handleClick"] = () => {

this.takePhoto();

};

},

created() {

this.getSvg();

},

methods: {

// 初始化svg

getSvg() {

/* 创建xhr对象 */

const xhr = new XMLHttpRequest();

this.svgUrl = this.baseUrl + "/svgs/" + "test.svg";

xhr.open("GET", this.svgUrl, true);

xhr.send();

/* 监听xhr对象 */

xhr.addEventListener("load", () => {

/* 1. 获取 dom */

const resXML = xhr.responseXML;

this.svgDom = resXML.documentElement.cloneNode(true);

/* 2.SVG对象添加click事件 */

let btnTakePhotoDom = this.svgDom.getElementById("...");

btnTakePhotoDom.setAttribute("v-on:click", "this.handleClick()");

/* 3. 修改 dom */

this.svgDom.getElementById("...").childNodes[0].nodeValue = ...;

this.svgDom.getElementById("...").setAttribute("style",

`....; fill:${this.photoResult.resultColor}; ...`);

/* 4.将svgDom对象转换成vue的虚拟dom */

var oSerializer = new XMLSerializer();

var sXML = oSerializer.serializeToString(this.svgDom);

var Profile = Vue.extend({

template: "

" + sXML + "
"

});

// 创建实例,并挂载到元素上

new Profile().$mount("#svgTemplate");

});

},

// 事件

takePhoto() { ... },

},

beforeDestroy() {

this.svgDom = null;

},

watch: {

photoResult: {

handler(newVal, oldVal) {

this.getSvg();

},

deep: true

}

}

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值