vue动态渲染svg、添加点击事件的实现
业务需求(vue项目中)
1.页面展示svg内容
2.监听svg内部的点击事件
3.动态改变svg内部元素的属性和值
html标签
经多次实验,用embed、img等标签改变src属性的方式,均无法实现上述全部功能(尤其是svg内部点击事件),最终采用Vue.extend()方法完整实现,代码也较为简洁,html结构如下:
直接将svg文件的内容复制粘贴到.vue文件里,是可以在标签内直接添加@click事件完成需求的,方式简单但会造成文件过长,本文不多陈述
实现思路
1.创建xhr对象
constxhr=newXMLHttpRequest();
this.svgUrl=...;//svg的绝对地址,在浏览器中打开能看到的那个
xhr.open("GET",this.svgUrl,true);
xhr.send();
2.监听xhr对象(获取svg的dom->添加事件->修改dom->转成虚拟dom并挂载)
xhr.addEventListener("load",()=>{
//①获取svg的dom
constresXML=xhr.responseXML;
this.svgDom=resXML.documentElement.cloneNode(true);//console.log(this.svgDom);
//②添加click事件
letbtn=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,创建实例并挂载到元素上
varoSerializer=newXMLSerializer();
varsXML=oSerializer.serializeToString(this.svgDom);
varProfile=Vue.extend({
template:""+sXML+" 3.将methods里要执行的事件绑定到window下面,供外部(刚添加的handleClick事件)调用
asyncmounted(){
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.如果报错如下
则将importVuefrom"vue"改为importVuefrom"vue/dist/vue.esm.js"
其原因及其他解决办法本文不做探讨可自行百度。
2.vue.extend()方法是vue的一个构造器,用来动态创建vue实例,template组件模板只能有一个根元素
3.$mount手动挂载到id为svgTemplate的元素上,挂载后将替换原本的dom(替换原本的