滑鼠切換到導覽列的不同選項時,會顯示該選項的下拉式清單,此外,下拉式清單會以「移動」的方式切換。範例連結
HTML 標籤如下。
<nav class="top">
<!-- 下拉式選單的背景 -->
<div class="dropdownBackground">
<!-- 黏在背景上的箭頭 -->
<span class="arrow"></span>
</div>
<!-- 導覽列的主體 -->
<ul class="cool">
<li>
<a href="#">About Me</a>
<div class="dropdown">
<!-- 下拉選單內容 -->
</div>
</li>
<li>
<a href="#">Courses</a>
<ul class="dropdown">
<!-- 下拉選單內容 2 -->
</ul>
</li>
<li>
<a href="#">Other Links</a>
<ul class="dropdown">
<!-- 下拉選單內容 3 -->
</ul>
</li>
</ul>
</nav>
下拉式清單會移動其實是我們利用 Day 22 的手法製造出來的錯覺。
我們額外製作出一個元素 dropdownBackground
假冒下拉式選單的背景,當滑鼠移動到導覽列的選項 <li>
時,讀取該選項的下拉式選單 dropdown
位置、內容的寬度等資訊,並利用這些資訊呈現假冒的下拉式選單背景,最後在假冒的選單背景上附加轉場效果,就會讓下拉選單在切換時有移動的效果了。
程式碼架構如下:
// 導覽列的清單的所有選項
const triggers = document.querySelectorAll('.cool > li');
// 假冒下拉背景的元素
const background = document.querySelector('.dropdownBackground');
// 整個導覽列元素
const nav = document.querySelector('.top');
function handleEnter() {
// 處理移入效果
}
function handleLeave() {
// 處理移出效果
}
// 滑鼠移入選項時處理移入效果
triggers.forEach(trigger => trigger.addEventListener('mouseenter', handleEnter));
// 滑鼠移出選項時處理移出效果
triggers.forEach(trigger => trigger.addEventListener('mouseleave', handleLeave));
重點在如何處理移入與移出效果。首先我們希望下拉選單 dropdown
的內容在滑鼠移入後可以出現。程式碼如下:
function handleEnter() {
this.classList.add('trigger-enter');
setTimeout(() => {
console.log(this.classList);
if(this.classList.contains('trigger-enter')) {
this.classList.add('trigger-enter-active');
}
} ,150);
}
CSS設定如下:
.dropdown {
/* 前略 */
transition: all 0.5s;
opacity: 0;
display: none;
}
.trigger-enter .dropdown {
display: block;
}
.trigger-enter-active .dropdown {
opacity: 1;
}
只要 dropdown
父層元素加上 trigger-enter
、 trigger-enter-active
屬性,就能讓元素顯現。
但大家可能會發現,為了切換 opacity
與 display: block
,我們不只將其分開成兩個類別,還大費周章地讓 trigger-enter-active
在 trigger-enter
後才執行,而且還特意地等了 150 毫秒。為何要這麼做?
因為如果將其放在同個類別同時添加, opacity
的動作將不會顯現出來,原因是 當一個元素設定為 display: none
,瀏覽器根本不會將呈現出來。而 opacity
的切換對某個「確實存在」的元素才有效果。若將兩者放在同個類別同時切換,相當於對著 display: none
的元素進行 opacity
調整,是無效的。
了解這部分後,接下來只要運用 Day 22 的手法加入下拉式選單的背景就好。程式碼如下:
function handleEnter() {
this.classList.add('trigger-enter');
setTimeout(() => {
if(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 = {
width: dropdownCoords.width,
height: dropdownCoords.height,
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)`);
}
利用 getBoundingClientRect()
取得該下拉式選單 dropdown
的座標位置跟寬高等資訊,然後與最外層元素 nav
的位置相減來得到該下拉式選單相對於整個導覽列的位置。用這些資訊拉設定假冒下拉式選單的位置與寬高即可。
以上就是 JS30 第二十六篇!