vue中怎么动态创建节点并绑定事件

  • A+
所属分类:vue

一.前言

有原生js学习基础的都知道原生js是怎么动态创建节点并且绑定事件的,于是我在vue项目中使用原生js的形式动态创建了一个节点并且绑定了一个点击事件,但是,在浏览器中却发现这个点击事件失效了,下面就来探究一下该怎么样实现这个需求

二.原生js动态创建节点并绑定事件失效

<template>     <div>         <div id="app2">             <div >                 <div @click="fn" >点击我查看事件</div>             </div>         </div>         <button @click="createNode">创建新的节点</button>     </div> </template> <script> export default {     data() {         return {             msg:'hello world'         }     },     methods: {         fn(){             console.log(this.msg);         },         createNode(){             var oDiv=document.createElement('div');             oDiv.setAttribute('class','wrapper')             oDiv.innerHTML='<div @click="fn">点击我查看事件</div>';             var app2=document.querySelector('#app2');             app2.appendChild(oDiv);         }     },       } </script> <style lang="">      </style> 复制代码

上面的代码中我给按钮绑定了一个事件:每当点击这个按钮,就使用原生的js动态创建一个类名为wrapper的节点,并且对它的子节点绑定了一个点击事件fn,然后插入到idapp2的节点下

但是在浏览器中发现,动态创建的节点的点击事件是失效的,而一开始就绑定了同样事件的节点的事件却又是有效的

这是什么原因呢?

具体的原因我还不是特别的清楚,但是这可能是与vue周期与编译有关,其实在使用vue就是为了减少对DOM的操作,以数据来驱动试图,为什么要在vue中折腾DOM呢?

那么vue中怎么实现类似动态插入一个节点并且绑定事件的需求呢?

三.利用组件构造器实现动态添加一个节点并绑定事件

vue.extend()是一个构造器,用来创建一个子类,参数是一个包含组件选项的对象

下面介绍vue.extend()的使用方式

第一种:从外部引入节点组件

1.将需要动态添加的节点以及它所绑定的事件以组件的形式,先事先写好

新建一个组件文件NewComponent.vue

<template>     <div>    <!-- 将要动态添加的节点 -->         <button @click="fn">点我显示信息</button>     </div> </template> <script> export default {     data() {         return {             msg:'hello world'         }     },     methods: {         fn:function(){             console.log(this.msg);         }     }, } </script> <style lang="">      </style> 复制代码

2.在你想要动态添加一个节点的那个组件中引入NewComponent.vue

<template>     <div id='app2'>       <div id="mount-point"><h1>从外部引入新的节点</h1></div>       <button @click = "addNode">添加节点</button>     </div> </template> <script> //引入将要动态添加节点所在的组件 import Append from './NewComponent.vue'; import Vue from 'vue'; export default {     data(){         return{                      }     },     methods:{         addNode(){          //创建一个构造器           var Profile = Vue.extend(Append);             // 创建 Profile 实例,并挂载到一个元素上。            new Profile().$mount('#mount-point');         }     },      } </script> <style lang=""> </style> 复制代码

第二种:在内部创建节点组件

<template>     <div id='app2'>       <div id="mount-point"><h1>在本组件中动态创建新的节点</h1></div>       <button @click = "addNode">添加节点</button>     </div> </template> <script> import Vue from 'vue'; export default {     data(){         return{                      }     },     methods:{         addNode(){             // 创建构造器             var Profile = Vue.extend({                 template: '<button @click="fn">内部创建的节点组件</button>',                 data: function () {                     return {                         msg:'hello world'                     }                 },                 methods:{                     fn(){                         console.log(this.msg)                     }                 }             })             // 创建 Profile 实例,并挂载到一个元素上。             new Profile().$mount('#mount-point')         }     },      } </script> <style lang=""> </style> 复制代码

上面两种方式动态创建已经绑定事件的节点组件,在浏览器中运行,当点击"添加节点"按钮,便将节点所在的组件,挂载到id="mount-point"div中,注意这里的挂载是会把id="mount-point"div给完全的替换掉,然后你再点击新创建的节点上绑定的事件,是可以触发的

新动态添加的节点会覆盖被挂载的节点,那么如果我想要保留被挂载的节点怎么办呢?

我们可以采用不直接挂载,采用间接挂载的方式,在原先想要挂载的那个挂载点的内部,使用原生js动态创建一个新节点,将节点组件挂载到这个节点上

<template>     <div id='app2'>       <div id="mount-point"><h1>在本组件中动态创建新的节点</h1></div>       <button @click = "addNode">添加节点</button>     </div> </template> <script> import Vue from 'vue'; export default {     data(){         return{                      }     },     methods:{         addNode(){             //手动创建节点,实现间接挂载                 var mpNode=document.createElement('div');                 mpNode.setAttribute('id','mp');                 var pnode=document.querySelector("#mount-point");                 pnode.appendChild(mpNode);                              // 创建构造器             var Profile = Vue.extend({                 template: '<button @click="fn">内部创建的节点组件</button>',                 data: function () {                     return {                         msg:'hello world'                     }                 },                 methods:{                     fn(){                         console.log(this.msg)                     }                                      }             })             // 创建 Profile 实例,并挂载到一个元素上。             new Profile().$mount('#mp')         }             },      } </script> <style lang=""> </style> 复制代码

四.组件构造器创建的组件与外部组件之间怎么传值

使用组件构造器创建组件是解决了原生js创建DOM节点,绑定的事件失效的问题,那现在问题又来了,新创建的组件和原来组件之间怎么传值呢?

外部组件传到新创建组件

  • 新创建的组件用props将外部传进来的参数存起来

  • 新创建的组件在挂载时以对象的方式传入参数,传入的值存在propsData

<template>     <div id='app2'>       <div id="mount-point"><h1>在本组件中动态创建新的节点</h1></div>       <button @click = "addNode">添加节点</button>     </div> </template> <script> import Vue from 'vue'; export default {     data(){         return{                      }     },     methods:{         addNode(){             //手动创建节点,实现间接挂载                 var mpNode=document.createElement('div');                 mpNode.setAttribute('id','mp');                 var pnode=document.querySelector("#mount-point");                 pnode.appendChild(mpNode);                              // 创建构造器             var Profile = Vue.extend({                 template: '<button @click="fn">内部创建的节点组件</button>',                 data: function () {                     return {                         msg:'hello world'                     }                 },                 props:['str'],                 methods:{                     fn(){                         console.log(this.str)                     }                 }             })             // 创建 Profile 实例,并挂载到一个元素上。             new Profile({              propsData:{str:'你好呀'}             }).$mount('#mp')         }             },      } </script> <style lang=""> </style> 复制代码

新创建组件传到外部组件

对于新创建组件传到外部组件,目前我尚未查到有效的方法,希望有知道的小伙伴可以留言告诉我

写到这里,不知大家有没有觉得这样创建一个新的节点很麻烦,至少在数据处理上不是很方便

下面我提供另一种思路来实现动态创建节点

五.利用数组以及v-for实现动态创建节点

<template>     <div id='app2'>       <div v-for="(v,i) in data" v-bind:key="i">           <input type="text" placeholder="type it" v-model="data[i]"><button @click="del()">取消</button>       </div>       <button @click = "createNode">创建表单input节点</button>       <button @click = "getdata">获取所有input值</button>     </div> </template> <script> import Vue from 'vue'; export default {     data(){         return{             data:[],             index:0                     }     },     methods:{         getdata(){ console.log(this.data);         },         del(){            event.currentTarget.parentNode.style.display='none';         },         createNode(){             this.index++;             console.log(this.index);             var str="请输入选项"             var ele=str+this.index;             this.data.push(ele);         }             },      } </script> <style lang=""> </style> 复制代码

效果:

上面的代码,一开始定义一个空的数组,每点击一次添加按钮,就往数组中添加一个数,数组中有多少个数,就遍历该数组,显示相应数量的节点,然后对应双向数据绑定数组中的值,如果要获取所有表单里面的值,直接获取this.data即可

六.总结

我们实现一个需求时,我们可以尝试多种方法,但是,有时你会发现,你尝试的方法只解决了其中的某个点,这是就不得不去尝试新的方法,直到实现我们的需求

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: