iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 27
0
Modern Web

森林系工程師之開始工作才學JS?!系列 第 28

Day27 -- Stripe Follow Along Dropdown

目標

今天要來做的是做出stripe網站上,游標選到nav bar上就出現選單的效果

https://ithelp.ithome.com.tw/upload/images/20201010/20121041ksstg9YAiw.png

Step1

https://ithelp.ithome.com.tw/upload/images/20201010/20121041D4k0nZD8C9.png

const triggers = document.querySelectorAll('.cool > li');
const background = document.querySelector('.dropdownBackground');
const nav = document.querySelector('.top');

function handelEnter() {
    console.log('enter!!');
}

function handelLeave() {
    console.log('leave!!');
}

triggers.forEach(trigger => trigger.addEventListener('mouseenter', handelEnter));
triggers.forEach(trigger => trigger.addEventListener('mouseleave', handelLeave));

首先,我們要先選定上圖的三個列表元素,還有當dropodown出現時,要加上一個背景,再來是nav bar

這邊需要兩個函式,分別處理游標移入跟移出

https://ithelp.ithome.com.tw/upload/images/20201010/20121041w5J8WOxV7b.png

Step2

接下來,當游標移入時,我們要幫觸發函式的列表元素加上trigger-enter以及trigger-enter-active

function handelEnter() {
    console.log('enter!!');
    this.classList.add('trigger-enter')
    setTimeout(() => {this.classList.add('trigger-enter-active')}, 150)
}

function handelLeave() {
    console.log('leave!!');
    this.classList.remove('trigger-enter', 'trigger-enter-active')
}

https://ithelp.ithome.com.tw/upload/images/20201010/20121041kPvrhhRKaQ.png

WindowOrWorkerGlobalScope.setTimeout()

setTimeout()方法會幫接下來要觸發的函式加上一個計時器,讓其可以延遲觸發,時間以毫秒計算

Step3

這邊要替出現的選單加上背景

function handelEnter() {
    // ...
    background.classList.add('open');
}

function handelLeave() {
    // ...
    background.classList.remove('open');
}

背景的CSS設定就由原作者提供,這邊只需要加上一個open class,透明度就會被設為1

.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;
}

.dropdownBackground.open {
    opacity: 1;
}

https://ithelp.ithome.com.tw/upload/images/20201010/20121041SjCAonWDy3.png

接下來要開始處理選單背景的大小和位置

function handelEnter() {
		// ...
    const dropdown = this.querySelector('.dropdown');
    console.log(dropdown);
}

這邊要在游標移入的時候選定選單,因為這邊總共有三個選單,而每一個的大小都不一樣

https://ithelp.ithome.com.tw/upload/images/20201010/20121041c5s43DU9Zh.png

function handelEnter() {
		// ...
    const dropdown = this.querySelector('.dropdown');
		const dropdownCoords = dropdown.getBoundingClientRect()
}

接下來用之前提過的getBoundingClientRect(),取得列表的大小和位置

function handelEnter() {
		// ...
		const coords = {
		    height: dropdownCoords.height,
		    width: dropdownCoords.width
		};
		
		background.style.setProperty('width', `${coords.width}px`);
		background.style.setProperty('height', `${coords.height}px`);
}

這邊先帶入列表大小的數據,由下兩圖可以看到選單背景的大小不同

https://ithelp.ithome.com.tw/upload/images/20201010/20121041sttYtKZeNT.png

https://ithelp.ithome.com.tw/upload/images/20201010/20121041dMFx03Kh5V.png

function handelEnter() {
		// ...
		const coords = {
        height: dropdownCoords.height,
        width: dropdownCoords.width,
        top: dropdownCoords.top,
        left: dropdownCoords.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)`);
}

這邊帶入位置資訊,背景好像就對上了(?

https://ithelp.ithome.com.tw/upload/images/20201010/201210411r8mTJqitP.png

https://ithelp.ithome.com.tw/upload/images/20201010/20121041M7hZRvSJ1b.png

但是如果在列表元素上面還有其他html元素呢?

const coords = {
    height: dropdownCoords.height,
    width: dropdownCoords.width,
    top: dropdownCoords.top - navCoords.top,
    left: dropdownCoords.left - navCoords.left
};

所以,我們還會需要nav bar的位置,然後自己做一下計算

Ta-da!這樣問題就解決囉

https://ithelp.ithome.com.tw/upload/images/20201010/20121041kBzavsqNRO.png

Step4

讓我們仔細看一下html現在的狀態

https://ithelp.ithome.com.tw/upload/images/20201010/20121041dc16l9xwSH.png

咦!?怎麼明明沒選任何列表,但class卻有trigger-enter-active呢?

這是因為我們在加上class的時候有做一點延遲,當我們的游標快速移動,選過這些列表元素,但trigger-enter-active還沒出現,然後就離開了,所以應該被移除的class就在移除之後出現

這邊原作者提出兩種解法:

  • 加上一個判斷式
setTimeout(() => { 
		if(this.classList.contains('trigger-enter') {
				this.classList.add('trigger-enter-active') 
		}
}, 150)
  • 運用邏輯運算子

這邊的思路跟加上判斷式差不多,要先滿足第一個條件,然後才會執行接下來的動作

setTimeout(() => this.classList.contains('trigger-enter') && this.classList.add('trigger-enter-active'), 150);

上一篇
Day26 -- Event Capture, Propagation, Bubbling and Once
下一篇
Day28 -- Click and Drag
系列文
森林系工程師之開始工作才學JS?!32

尚未有邦友留言

立即登入留言