【原生javascript项目】Strip follow along nav 26
作者:©Iaine 万一
简介:30 Day Challenge是 Wes Bos 设计的一个 30 天原生js编程挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。本项目为第26天的“内容悬浮显示”项目。Have fun with the website! ♪(^∇^*)
源代码:https://github.com/janice143/JavaScript30Program/tree/master/26%20stripAlongNav/index.html

项目描述
本项目的网页主题是一个菜单栏,通过鼠标mouseenter进入每个菜单项目时,会显示出相应的隐藏扩展内容,当从一个菜单进入另一个菜单时,扩展内容的显示会有一个从上一个菜单平移到当前菜单的动态效果。
本项目的难点在于适应大小的动态显示和平移效果。由于扩展内容包含的类型各不相同,而且显示要有一个从上一个菜单平移到当前菜单的动态效果,要做到用通用化的操作,根据扩展内容的具体大小动态显示出,是一件值得思考的事情。
项目重点
- getBoundingClientRect()
项目过程
HTML部分
- 第一个菜单及其扩展内容 - 包含 - a标签标记的标题;类名为- dropdown,dropdown1的下拉菜单(一张图片和描述文本)- 1 
 2
 3
 4
 5
 6
 7
 8
 9- <li>
 <a href="#">About Me</a>
 <div class="dropdown dropdown1">
 <div class="bio">
 <img src="https://logo.clearbit.com/wesbos.com">
 <p>Wes Bos sure does love web development. He teaches things like JavaScript, CSS and BBQ. Wait. BBQ isn't part of web development. It should be though!</p>
 </div>
 </div>
 </li>
- 第二个菜单及其扩展内容 - 包含 - a标签标记的标题;类名为- dropdown,dropdown2的下拉菜单(类型相同的列表)- 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- <li>
 <a href="#">Courses</a>
 <ul class="dropdown courses">
 <li>
 <span class="code">RFB</span>
 <a href="https://ReactForBeginners.com">React For Beginners</a>
 </li>
 <li>
 <span class="code">ES6</span>
 <a href="https://ES6.io">ES6 For Everyone</a>
 </li>
 <li>
 <span class="code">NODE</span>
 <a href="https://LearnNode.com">Learn Node</a>
 </li>
 <li>
 <span class="code">STPU</span>
 <a href="https://SublimeTextBook.com">Sublime Text Power User</a>
 </li>
 <li>
 <span class="code">WTF</span>
 <a href="http://Flexbox.io">What The Flexbox?!</a>
 </li>
 <li>
 <span class="code">GRID</span>
 <a href="https://CSSGrid.io">CSS Grid</a>
 </li>
 <li>
 <span class="code">LRX</span>
 <a href="http://LearnRedux.com">Learn Redux</a>
 </li>
 <li>
 <span class="code">CLPU</span>
 <a href="http://CommandLinePowerUser.com">Command Line Power User</a>
 </li>
 <li>
 <span class="code">MMD</span>
 <a href="http://MasteringMarkdown.com">Mastering Markdown</a>
 </li>
 </ul>
 </li>
- 第三个菜单及其扩展内容 - 包含 - a标签标记的标题;类名为- dropdown,dropdown3的下拉菜单(类型相同的列表)- 1 
 2
 3
 4
 5
 6
 7
 8
 9- <li>
 <a href="#">Other Links</a>
 <ul class="dropdown dropdown3">
 <li><a class="button" href="http://twitter.com/wesbos">Twitter</a></li>
 <li><a class="button" href="http://facebook.com/wesbos.developer">Facebook</a></li>
 <li><a class="button" href="http://wesbos.com">Blog</a></li>
 <li><a class="button" href="http://wesbos.com/courses">Course Catalog</a></li>
 </ul>
 </li>
- 白背景 - 1 
 2
 3- <div class="dropdownBackground">
 <span class="arrow"></span>
 </div>
CSS部分
- trigger-enter类名。效果是显示下拉菜单,但是此时下拉菜单的不透明度还是为0,所以还是看不到下拉菜单- 1 
 2
 3- .trigger-enter .dropdown {
 display: block;
 }
- trigger-enter-active类名。效果是将下拉菜单的不透明度设置为1- 1 
 2
 3- .trigger-enter-active .dropdown {
 opacity: 1;
 }
- dropBackgorund类名。设置背景图片的样式,也是本项目难点的实现机制,通过移动背景白版,来实现一个动态平移的过程。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- .dropdownBackground {
 width: 100px;
 height: 100px;
 position: absolute;
 background: #fff;
 border-radius: 4px;
 box-shadow: 0 50px 100px rgba(50,50,93,.1), 0 15px 35px rgba(50,50,93,.15), 0 5px 15px rgba(0,0,0,.1);
 transition: all 0.3s, opacity 0.1s, transform 0.2s;
 transform-origin: 50% 0;
 display: flex;
 justify-content: center;
 opacity: 0;
 }
- open类名。将背景图片的不透明度设置为1- 1 
 2
 3- .dropdownBackground.open {
 opacity: 1;
 }
JS部分
- 鼠标进入导航栏项目时。 - 给对应的项目添加类名trigger-enter,随后150ms,添加类名trigger-enter-active。
- 白背景也要同时显示,所以添加类名open
- 利用getBoundingClientRect()属性获取元素的宽高和位置信息。这是本项目的难点的实现机制,根据下拉菜单大小动态显示。
- 白背景的显示位置是相对于已定位的nav元素,所以其top位置是下拉菜单的top位置减去nav的top位置。
 - 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- const triggers = document.querySelectorAll('.cool > li');
 const background = document.querySelector('.dropdownBackground');
 const nav = document.querySelector('.top');
 function handleEnter() {
 this.classList.add('trigger-enter');
 setTimeout(() => this.classList.contains('trigger-enter') && this.classList.add('trigger-enter-active'), 150);
 background.classList.add('open');
 const dropdown = this.querySelector('.dropdown');
 const dropdownCoords = dropdown.getBoundingClientRect();
 const navCoords = nav.getBoundingClientRect();
 const coords = {
 height: dropdownCoords.height,
 width: dropdownCoords.width,
 top: dropdownCoords.top - navCoords.top,
 left: dropdownCoords.left - navCoords.left
 };
 background.style.setProperty('width', `${coords.width}px`);
 background.style.setProperty('height', `${coords.height}px`);
 background.style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`);
 }
 triggers.forEach(trigger => trigger.addEventListener('mouseenter', handleEnter));
- 给对应的项目添加类名
- 鼠标移除导航栏菜单时。删除类名 - trigger-enter,- trigger-enter-active,- open。- 1 
 2
 3
 4
 5- function handleLeave() {
 this.classList.remove('trigger-enter', 'trigger-enter-active');
 background.classList.remove('open');
 }
 triggers.forEach(trigger => trigger.addEventListener('mouseleave', handleLeave));
项目补充
getBoundingClientRect()
返回DOMRect对象,包含元素的大小和相对视口的位置信息。
| 1 |  | 
will-change: opacity
提醒浏览器该元素的opacity特性将会改变
A > B
匹配A的所有直接后代B
JS30的第26个项目圆满完成啦,感谢阅读,有问题联系我的邮箱1803105538@qq.com.