Fork me on GitHub

关于 ref 与 $refs 对 dom 元素的操作

# 如何获取 v-for 渲染的多个不同 ref 的 dom

🍍 在编写 vue 项目过程中,遇到了获取不到正确的 dom 元素节点的问题。

功能界面如图所示:

界面

需要实现的是,点击每个播放器的右上角的关闭按钮,则关闭播放器。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<template>
<div class="device-status">
<!-- 树组件 -->
<Tree></Tree>
<!-- 业务内容 -->
<div class="content-container" ref="container">
<h1>{{ msg }} (这是待删除文字) 具体看 status.vue 看代码写法</h1>
<div>{{buildings}}</div>
<div class="video-box" ref="video1">
<Player
src="https://gss3.baidu.com/6LZ0ej3k1Qd3ote6lo7D0j9wehsv/tieba-smallvideo/12846619_51a524dffce6834f1d221be2a1037834.mp4"
poster="https://puui.qpic.cn/fans_admin/0/3_118841988_1557667793407/0"
></Player>
<i class="el-icon-circle-close" @click="close('video1')"></i>
</div>
</div>
</div>
</template>

<script>
import Tree from "component/checkbox-tree/tree";
import Player from "component/player/player";

export default {
data() {
return {
msg: "我是设备状态及控制组件",
videoList: [
{
id: 1,
index: 0,
src: "https://gss3.baidu.com/6LZ0ej3k1Qd3ote6lo7D0j9wehsv/tieba-smallvideo/12846619_51a524dffce6834f1d221be2a1037834.mp4",
poster: "https://puui.qpic.cn/fans_admin/0/3_118841988_1557667793407/0"
},
{
id: 2,
index: 1,
src: "https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm",
poster: "https://ww1.sinaimg.cn/large/007i4MEmgy1g29h63wl0yj30et08c0tc.jpg"
}
]
};
},
computed: {
buildings(){
return this.$store.state.buildingTree
}
},
components: {
Tree,
Player
},
watch: {
buildings(newVal, oldVal){
console.log("监听到树选中值变化",JSON.stringify(newVal))
//判断树数组的值,空则不作操作,否则带上树id
//进行 http 请求获取数据

}
},
methods: {
//关闭播放器
close(videoStr){
//应该用数据驱动dom,这里直接操作了dom,不符合vue的理念,暂时
let video = this.$refs[videoStr]
video.parentNode.removeChild(video)
}
}
};
</script>

<style scoped lang="stylus">
.device-status{
.el-tree{
width: 200px;
height: calc(100vh - 70px);
background-color: #f8f8f8;
float: left;
padding: 10px 20px;
}

.content-container{
padding: 20px;
margin-left: 200px;

.video-box{
position: relative;
width: 640px;
display: inline-block;
border: 5px solid #6699FF;
margin: 40px 40px 10px 10px;

.el-icon-circle-close{
position: absolute;
top: -40px;
right: -40px;
color: #4A6495;
font-size: 40px;
opacity: 0.8;
cursor: pointer;
}
}
}
}
</style>

至此代码功能正常,点击右上角关闭按钮,则移除播放器元素。

然后问题在于,播放器可能有多个存在,这时候,如何实现点击每个关闭按钮,关闭对应的播放器呢?

尝试修改代码如下:

1
2
3
4
5
6
7
8
9
10
<template v-for="video in videoList">
<!-- 播放器组件,带关闭按钮 -->
<div class="video-box" :ref="`video${video.id}`" >
<Player
:src="video.src"
:poster="video.poster"
></Player>
<i class="el-icon-circle-close" @click="close(`video${video.id}`)"></i>
</div>
</template>

1
2
3
4
5
6
7
8
methods: {
//关闭播放器
close(videoStr){
//应该用数据驱动dom,这里直接操作了dom,不符合vue的理念,暂时
let video = this.$refs[videoStr]
video.parentNode.removeChild(video)
}
}

然后点击关闭按钮时,可以看到控制台报错:

报错

提示不能获取未定义的属性,则表明该 dom 元素节点获取不对。

# 发现问题所在

后面经过一番折腾,发现以上代码 this.$refs[videoStr] 获取的是一个 ref 等于 videoStr (此处为变量)的 dom 节点数组,不是单个 dom 节点元素!

数组节点

至此,踩坑这个之后,就明白了为什么获取不到对应的 dom 元素了。

代码修改如下即可:

1
2
3
4
5
6
7
8
methods: {
//关闭播放器
close(videoStr){
//应该用数据驱动dom,这里直接操作了dom,不符合vue的理念,暂时
let video = this.$refs[videoStr][0]
video.parentNode.removeChild(video)
}
}

功能实现后,再来拓展其他方法。

比如当 ref 的值一样,都为 videoBox 时:

1
2
3
4
5
6
7
8
9
<template v-for="video in videoList">
<div class="video-box" ref="videoBox" >
<Player
:src="video.src"
:poster="video.poster"
></Player>
<i class="el-icon-circle-close" @click="close(video.index)"></i>
</div>
</template>

1
2
3
4
5
6
7
8
methods: {
//关闭播放器
close(index){
// 利用数组下标操作
let video = this.$refs.videoBox[index]
video.parentNode.removeChild(video)
}
}

又或者不使用 ref 属性,改而为每个播放器元素赋值 id,也可:

1
2
3
4
5
6
7
8
9
<template v-for="video in videoList">
<div class="video-box" :id="`video${video.id}`">
<Player
:src="video.src"
:poster="video.poster"
></Player>
<i class="el-icon-circle-close" @click="close(`video${video.id}`)"></i>
</div>
</template>

1
2
3
4
5
6
7
8
methods: {
//关闭播放器
close(videoId){
// 利用数组下标操作
let video = document.querySelector(`#${videoId}`)
video.parentNode.removeChild(video)
}
}

官网概念:

官网概念

总结:

ref 相当于给元素或组件赋予一个 ID 引用,用来注册引用信息的,方便获取 dom 元素或获取组件实例。

使用场景:

1
2
3
1. ref 加在普通元素上,this.$refs.name 获取的是 dom 元素
2. ref 加在子组件上,this.$refs.name 获取到的是组件实例,方便父组件使用子组件的所有方法
3. 当 v-for 用于元素或组件,ref 获取的将是一组数组或 dom 节点

文章所说遇到的问题即是上述第三种情况。

0%
            [00:06.59]呼んでいる 胸のどこか奥で
            [00:12.06]いつも心踊る 夢を見たい
            [00:18.09]かなしみは 数えきれないけれど
            [00:23.87]その向こうできっと あなたに会える
            [00:29.40]
            [00:31.81]繰り返すあやまちの そのたび ひとは
            [00:38.20]ただ青い空の 青さを知る
            [00:43.33]果てしなく 道は続いて見えるけれど
            [00:50.92]この両手は 光を抱ける
            [00:54.89]
            [00:57.26]さよならのときの 静かな胸
            [01:02.80]ゼロになるからだが 耳をすませる
            [01:08.60]生きている不思議 死んでいく不思議
            [01:14.53]花も風も街も みんなおなじ
            [01:20.50]
            [01:22.40]nananan lalala lululu
            [01:47.63]呼んでいる 胸のどこか奥で
            [01:53.56]いつも何度でも 夢を描こう
            [01:59.14]かなしみの数を 言い尽くすより
            [02:05.16]同じくちびるで そっとうたおう
            [02:11.48]
            [02:12.96]閉じていく思い出の そのなかにいつも
            [02:19.31]忘れたくない ささやきを聞く
            [02:24.47]こなごなに砕かれた 鏡の上にも
            [02:31.12]新しい景色が 映される
            [02:35.89]
            [02:38.27]はじまりの朝の静かな窓
            [02:43.72]ゼロになるからだ 充たされてゆけ
            [02:49.70]海の彼方には もう探さない
            [02:55.66]輝くものは いつもここに
            [03:01.66]わたしのなかに 見つけられたから
            [03:08.20]
            [03:14.13]nananan lalala lululu
        
            [by:嘶哑音符]
            [00:00.12]二人の間 通り過ぎた風は
            [00:07.15]どこから寂しさを運んできたの
            [00:13.66]泣いたりしたそのあとの空は
            [00:19.82]やけに透き通っていたりしたんだ
            [00:26.02]music
            [00:37.03]いつもは尖ってた父の言葉が
            [00:42.82]今日は暖かく感じました
            [00:48.47]優しさも笑顔も夢の語り方も
            [00:54.07]知らなくて全部 君を真似たよ
            [00:59.48]もう少しだけでいい あと少しだけでいい
            [01:05.39]もう少しだけでいいから
            [01:11.20]もう少しだけでいい あと少しだけでいい
            [01:16.97]もう少しだけ くっついていようか
            [01:21.11]music
            [01:25.69]僕らタイムフライヤー 時を駆け上がるクライマー
            [01:30.74]時のかくれんぼ はぐれっこはもういやなんだ
            [01:37.51]嬉しくて泣くのは 悲しくて笑うのは
            [01:41.74]君の心が 君を追い越したんだよ
            [01:46.78]music
            [02:08.22]星にまで願って 手にいれたオモチャも
            [02:14.10]部屋の隅っこに今 転がってる
            [02:19.83]叶えたい夢も 今日で100個できたよ
            [02:25.53]たった一つといつか 交換こしよう
            [02:31.26]music
            [02:37.14]いつもは喋らないあの子に今日は
            [02:42.95]放課後「また明日」と声をかけた
            [02:48.50]慣れないこともたまにならいいね
            [02:54.25]特にあなたが 隣にいたら
            [02:59.69]もう少しだけでいい あと少しだけでいい
            [03:05.39]もう少しだけでいいから
            [03:11.09]もう少しだけでいい あと少しだけでいい
            [03:16.69]もう少しだけくっついていようよ
            [03:21.02]music
            [03:25.75]僕らタイムフライヤー 君を知っていたんだ
            [03:30.42]僕が 僕の名前を 覚えるよりずっと前に
            [03:43.62]君のいない 世界にも 何かの意味はきっとあって
            [03:49.00]でも君のいない 世界など 夏休みのない 八月のよう
            [03:55.15]君のいない 世界など 笑うことない サンタのよう
            [04:00.60]君のいない 世界など
            [04:08.70]music
            [04:34.00]僕らタイムフライヤー 時を駆け上がるクライマー
            [04:39.32]時のかくれんぼ はぐれっこはもういやなんだ
            [04:46.03]なんでもないや やっぱりなんでもないや
            [04:50.46]今から行くよ
            [04:55.00]僕らタイムフライヤー 時を駆け上がるクライマー
            [04:59.98]時のかくれんぼ はぐれっこ はもういいよ
            [05:06.45]君は派手なクライヤー その涙 止めてみたいな
            [05:11.34]だけど 君は拒んだ 零れるままの涙を見てわかった
            [05:18.08]嬉しくて泣くのは 悲しくて 笑うのは
            [05:22.51]僕の心が 僕を追い越したんだよ
            [05:27.55]end
        
            [ti:Down by the salley gardens]
            [ar:藤田惠美]
            [al:挪威甘菊]
            [by:]
            [00:12.00]Down by the salley gardens my love and I did meet
            [00:25.00]She passed the salley gardens with little snow-white feet
            [00:38.00]She did me take love easy, as the leaves grow on the tree
            [00:50.00]But I, being young and foolish, with her would not agree
            [01:04.00]
            [01:15.00]In a filed by the river my love and I did stand
            [01:28.00]And on my leaning shoulder she laid her snows-white hand
            [01:40.00]She bid me take life easy, as the grass grows on the weirs
            [01:52.00]But I was young and foolish, and now I am full of tears
            [02:06.00]
            [02:54.00]Down by the salley gardens my love and I did meet
            [03:05.00]She passed the salley gardens with little snow-white feet
            [03:18.00]She did me take love easy, as the leaves grow on the tree
            [03:30.00]But I, being young and foolish, with her would not agree
        
            [00:11.860]夜明けまであと一時間 もうそろそろ行こう
            [00:23.120]聞こえるのは眠る君のかすかな寝息だけ
            [00:34.240]
            [00:37.710]目を閉じた君の横顔 とてもきれいだよ
            [00:49.440]さよなら 君の耳元にそっと囁いた
            [01:00.790]
            [01:01.480]ああ、僕は君を置いて 今ここを出て行く
            [01:12.970]外は雨 音もなく 僕の頬を濡らす
            [01:24.330]
            [01:36.410]君と出会ったのはたった半年前のこと
            [01:48.080]もうずいぶん前のことのような気がする
            [01:59.460]
            [01:59.900]今思えば僕らろくに話もしなかった
            [02:11.220]时间はいつも余るほどあったはずなのに
            [02:22.640]
            [02:23.090]ああ、僕は君を置いて 今ここを出て行く
            [02:34.740]外の雨は僕の涙、静かに降り続く
            [02:46.870]外の雨は僕の涙、静かに降り続く
            [02:59.480]
        
            [00:07.694]灯りを消したまま話を続けたら
            [00:19.901]ガラスの向こう側で星がひとつ消えた
            [00:26.559]からまわりしながら通りを駆け抜けて
            [00:37.816]砕けるその時は君の名前だけ呼ぶよ
            [00:45.374]広すぎる霊園のそばの このアパートは薄ぐもり
            [00:56.155]暖かい幻を見てた
            [01:04.441]猫になりたい 君の腕の中
            [01:13.254]寂しい夜が終わるまでここにいたいよ
            [01:22.621]猫になりたい 言葉ははかない
            [01:31.369]消えないようにキズつけてあげるよ
            [01:47.999]目を閉じて浮かべた密やかな逃げ場所は
            [01:57.880]シチリアの浜辺の絵ハガキとよく似てた
            [02:05.646]砂ぼこりにまみれて歩く 街は季節を嫌ってる
            [02:15.629]つくられた安らぎを捨てて
            [02:23.888]猫になりたい 君の腕の中
            [02:32.579]寂しい夜が終わるまでここにいたいよ
            [02:41.912]猫になりたい 言葉ははかない
            [02:50.362]消えないようにキズつけてあげるよ
        
            [by:最短距離]
            [00:08.40]落し物をしたのね そんな曇りの日に
            [00:15.78]プラネタリュウムに行って 重いドアを閉じましょうよ
            [00:23.28]いつのまに落としたの それさえわからなくて
            [00:30.55]涙をもう落とせない なら星見上げましょうよ
            [00:38.12]何がほんと作り事 答えは欲しくないけど
            [00:46.23]あなたが言う言葉は本当に したいよ
            [00:57.77]落し物をしたのね そんな曇りの日に
            [01:05.20]プラネタリュウムに行って 重いドアを閉じましょうよ
            [01:12.71]魔法信じれないけど 魔法みたいな力を
            [01:19.74]信じたくってこうして こんな星を見上げるんだ
            [01:26.89]差しわされた あなたの手を
            [01:30.27]私は今 ほら 握れなくて
            [01:34.12]通り過ぎてく 遠い答えを
            [01:37.72]言いたくても ほら言い出せない
            [01:57.90]
            [01:57.96]落し物をしたのね あなたを信じたことを
            [02:05.25]プラネタリュウム出るから 私のドア開けましょうよ
            [02:11.69]私のドア開けましょうよ
            [02:15.29]私のドア開けましょうよ
            [02:19.60]
            [02:19.67]はい!
            [02:21.60]はい!
            [02:23.47]はい!
            [02:24.89]はい!
            [02:26.91]はい!
            [02:28.74]はいはい!
            [02:30.97]はい!
            [02:32.40]はい!