JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks
、No Compilers
、No Libraries
、No Boilerplate
在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
利用 flexbox
、transition
等 CSS 屬性,搭配 JS 監聽 transitionend
、click
事件,最終實作出一個美觀的 Image Gallery。
由最外層的.panels
包覆住內部的5個.panel
所形成的一個巢狀結構。
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
(僅說明影片中更動的部分)
先將最外層的 .panels
顯示類型設定為 flex
,它同時也作為一個 flex-container
包覆住內部的五個 flex-items
也就是 .panel
。
.panels {
/*其餘略過*/
display: flex;
}
flex
是 flex-grow
、flex-shrink
、flex-basis
的簡寫,只有指定一個值給 flex
時,則代表設定的是 flex-grow
,其餘屬性以預設值帶入。
flex-grow
可以指定 flex-container
的剩餘空間該如何分配,下面所有的 .panel
的 flex-grow
都是 1,也就是均勻分配剩餘空間。
接著,將每一個 .panel
的顯示類型都設定為 flex
(此時的.panel
對下面的<p></p>
來說就是 flex-container
) 並將其下的 flex-item
在水平、鉛直方向都置中。
最後,設定 flex-direction
將 flex-box
的 main-axis
更改為直列。
.panel {
flex: 1; /*將每個 flex-item 的大小都設為一樣並填滿*/
display: flex;
justify-content: center; /*在水平方向置中*/
align-items: center;/*在鉛直方向置中*/
flex-direction: column;
}
將 .panel
(flex-container
)下的 <p></p>
(flex-item
) 的 flex
屬性設定為 1、0、auto,也就是均勻分配 flex-container
的剩餘空間、flex-item
長度超過 flex-container
時的收縮量設為 0、flex-item
在 flex container
的初始大小設為自動(auto)。
接著,也將<p></p>
當作是一個 flex-container
,設定顯示類型為 flex
。透過 justify-content
、align-items
,將標籤內的文字(flex-item
) 水平、鉛直置中排列。
.panel > * {
/*其餘省略*/
flex: 1 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
初始狀態,分別將在 .panel
上方和下方的 <p></p>
都各上移、下移 100% 以達到隱藏的效果。
.panel > *:first-child{
transform: translateY(-100%);/*上移*/
}
.panel > *:last-child{
transform: translateY(100%);/*下移*/
}
接著設定當.panel
上有open-active
這個 class 時,就將原本隱藏的文字分別下移和上移顯示出來。
.panel.open-active > *:first-child{
transform: translateY(0);
}
.panel.open-active > *:last-child{
transform: translateY(0);
}
.panel
開啟時,將內部文字放大為 40px 並將 flex-container
剩餘分配的位置變為原來的5倍。
.panel.open {
font-size: 40px;
flex: 5;
}
CSS flex 屬性
圖解 Flexbox 基本屬性
FLEXBOX FROGGY-學習 flexbox 的小遊戲
取得所有的.panel
並放到 NodeList
'panels'
中。
const panels = document.querySelectorAll('.panel');
為 panels
中的每一個.panel
都註冊兩個事件監聽器,當 click
或 transitionend
事件發生時,就分別以 toggleOpen
和 toggleActive
方法進行事件處理。
panels.forEach(panel => panel.addEventListener('click',toggleOpen));
panels.forEach(panel => panel.addEventListener('transitionend',toggleActive));
點擊任意一個.panel
,toggleOpen()
方法會替觸發事件的.panel
依照情況的不同,新增或是移除.open
這個 class,若是觸發事件的.panel
原本沒有.open
則新增,有的話則移除.open
。
當 transitionend
事件發生,toggleActive()
會作出如同 toggleOpen()
一樣的判斷,決定新增還是移除.open-active
這個 class。因為同時會被觸發的 transitionend
事件有很多個,我們決定在觸發 transitionend
事件的 CSS 屬性是 flex
時,才採取處理。為什麼不寫 e.propertyName == 'flex-grow'
是因為在 Safari 顯示的是 flex
而 Chrome、FireFox 顯示的是 flex-grow
,為避免這個差異導致錯誤,我們可以使用 include('flex'),當 propertyName 含有 flex
就進行事件處理。
function toggleOpen(){
this.classList.toggle('open');
}
function toggleActive(e){
if(e.propertyName.includes('flex')){
this.classList.toggle('open-active');
}
}
Element.classList
String.prototype.includes()