第八讲:联动控制函数如何管理众多子元素的动态变化
第七讲我们在JS代码中创建了一个联动控制函数 mState(),它能有效管理拥有id标识的子元素,使这些子元素的变化能与音频的播放&暂停同步。我们知道,同一个页面元素的id好比人的身份证,是唯一的,当需要控制的子元素非常多,我们一一给它们标注id然后去控制它们会很繁琐、代码也会很冗长。为此,我们需要更好的途径来处理这个问题,这里假设我们的帖子用到两个视频、两张图片做播放器、三张图片做飞鹰,HTML代码如下:
<!-- html 代码 -->
<div id="mydiv">
<video class="vid" src="视频地址1" autoplay loop muted></vid>
<video class="vid" src="视频地址1" autoplay loop muted></vid>
<img class="player" src="图片地址1" alt="" />
<img class="player" src="图片地址2" alt="" />
<img class="bird" src="图片地址3" alt="" />
<img class="bird" src="图片地址5" alt="" />
<img class="bird" src="图片地址4" alt="" />
</div>
视频与图片标签都是用了class替代了id,共有三类,vid、player、bird,它们对标的CSS选择器大概如下:
/* CSS类选择器 */
.vid {
/* 视频类选择器代码略 */
}
.player {
/* 播放器类选择器代码略 */
}
.bird {
/* 飞鹰类选择器代码略 */
}
CSS代码可能还会更多一些,比如使用伪类选择器 :nth-of-type(序号) 来额外定义个别元素的具体样式如位置、大小等。这不是我们控制众多子元素的重点,重头戏是JS如何拿到它们的操作标识并管控它们。JS有能力拿到任意一个页面中的元素,它内置了很多这类方法,我个人最喜欢的是 querySelectorAll(参数 ) 方法,它可以基于 document(文档),也可以基于元素(比如子元素的父元素),参数 则可以使用CSS类选择器的完整名称(如 .bird )。例如,针对前面的HTML代码,帖子容器元素 id="mydiv",那么我们可以基于 mydiv 分别查询并拿到到 class="vid/player/bird" 之类的子元素的操作标识:
//JS代码 :拿到拥有 class 属性的子元素的操作标识
var vids = mydiv.querySelectorAll('.vid');
var players = mydiv.querySelectorAll('.player');
var birds = mydiv.querySelectorAll('.bird');
var 是 JS 的声明关键字,var vids 声明一个变量 vids ,vids 是我们定义的名字,用复数形式表示这是多个元素的集合,等号后面是给声明的变量 vids 赋值,取值方法是用基于父元素 mydiv 的 querySelectorAll() 方法,参数是完整的元素对标的CSS选择器 .vid ,因为这是一个字符串所以用引号包裹起来。其余两个声明与赋值道理与此同,无需赘述。
这样,以视频为例,vids 就是我们操纵所有视频子元素的依据。vids 是视频元素的集合,是多个的,它实际上是一个对象数组,数组就是一类东西的集合,我们要对这个集合进行操作,在JS里拥有多种方法,这里介绍最简洁的 数组.forEach() 方法,forEach() 说的是里面的每一个的意思:
vids.forEach( vid => aud.paused ? vid.pause() : vid.play() );
vids.forEach( vid => ... ) 意思是,数组 vids(视频集合)里面的每一个 vid(视频个体)要咋样咋样,其中,vid 是我们自己命名的名称,该名称代表将要处理的元素个体标识。接着,我们用箭头函数符号 => 引出函数体,因为是单行所以不要花括号。函数体内容我们应该已经熟悉了,意思是,问:音频是不是处于暂停状态中?答,是的话,vid.pause(),意思是 vid 也暂停;不是的话,vid.play(),意思是,vid 播放。其他两个数组 players 和 birds 也是采用类似的处理方法,不同的是,要使用 setProperty() 方法动态设置CSS关键帧动画动画的运行与暂停状态,后面提供的实例代码会看到。
下面给一个帖子实例代码,按前面的预设,有两个播放器、两个视频、三只飞鹰,这些元素都在帖子中得到同步管控。代码可以在线运行。领会代码时,请特别留意一下伪类选择器 :nth-of-type 与相应HTML标签出现顺序的关系,它是基于 HTML 标签而非 CSS选择器:
<!-- 第一部分 :css代码 -->
<style>
/* 帖子容器id选择器 */
#mydiv {
position: relative;
margin: 20px auto;
width: 800px;
height: 450px;
background: url('https://638183.freep.cn/638183/t22/webp/jyiu.webp') no-repeat center/cover;
overflow: hidden;
}
/* 小播class选择器 */
.player {
position: absolute;
bottom: 40px;
width: 120px;
height: 120px;
opacity: .7;
cursor: pointer;
animation: rot 8s linear infinite var(--state);
}
.player:nth-of-type(4) { left: 40px; } /* 对标的 img 标签排在帖子中第4位 */
.player:nth-of-type(5) { right: 40px; } /* 对标的 img 标签排在帖子中第5位 */
/* 飞鹰class选择器 */
.bird {
position: absolute;
left: -100px;
top: 10px;
animation: fly 6s linear infinite var(--state);
}
.bird:nth-of-type(2) { animation-delay: -2s; } /* 对标的 img 标签排在帖子中第2位 :提前2秒执行动画 */
.bird:nth-of-type(3) { animation-delay: -4s; } /* 对标的 img 标签排在帖子中第3位 :提前4秒执行动画 */
/* 视频class选择器 */
.vid {
position: absolute;
bottom: 0;
width: 200px;
height: 200px;
object-fit: cover;
border-radius: 50%;
pointer-events: none;
opacity: 1;
mix-blend-mode: screen;
}
.vid:nth-of-type(1) { left: 0; } /* 对标的 video 标签排在帖子中第1位 */
.vid:nth-of-type(2) { right: 0; } /* 对标的 video 标签排在帖子中第2位 */
@keyframes rot { to { transform: rotate(360deg); } }
@keyframes fly { to { left: 800px; } }
</style>
<!-- 第二部分 :html代码 父元素带8个子元素 -->
<div id="mydiv">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=1844994989" autoplay loop></audio>
<video class="vid" src="https://img.tukuppt.com/video_show/2418175/00/02/20/5b51f631b0f2d.mp4" autoplay loop muted></video>
<video class="vid" src="https://img.tukuppt.com/video_show/2418175/00/02/20/5b51f631b0f2d.mp4" autoplay loop muted></video>
<img class="bird" alt="" src="https://638183.freep.cn/638183/t22/gif/ying1.gif" />
<img class="bird" alt="" src="https://638183.freep.cn/638183/t22/gif/ying1.gif" />
<img class="bird" alt="" src="https://638183.freep.cn/638183/t22/gif/ying1.gif" />
<img class="player" alt="" src="https://638183.freep.cn/638183/small/4yc.png" title="播放/暂停" />
<img class="player" alt="" src="https://638183.freep.cn/638183/small/4yc.png" title="播放/暂停" />
</div>
<!-- 第三部分 :JS代码 -->
<script>
//声明并获取待控制的元素集合变量 : vids 视频、players 小播、birds 飞鹰
var vids = mydiv.querySelectorAll('.vid'),
players = mydiv.querySelectorAll('.player'),
birds = mydiv.querySelectorAll('.bird');
//联动控制函数
var mState = () => {
mydiv.style.setProperty('--state', aud.paused ? 'paused' : 'running');
vids.forEach(vid => aud.paused ? vid.pause() : vid.play());
players.forEach(player => player.title = aud.paused ? '播放' : '暂停');
};
//audio空间三个监听事件
aud.oncanplay = aud.onplaying = aud.onpause = () => mState();
//小播点击事件
players.forEach(player => player.onclick = () => aud.paused ? aud.play() : aud.pause());
</script>
点击这里运行代码
帖子内容增多多代码量自然跟着增多,不过只要弄清代码结构、逻辑关系,理解起来不是太难的事情。帖子代码中出现的新知识点均有注释,应花点时间理解消化。帖子实例已属于复杂构成的帖子,但还不算特别复杂,试着想一想:如果需要添加成百上千的子元素,我们也得这样写CSS和HTML代码吗?
返回目录