vue中怎么动态创建节点并绑定事件
一.前言
有原生js学习基础的都知道原生js是怎么动态创建节点并且绑定事件的,于是我在vue项目中使用原生js的形式动态创建了一个节点并且绑定了一个点击事件,但是,在浏览器中却发现这个点击事件失效了,下面就来探究一下该怎么样实现这个需求
二.原生js动态创建节点并绑定事件失效
<template>
<div>
<div id="app2">
<div class="wrapper" >
<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
,然后插入到id
为app2
的节点下
但是在浏览器中发现,动态创建的节点的点击事件是失效的,而一开始就绑定了同样事件的节点的事件却又是有效的
这是什么原因呢?
具体的原因我还不是特别的清楚,但是这可能是与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>