0%

前言

CSS預處理器,增加了一些編寫的特性,也可以說是運用程式化的方式寫CSS。

  • 其中SASS最廣泛使用:Sass包含SASS與SCSS兩種
    • 兩者之間不同之處有以下兩點:
    • 1.文件擴展名不同,Sass 是以“.sass”後綴為擴展名,而SCSS 是以“.scss”後綴為擴展名
    • 2.語法書寫方式不同,Sass是以嚴格的縮進式語法規則來書寫,不帶大括號({})和分號(;),而SCSS的語法書寫和我們的CSS語法書寫方式非常類似。

      安裝sass

      sass-github
  • 在專案底下,安裝
    1
    npm install -g sass
  • 安裝後,建立index.scss檔案
  • 手動轉擋
    • 會出現css檔案

使用node-sass編譯

  1. 先取得package.json檔案
    安裝語法:npm init --yes
    (要注意安裝的路徑位置)

  2. 安裝node-sass
  • npm i -g node-sass (i -> install)
  • 查尋安裝是否完成
  1. 將檔案編譯為css
    建立css(資料夾)

    使用watch,使每次修改sass檔案時,會自動編譯到css中

  • 在package.jason檔案中的"scripts"
  • 要注意編譯資料的位置
    • css :為資料夾路徑
    • scss/main.scss:建立的css路徑
1
2
3
"scripts": {
"watch": "node-sass -o css scss/main.scss -w"
}
  • 在終端機要輸入
    npm run watchcontrol+c退出

基本語法實作

變數

  • 在變數前加上$,來宣告要使用的變數
  • 運用抽取變數方式,使運用版面顏色時,可以有一致性,修改版面時也較快速
  • 變數:數量不要超過10個,自己在記憶上會混亂
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 創造顏色、字大小的變數
    $white:#fff;
    $black:#000;
    .button{
    text-align:center;
    background-color:$black;
    border:1px solid $black;
    color:$white;
    }

    $font-lg:40px;
    $font-md:30px;
    $font-sm:20px;

    .footer h3{
    font-size: $font-md;
    color: $color-secondary;
    text-align: center;

    }

    scope of sass variable

  • 一般常見屬於global scope
  • 如果將變數設在,標間架構中->local scope
  • 在後面加上!global就變成全域

PS: 還是建議以global 的方式進行修改,才不會造成維護的混亂

  • 命名中使用-以及_,在sass中是一樣的
    • $font-lg改為$font_lg一樣也會有效

Nested 巢狀

1.針對nav下的ulli

1
2
3
4
5
6
7
8
9
10
11
.nav{
background-color: $color-primary;
ul li{
list-style: none;
}
a{
text-decoration: none;
font-size: $font-sm;
color: $color-secondary;
}
}
  • 轉換為css
    1
    2
    3
    4
    5
    6
    7
    8
    .nav {
    background-color: orange; }
    .nav ul li {
    list-style: none; }
    .nav a {
    text-decoration: none;
    font-size: 20px;
    color: gray; }

2.增加hover的效果

  • &,來指出parent element:在這便是指<a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.nav{
background-color: $color-primary;
ul li{
list-style: none;
}
a{
text-decoration: none;
font-size: $font-sm;
color: $color-secondary;
&:hover{
color: $color-tertiary;
}
}
}
  • 轉換為css
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    .nav {
    background-color: orange; }
    .nav ul li {
    list-style: none; }
    .nav a {
    text-decoration: none;
    font-size: 20px;
    color: gray; }
    .nav a:hover {
    color: royalblue; }

巢狀層級不可以太多層,否則會出現效率問題

@import 是什麼

  • import 讓我們可以拉出特定區塊或是根據功能性不同拆成不同區塊,來撰寫scss,並利用@import放入主要的scss檔案,最後彙整到同一個css之中。
  • 再用一個css引入(@import),各個功能
  • 要引入的檔案命名:在要import的檔案要加_

六角學院

_test.scss為撰寫footer樣式的檔案

1
2
3
4
.footer h3{
font-style:italic ;
border: blue 3px solid;
}

main.scss 要引入檔案的主要檔案

1
@import "test";

轉譯為css(main.css)

1
2
3
.footer h3 {
font-style: italic;
border: blue 3px solid; }

@mixin 與 @include

  • 當在多個地方想使用同一種style的方式。可以組成一組的樣式,並於scss中重複使用。
  • @mixin 開頭,並自定義一個名稱
  • 需要它的時候就用 @include呼叫
  1. 設定對齊方式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @mixin horizontal-list{
    li{
    display:inline-block;
    margin:{
    left:-2px;
    right:2em;
    }
    }
    }

    /*當要引用時*/
    nav ul{
    @include horizontal-list
    }

  2. 調整字體大小(以帶參數的方式)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 發現h1,h3樣式一樣
    @mixin headingStyles($fontSize){
    //以代參數方式來放入不同字體大小的變數設定
    font-size: $fontSize;
    color: $color-secondary;
    text-align: center;
    }


    .banner h1{

    @include headingStyles($font-lg);
    }
    .footer h3{

    @include headingStyles($font-md);
    }
  • 在css的地方,可以看到字的大小不同
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .banner h1 {
    font-size: 40px;
    color: gray;
    text-align: center; }

    .footer h3 {
    font-size: 30px;
    color: gray;
    text-align: center; }

  1. 加入動畫效果
  • footer h3加上hover
  • @mixin transition($param...)加上設定參數特性
    • 當要同時設定特性中的多個值時,要記得在參數後面加上...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 發現h1,h3樣式一樣
@mixin headingStyles($fontSize){
//以代參數方式來放入不同字體大小的變數設定
font-size: $fontSize;
color: $color-secondary;
text-align: center;
}

@mixin transition($param...) {
transition: $param;
}
.banner h1{

@include headingStyles($font-lg);
}
.footer h3{

@include headingStyles($font-md);
@include transition(color .5s ,background-color 1s);
&:hover{
color:$color-tertiary;
background-color: $color-primary;
}
}
  • 轉譯為css
    1
    2
    3
    4
    5
    6
    7
    8
    .footer h3 {
    font-size: 30px;
    color: gray;
    text-align: center;
    transition: color 0.5s, background-color 1s; }
    .footer h3:hover {
    color: royalblue;
    background-color: orange; }

Day27:小事之 Transition 與 Animation
CSS3 Animation

新手也可以輕鬆玩轉 SASS - @mixin and @include

Functions

  • 透過函式的建立,可以在需要使用時,不斷地呼叫使用
  • @function fontSize($size){ @return $size*2; }建立函式
1
2
3
4
5
6
7
8
9
10
11
12
13
@function fontSize($size){
@return $size*2;
}


h1{


.banner p{
font-size: fontSize($font-sm);
//字的大小會變成 20px*2 =>40px
}

轉譯為css

1
2
.banner p {
font-size: 40px; }
  • 將文字大小,預先設定預設值
1
2
3
4
5
6
7
8
9
10
//設定預設為25px
@function fontSize($size:25px){
@return $size*2;
}



.footer p{
font-size: fontSize();
}

轉譯為css

1
2
3
.footer p {
font-size: 50px; }
/* 25*2=>50 */
  • 若插入數字
1
2
3
4
.footer p{
font-size: fontSize(30px);
}

  • 轉譯為css
    1
    2
    3
    .footer p {
    font-size: 60px; }
    /* 30*2=>50 */

    內建函式介紹

  1. lighten()
    1
    2
    .nav{
    background-color: lighten($color-primary,20%);}
    css
    1
    2
    .nav {
    background-color: #ffc966; }
  • 顏色變淡
  1. darken()
    1
    2
    .nav{
    background-color: darken($color-primary,20%);}
  • 編譯為css
    1
    2
    .nav {
    background-color: #996300; }

3.transparentize()

  • 透明度從0-1 (1是完全透明)
    1
    2
    .nav{
    background-color: transparentize($color-primary,0.6);}
  • 編譯為css
    1
    2
    .nav {
    background-color: rgba(255, 165, 0, 0.4); }

4.mix()

  • 將顏色混合
    1
    2
    .nav{
    background-color: mix(blue,green);}
    css
    1
    2
    .nav {
    background-color: #004080; }

加入百分比(利用百分比來調配顏色的比重)

  • mix(blue,green,10%)表示只有10%的藍色,90%為綠色
    1
    2
    .nav{
    background-color: mix(blue,green,10%);}
    css
    1
    2
    .nav {
    background-color: #00731a; }

補充scss檔案編制

  • 主要視自己專案上的區分來編制
    vendor:外部套件,放置bootstrap或其他frameworks、liberaries、hover.css
    utils(helpers):工具類的class,放置字的大小、margin、border顏色設置、hover效果
    partials:放置一些部分區域的css,如:nav、footer; layout: (共通的佈局)如表頭、表尾,就是每頁都會出現
    而這幾個檔案,放在main.scss要有順序

    base:p段落、h1..等的全站設定(css reset)
    components :元件,如按鈕、卡片的設定

參考資料:alphacamp-Sass/SCSS 基本語法介紹,搞懂CSS 預處理器

成品

觀察功能需求

  • 搜尋框,輸入搜尋關鍵字,下方會列出5筆列表
    • 有圖片縮圖、影片標題、影片描述
  • 按下next page 會取得下5筆資料
  • 按下prev page,會取得上5筆資料

ajax 在同一個頁面,不做刷新就可以改變頁面的內容。
q: query

youtube API(application programming interface)資料查看

YouTube 資料 API 參考手冊

取得授權:憑證

API 金鑰:AIzaSyCKPRCaNS-PQJLl2jzoQEf-O4I0tnrtd8M

1
2
jQuery API D ocument => 表示看官方文件
串API =>

檢視範例,ajax資料的情形

JS 部分

1.嘗試串接Youtube API

  • 首先需考量,我們所需要的資料參數有哪些(
  • 在抓取成功的部分,要設置抓取五筆搜尋到的五筆資料,並將其呈現於頁面
    • 除了5筆資料外,還需要跳轉上、下頁的按鈕
      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
      function getVideo() {
      let value = $('#search-field').val();
      let youtubeURL = "https://www.googleapis.com/youtube/v3/search";
      $.ajax({
      type: "GET",
      url: youtubeURL,
      data: {//將要放入網址的參數放在這
      part:
      'id,snippet',// 必填,把需要的資訊列出來
      q: value,// 查詢文字
      maxResults: 5,// 預設為五筆資料,可以設定1~50
      type: "video",
      key: 'AIzaSyCKPRCaNS-PQJLl2jzoQEf-O4I0tnrtd8M'// 使用 API 只能取得公開的播放清單

      },
      dataType: "json",
      success: function (data) {
      //console.log(data);
      let nextPageToken = data.nextPageToken;
      let prevPageToken = data.prevPageToken;

      //試著將收到的資料傳入,並呈現於畫面中
      insertItems(data);

      getBtn(data, prevPageToken, nextPageToken)


      }, error: function (err) {
      console.log('oh no');
      }
      });
      }
      網址放入參數
      [JS] 使用 JavaScript 解析網址與處理網址中的參數(URL Parameters)

2.將資料呈現於頁面(insertItems())

  • 透過迴圈方式將要抓取的資料依序填入
  • 觀察原本畫面結構,並依據去找尋資料中符合的資料
    • videoId、videoTitle、description、channelTitle…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function insertItems(item) {
let output = '';
$.each(item.items, function (index, value) {
let videoId = item.items[index].id.videoId;
let videoTitle = item.items[index].snippet.title;
let description = item.items[index].snippet.description;
let thumbnailURL = item.items[index].snippet.thumbnails.high.url;//高解析度影片縮圖
let channelTitle = item.items[index].snippet.channelTitle;
let publishedDate = item.items[index].snippet.publishedAt;
// 按鈕



output += `<li><div class="list-left">
<img src=" ${thumbnailURL}"></div>
<div class="list-right">
<h3>
<a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${videoId}" "href="javascript:;"> ${videoTitle} </a></h3> <small>By <span class="cTitle"> ${channelTitle}</span> on ${publishedDate}</small>
<p> ${description} <p></div></li> <div classs="clearfix"></div>
`

$('#results').html(output);
});
};

3.製作上、下頁的按鈕鍵

  • 注意該函式要帶入的參數有哪些
  • 我們要判斷為,在第一頁時,只有下一頁的按鈕
    • 判斷方式為,(!prevPageToken)沒有上一頁的token
    • (截圖)
  • 在判斷之中,我們需要將要插入的標籤與內容,放入,並append到畫面要呈現的位置
  • 注意:在插入的button之中,加入data-token="${nextPageToken}" data-query="${value}",用於後續抓取屬性內容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getBtn(value, prevPageToken, nextPageToken) {
console.log(nextPageToken)
if (!prevPageToken) {
var btnOutput = $(`<div class="button-container">
<button id="next-button" class="paging-button" data-token="${nextPageToken}" data-query="${value}" onclick="pressNext();"> Next Page</button></div>`
)
$("#buttons").append(btnOutput);
} else {
var btnOutput = $(`<div class="button-container"><button id="prev-button" class="paging-button" data-token="${prevPageToken}" data-query="${value}" onclick="pressPrev();"> Prev Page</button>
<button id="next-button" class="paging-button" data-token="${nextPageToken}" data-query="${value}" onclick="pressNext();">Next Page</button></div>`)
$("#buttons").append(btnOutput);
}

}

Youtube:Implementation: Pagination

4.下一頁點下後,下五筆的資料

  • 同樣以ajax的方式來抓取
    • 在參數的地方我們要抓取的是,按取下一頁的token
  • success之中所應用的函示,同樣為插入5筆的資料,以及上下頁的按鈕
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
//取得下一頁的資料
function pressNext() {

let youtubeURL = "https://www.googleapis.com/youtube/v3/search";
let token = $('#next-button').data('token');
let value = $('#search-field').val();
$.ajax({
type: "GET",
url: youtubeURL,
data: {//將要放入網址的參數放在這
part:
'id,snippet',// 必填,把需要的資訊列出來
q: value,// 查詢文字
pageToken: token,
maxResults: 5,// 預設為五筆資料,可以設定1~50
type: "video",

key: 'AIzaSyCKPRCaNS-PQJLl2jzoQEf-O4I0tnrtd8M'// 使用 API 只能取得公開的播放清單

},
dataType: "json",
success: function (data) {
console.log(data);
let nextPageToken = data.nextPageToken;
let prevPageToken = data.prevPageToken;

// 清空內容
$('#results').html('');
$('#buttons').html('');
//試著將收到的資料傳入,並呈現於畫面中
insertItems(data);

getBtn(data, prevPageToken, nextPageToken)


}, error: function (err) {
console.log('oh no');
}
});

}

5.上一頁

  • 同樣的抓取方式,只有要帶入的資料有些差異
  • token = $('#prev-button').data('token');
    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
    42
    43
    //前一頁
    function pressPrev() {
    // 清空內容
    $('#results').html('');
    $('#buttons').html('');
    let youtubeURL = new URL(`https://www.googleapis.com/youtube/v3/search?`);
    let token = $('#prev-button').data('token');
    let value = $('#search-field').val();
    $.ajax({
    type: "GET",
    url: youtubeURL,
    data: {//將要放入網址的參數放在這
    part:
    'id,snippet',// 必填,把需要的資訊列出來
    q: value,// 查詢文字
    pageToken: token,
    maxResults: 5,// 預設為五筆資料,可以設定1~50
    type: "video",

    key: 'AIzaSyCKPRCaNS-PQJLl2jzoQEf-O4I0tnrtd8M'// 使用 API 只能取得公開的播放清單

    },
    dataType: "json",
    success: function (data) {
    console.log(data);
    let nextPageToken = data.nextPageToken;
    let prevPageToken = data.prevPageToken;

    // 清空內容
    $('#results').html('');
    $('#buttons').html('');
    //試著將收到的資料傳入,並呈現於畫面中
    insertItems(data);

    getBtn(data, prevPageToken, nextPageToken)


    }, error: function (err) {
    console.log('oh no');
    }
    });

    }

HTML5 自定義屬性 data-* 和 jQuery.data 詳解
2018.06.20

[技術分享] 什麼是 HTML 5 中的資料屬性(data-* attribute)

6.運用fancybox的方式彈出

  • 使用fancyBox展示影片

  • 使用iframe連結欲嵌入的外部影片連結

    • iframe 是框架的一種,也稱為內置框架或內聯框架,用來在網頁內嵌入另外一個網頁
  • 透過CDN方式連結fancybox套件

    1
    2
    3
    <!-- fancyBox -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js"></script>
  • 在我們點選該影片名稱區,插入影片id

    1
    2
    <h3><a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${videoId}" "href="javascript:;"> ${videoTitle} 
    </a></h3>
    1
    2
    3
    4
    5
    6
    7
    $('[data-fancybox]').fancybox({
    toolbar : false,
    smallBtn : true,
    iframe : {
    preload : false
    }
    })

    [jQuery] - 練習-Youtube Search Engine


補充知識:
XMLHttpRequest(XHR):是一種規範,用來發ajax使用

token(令牌):唯一的、可以認證身份或取得資料

GET:向伺服器發request,(伺服器會告訴我們要發伺服器的網址,我們向它請求資訊)

用瀏覽器debug

  • 可在watch加上要觀察的變數

參考資料
[30apis] Day 7 : YouTube Data API + 基礎 Fetch API
How To Implement A Youtube Video Search Using Youtube Data API V3
Youtube Data API 使用手札
Youtube Data API 申請
配額

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"error": {
"code": 403,
"message": "The request cannot be completed because you have exceeded your \u003ca href=\"/youtube/v3/getting-started#quota\"\u003equota\u003c/a\u003e.",
"errors": [
{
"message": "The request cannot be completed because you have exceeded your \u003ca href=\"/youtube/v3/getting-started#quota\"\u003equota\u003c/a\u003e.",
"domain": "youtube.quota",
"reason": "quotaExceeded"
}
]
}
}

favicon.ico error

問題:

  • script.js: Allow attribute will take precedence over ‘allowfullscreen’.

成品

觀察功能需求

  • 從公開的氣象資料平台取得氣象資料
  • 建立下拉式選單,選擇地區,並按下按鈕取得氣象資料
  • 呈現當日天氣狀況(包含氣溫、圖示、描述)
  • 呈現未來一週的天氣預報(包含日期、星期;氣溫、圖示、描述)

了解要串接的資料open data

至中央氣象局-氣象開放資料平台的網站,註冊會員
註冊並登入之後,要取得授權碼

中央氣象局開放資料平臺之資料擷取API,查看要取用的資料
我的授權碼:CWB-F0145DA5-2539-4333-BAFD-466910C1EECC

參考資料:
https://www.youtube.com/watch?v=QDdn3yrsyCQ
[Day 13 - 即時天氣] 建立一個即時天氣 App - 前置準備

尋找要參考實現畫面呈現的資料

畫面主要樣式是參考:
Build a Simple Weather App With Vanilla JavaScript

JS部分

1.利用Ajax串接資料

  • 根據平台提供的url作為取得資料的地方
  • 利用sucess/error,來查看是否有成功取得資料
    • 依據console.log()出要串接的資料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getCity() {
let url = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-091?Authorization=CWB-F0145DA5-2539-4333-BAFD-466910C1EECC&format=JSON";

$.ajax({
type: "get",
url: url,

dataType: "json",
success: function (data) {
console.log(data);

})

},
error: function (err) {
console.log('oh no')
}
});

2.製作下拉式選單

  • 當串到想要的資料後,希望能在下拉選單,呈現出各個地區,以供選擇
  • 因為地區有很多,以迴圈方式來填入資料
    • i的長度 data.records.locations[0].location.length
    • opt = $('<option></option>')創立選項標籤
    • 利用attr,html填入該地區的index,以及地區名稱
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
function getCity() {
let url = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-091?Authorization=CWB-F0145DA5-2539-4333-BAFD-466910C1EECC&format=JSON";

$.ajax({
type: "get",
url: url,

dataType: "json",
success: function (data) {
//console.log(data);
let selectCity = $('#selector-list');
for (let i = 0; i < data.records.locations[0].location.length; i++) {
let opt = $('<option></option>');
opt.attr("data-index", i);
opt.html(data.records.locations[0].location[i].locationName);
selectCity.append(opt);
}


},
error: function (err) {
console.log('oh no')
}
});


};

3.點選(submit)按鈕時要跳出取得今日天氣以及未來一週的天氣

  • 在sucess:中放入取得今日天氣、週天氣的函式
    • 在此要宣告selectedCityIndex變數,並取得被選取的city index$('#selector-list').get(0).selectedIndex
      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
      function getCity() {
      let url = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-091?Authorization=CWB-F0145DA5-2539-4333-BAFD-466910C1EECC&format=JSON";

      $.ajax({
      type: "get",
      url: url,

      dataType: "json",
      success: function (data) {
      console.log(data);
      let selectCity = $('#selector-list');
      for (let i = 0; i < data.records.locations[0].location.length; i++) {
      let opt = $('<option></option>');
      opt.attr("data-index", i);
      opt.html(data.records.locations[0].location[i].locationName);
      selectCity.append(opt);
      }
      $('button').click(function (e) {
      e.preventDefault();
      clear();
      let selectedCityIndex = $('#selector-list').get(0).selectedIndex;
      getWeather(data, selectedCityIndex);
      weekWeather(data, selectedCityIndex);
      })

      },
      error: function (err) {
      console.log('oh no')
      }
      });


      };
      jQuery獲取Select選擇的Text和Value:

4.取得今日天氣(getWeather();)

  • 傳入參數分別為data\locationIndex:data為串接到的天氣資料,locationIndex,為放入要取的資料中的index
  • 我們想要取得的氣象資料,分別有地區、溫度(最低溫、最高溫)、天氣描述、icon圖示
    • name變數就是要取得資料內的地區名稱
    • weather變數就是取得氣象資料
    • weatherDescription以及weatherCode分別就是氣象描述以及給予氣象描述的編號
  • let weatherImg = checkImg(weatherCode);依據天氣描述的分類放入對應圖示
  • 抓取到所需要的變數資料後,就是將要填入呈現畫面的html標籤以及變數放入
  • after()在該區塊後面在插入想要呈現的其他標籤內容
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
function getWeather(data, locationIndex) {

//console.log(data, locationIndex)
let name = data.records.locations[0].location[locationIndex].locationName;
let weather = data.records.locations[0].location[locationIndex].weatherElement;
//console.log(name);
//console.log(weather);
// 天氣描述
let weatherDescription = weather[6].time[0].elementValue[0].value;
let weatherCode = weather[6].time[0].elementValue[1].value;
let minTemp = weather[8].time[0].elementValue[0].value;
let maxTemp = weather[12].time[0].elementValue[0].value;
let weatherImg = checkImg(weatherCode);
// 今天日期
// let date = getDate().toUTCString();

console.log(weatherCode);

let li = $('<li></li>').appendTo('.cities');
li.addClass("city");
li.html(`
<h2 class="city-name" data-name="${name},">
<span>${name}</span>
<sup>Today</sup>
</h2>
<div class="city-temp">${Math.round(minTemp)}<sup>°C</sup> ~ ${Math.round(maxTemp)}<sup>°C</sup>
</div>
<figure class='weather-icon'>
${weatherImg}
<figcaption>${weatherDescription}</figcaption>
</figure>
`);
$('.cities').after(`<h2 class="sec-h2">未來一週預報</h2>`)
$('.cities').append(li);

};

5.根據weather code分類,分配的圖示(checkImg())

  • 將分類的代碼區分到各個天氣狀況
  • 依分類代碼下判斷,並回傳要放入的圖示
  • weatherTypes.isThunderstorm.includes(weatherData)
    • 利用includes()取得 weatherTypes中各天氣情形陣列中,是否有符合參數的數值
      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
      function checkImg(code) {
      let weatherData = +code;
      const weatherTypes = {
      isThunderstorm: [15, 16, 17, 18, 21, 22, 33, 34, 35, 36, 41],
      isClear: [1],
      isCloudyFog: [25, 26, 27, 28],
      isCloudy: [2, 3, 4, 5, 6, 7],
      isFog: [24],
      isPartiallyClearWithRain: [
      8, 9, 10, 11, 12,
      13, 14, 19, 20, 29, 30,
      31, 32, 38, 39,
      ],
      isSnowing: [23, 37, 42],
      };
      // console.log(data)
      // console.log(weatherTypes.isPartiallyClearWithRain)

      // let s = weatherTypes.isThunderstorm.includes(weatherData);
      // console.log(s);



      if (weatherTypes.isThunderstorm.includes(weatherData)) {
      return `<img class="city-icon" src="./img/thunderstorm.png" alt="weather-img">`;
      } else if (weatherTypes.isClear.includes(weatherData)) {
      return `<img class="city-icon" src="./img/clear.png" alt="weather-img">`;
      } else if (weatherTypes.isCloudyFog.includes(weatherData)) {
      return `<img class="city-icon" src="./img/cloudyfog.png" alt="weather-img">`;
      } else if (weatherTypes.isCloudy.includes(weatherData)) {
      return `<img class="city-icon" src="./img/cloud-and-sun.png" alt="weather-img">`;
      } else if (weatherTypes.isFog.includes(weatherData)) {
      return `<img class="city-icon" src="./img/fog.png" alt="weather-img">`;
      } else if (weatherTypes.isPartiallyClearWithRain.includes(weatherData)) {
      return `<img class="city-icon" src="./img/clearwithrainy.png" alt="weather-img">`;
      } else { return `<img class="city-icon" src="./img/snow.png" alt="weather-img">`; }

      }

      [Day 21 - 即時天氣] 處理天氣圖示以及 useMemo 的使用
      JavaScript Array 陣列操作方法大全 ( 含 ES6 )
      includes(weatherData):JavaScript 陣列處理:找東西 - indexOf、$.inArray 與 filter
      預報因子欄位中文說明表

6.取得未來一週的天氣

  • 此作法與取的當日天氣有些類似,不過因為要取得未來一週6天的資料,所以須以迴圈的方式來取的並填入資料
  • let i = 1; i < 7; i++從1開始,6天
  • timeIndex= 2 * i是為了抓取一週時間所設置的index
  • 日期、星期的取得
    • 以算出tomorrow為目標,並帶入i,就會執行出往後六天的日期
      • 並從上圖,分別擷取出月份、日期、星期的資料
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
42
43
44
45
46
47
48
function weekWeather(data, locationIndex) {
// $('.week-ul').html('');
var weather = data.records.locations[0].location[locationIndex].weatherElement;
for (let i = 1; i < 7; i++) {
let timeIndex = 2 * i;
let weekday = $('<li></li>').appendTo('.week-ul');
weekday.addClass('day').attr('id', `day-${i}`);

let weatherDescription = weather[6].time[timeIndex].elementValue[0].value;
let weatherCode = weather[6].time[timeIndex].elementValue[1].value;
let minTemp = weather[8].time[timeIndex].elementValue[0].value;
let maxTemp = weather[12].time[timeIndex].elementValue[0].value;
let weatherImg = checkImg(weatherCode);
//console.log(weatherDescription);

//////日期、星期的擷取//////
let today = new Date();
let tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + i);
let weekDay = tomorrow.getDay();
let weekMonth = tomorrow.getMonth()
let date = tomorrow.getDate();
const dayNamesEn = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const monthNamesEn = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
]
let inweekDay = dayNamesEn[weekDay]
let inweekMonth = monthNamesEn[weekMonth];
let indate = date;
///////////


weekday.html(`<li class="day">
<h2 class="whichDay" data-name="">
<span>${indate} ${inweekMonth}</span>
<sup>${inweekDay}</sup>
</h2>
<div class="week-temp">${minTemp}<sup>°C</sup> ~ ${maxTemp}<sup>°C</sup>
</div>
<figure class='weather-icon'>
${weatherImg}
<figcaption>${weatherDescription}</figcaption>
</figure>
</li>`)
$('.week-ul').append(weekday);
}
}

抓取明天日期:How to get tomorrow’s date using JavaScript
日期與時間

7.當文件準備好

  • 先執行要串接的資料
  • 於選擇地區時,跳出提醒文字
1
2
3
4
5
6
7
8
9
$(document).ready(function (e) {
getCity();

$('#selector-list').change(function () {
$('.msg').text('別忘了按下SUBMIT');
})


});

8.清除資料

成品

觀察功能需求

  • 在difficuly地方,有設置難易度,並於重新整理後,仍然儲存選擇的難易度
    • 使用到取表格中的值
    • localstorage的方式,儲存設置的難易度
  • 創造出隨機的單字,並呈現於畫面
    • 輸入的值與隨機出現的值要核對
  • 左上方有倒數計時;右上方有分數的計算
    • setInterval設定倒數
  • 設置reload的按鈕
    • 在要加入文字內容的容器中,添加location.reload()

HTML 部分

1.icon來源
font-awesome - cdnjs

  • fa-cog

1.設置難易度選單

  • difficulty為label

  • 下拉選單使用option

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="settings" class="settings">
<form id="settings-form">
<div>
<label for="difficulty">Difficulty</label>
<select id="difficulty">
<option value="easy">Easy</option>
<option value="medium">Medium</option>
<option value="hard">Hard</option>
</select>
</div>
</form>
</div>


JS 部分

1.如何隨機取到已製作好的單字

words[Math.floor(Math.random() * words.length)]

  • $('#word').text(randomItem);讓文字呈現於畫面中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const words = [
'sigh',
'tense',
'airplane',
'ball',
'pies',
'juice',
'warlike',
'bad',
'north',
'dependent',
'steer',
'silver',
'highfalutin',
'superficial',
'quince',
'eight',
'feeble',
'admit',
'drag',
'loving'
];
1
2
3
4
5
6
7
8
9
function getRandomword() {
//https://www.codegrepper.com/code-examples/javascript/get+random+word+from+array+javascript
randomItem = words[Math.floor(Math.random() * words.length)];


$('#word').text(randomItem);
};

getRandomword()

2.單字的核對

  • 要確認抓到的單字是否一樣
  • let text = $('#text').val(); let Item = randomItem;
  • console.log(Item);查看抓取到的單字
  • 設定文字核對一樣,就執行分數加一,以及創造另一組新單字
  • 每次輸入完,並呈現新單字的同時,要清空輸入框的內容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //輸入單字核對
    function checkWord() {
    let text = $('#text').val();
    let Item = randomItem;
    //console.log(Item);
    if (text === Item) {
    getRandomword();
    updateScore();
    $('#text').val("");
    }
    }

3.倒數計時

  • 設置起始時間為10
  • setInterval()「不斷循環」地固定延遲了某段時間之後,才去執行對應的程式碼,使用 clearInterval()来終止
  • 在秒數倒計完之後,終止循環,並呈現結束畫面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Init time
let time = 10;
// 倒數計時
//https://jsfiddle.net/satyasrinivaschekuri/y03m54Le/
function countTime() {
time--;
$('#time').text(`${time}s`)
if (time <= 0) {
clearInterval(downloadTimer);
gameOver();
}
// } else {
// time--;
// $('#time').text(`${time}s`)
// }
}

var downloadTimer = setInterval(countTime, 1000);

4.倒數計時結束,終止畫面

  • 在該div容器中,插入要顯示的標籤與內容
  • 利用display:flex呈現該內容
1
2
3
4
5
6
7
8
9
10
//遊戲結束
//location.reload()
function gameOver() {
$('#end-game-container').html(`<h1>Time ran out</h1>
<p>Your final score is ${score}</p>
<button onclick="location.reload()">Reload</button>
`)
// 顯示指定的flex容器
$("#end-game-container").css('display', 'flex');
}

5.設置難易度

  • 設定在表格變動選項後的綁定事件
  • 抓取難易度選項的值,並以其值作為儲存local storage的內容
  • 接著將儲存的難易度,呈現於畫面$('#difficulty').val()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//如果儲存端不是空值(已有選擇),就以此為主,否則的話就是medium
let difficulty = localStorage.getItem('difficulty') !== null
? localStorage.getItem('difficulty')
: 'medium';


//設置select的選擇
$('form').change(function () {
difficulty = $('#difficulty').val();
localStorage.setItem('difficulty', difficulty);

});
//使畫面的難易度選擇,呈現儲存的選項
$('#difficulty').val(function () {
let a = $('#difficulty').val()
if (localStorage.getItem('difficulty') !== null) {
return a = localStorage.getItem('difficulty');
} else {
return a = 'medium';
}
})

6.調整難易度,每次打對題目可以增加秒數

  • checkWord()函式中,如過答對題目的條件下設置
  • 判斷如果難易度為高、中、低時,可以添加的秒數分別為多少
1
2
3
4
5
6
7
8
9
10
11
//time += 5;
if (difficulty === 'hard') {
time += 2;
} else if (difficulty === 'medium') {
time += 3;
} else {
time += 5;
}

//判斷完之後,隨之繼續倒數時間
countTime();

7.設定結束時,分數(>=5),會出現good job圖示

  • 在css檔案加入show的class,並插入該圖示的顯現方式
  • 於js部分,將判斷式,加入gameOver()a
1
2
3
4
5
6
7
8
//增加判斷,分數達到一標準,會出現圖示
let s = score
console.log(s)
if (s > = 5) {
$("#end-game-container").find('img').addClass('show');

};

成品

觀察功能需求

  • 練習語音辨識的使用: speech recognition API
  • 根據不同情形,有不一樣的回覆
    • 依據猜測數字的情形給予:go higher,go lower

  • 數字猜中後,會出現恭喜答對等敘述,並呈現Play Again的按鈕

js部分

1.設定隨機數字

  • Math.floor(Math.random() * 100) + 1取得1-100
1
2
3
4
5
6
7
8
// 取得隨1-100隨機數字
function theRandomNum() {
return Math.floor(Math.random() * 100) + 1;
//沒有加1,數字會是0-99
}

let randomNum = theRandomNum();
console.log('randomNum:', randomNum);

2.SpeechRecognition

  • 第一步要先將SpeechRecognition設為全域變數
    • 檢查瀏覽器是否有支援不需要 prefix 的 speechRecognition 介面,若沒有則將 webkit 標頭的 webkitSpeechRecognition 指定給該全域變數,
  • 要使用 speechRecognition ,必須透過 SpeechRecognition 建構式建立一個物件實例。
  • recognition.onresult 設定取得語音辨識的結果
  • 執行writeMessage(myWord);checkNumber(myWord);兩個函式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    try {
    var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
    var recognition = new SpeechRecognition();
    //設定辨識語言。
    recognition.lang = 'zh-tw';
    }
    catch (e) {
    console.error(e);

    }



    // 開始語音辨識
    recognition.start();
    //取得語音辨識的結果
    recognition.onresult = function (event) {
    //console.log(event);
    let myWord = event.results[0][0].transcript
    //console.log('You said: ', myWord);
    writeMessage(myWord);
    checkNumber(myWord);
    };

  • 簡易測試,取得得語音辨識 :point_down:
    1
    2
    3
    4
    //取得語音辨識的結果
    recognition.onresult = function (event) {
    console.log('You said: ', event.results[0][0].transcript);
    };

Using the Web Speech API
How to build a simple speech recognition app

3.記錄下使用者所說的話

  • msg的div插入辨識語言的結果文字
    1
    2
    3
    4
    5
    6
    7
    function writeMessage(myWord) {
    $('#msg').html(`
    <div>You said:</div>
    <span class="box">${myWord}</span>

    `)
    }

    4.核對隨機數字與猜的數字是否一致

  • 設定當辨識到的不是數字:This is not a valid number
  • 當範圍不在1-100之間:Number must be between 1 and 100
  • 猜中數字的結果:Congrats! You have guessed the number!
  • 數字猜測GO LOWER,GO HIGHER
    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
    function checkNumber(myWord) {
    let number = +myWord; //轉為數字
    if (Number.isNaN(number)) {
    $('#msg').append(`<div>This is not a valid number</div>`)
    return
    }
    //設定語句
    if (number > 100 || number < 1) {
    $('#msg').append(`<div>Number must be between 1 and 100</div>`);
    return;
    }

    if (number === randomNum) {
    $('body').html(`
    <h2>Congrats! You have guessed the number! <br><br>
    It was ${number}</h2>
    <button class="play-again" id="play-again" > Play Again </button>
    `);
    } else if (number > randomNum) {
    $('#msg').append(`<div>GO LOWER</div>`);
    } else {
    $('#msg').append(`<div>GO HIGHER</div>`)
    }

    }
  • 透過 Number.isNaN() 語法可以判斷,若回傳 true,則表示值為 NaN,否則為 false

Number.isNaN()

5.使猜數字可以持續猜,直到答對為止

  • recognition.onend使用於控制語音識別的服務,並應用在結束語音識別時的情境。
  • recognition.start();每次數字猜完後,都會重新開始語音識別,直到猜中。
  • 在數字猜中後,會出現play again按鈕,所以要設定此按鈕,按下後能夠重新開始遊戲 =>window.location.reload()
1
2
3
4
5
6
7
8
9
10
11
12
//https://stackoverflow.com/questions/51080738/how-to-make-speech-recognition-continous-for-a-fix-time-period
recognition.onend = function () {
recognition.start();
//點擊按鈕,重啟遊戲畫面
$('body').find('#play-again').click(

function () {
window.location.reload();
}

);
}

實戰智慧插座 14 - 我說開燈就開燈 ( 語音辨識 )


其他參考資料:
Converting from Speech to Text with JavaScript
JavaScript Speech Recognition
Day21 – Speech Recognition (lukechu)
Day 21 - Speech Detection (Arel)
DAY20 : Speech Detection (BROWN)

成品

觀察功能需求

  • 計算天、小時、分鐘、秒數的倒數
  • 最下面顯示倒數年份
  • page load的圖示(會旋轉1秒鐘)之後顯示倒數

CSS部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*增加讓螢幕寬度變化時,字的大小隨之改變
如果螢幕寬度為 500px 以下*/
@media (max-width: 500px) {
h1 {
font-size: 45px;
}

.time {
margin: 5px;
}

.time h2 {
font-size: 12px;
margin: 0;
}

.time small {
font-size: 10px;
}
}

Day22:小事之 Media Query CSS設定響應式(RWD)有幾種方法

JS部分

1. 宣告最終日期(新的一年1/1的日期)

  • 要先宣告取得目前的年份
  • 再利用此變數帶入,要倒數的最終日期
1
2
3
//getFullYear(),用來取得日期物件當中本地時間的年份
const currentYear = new Date().getFullYear();
const newYearTime = new Date(`January 01 ${currentYear + 1} 00:00:00`);

時間日期
JavaScript Date 時間和日期
getFullYear() Method

2. 倒數計時的日、時、分、秒

  • currentTime 取得目前時間

  • diff = newYearTime - currentTime;:計算出目前與未來最終日期的時間差異(此相減得出的是毫秒)

  • 計算天數、小時、秒數

    • const d1天等於24小時,1小時等於60分鐘,1分鐘等於60秒,1秒等於1000毫秒。所以,msec / 1000 / 60 / 60 / 24 保留整數就是天數。
    • h = Math.floor(diff / 1000 / 60 / 60) % 24:計算小時,並以除以24之後的餘數,來得到剩餘的小時。
    • 分鐘、以及秒數也是用同樣方式計算取得。
  • 將取得的日期帶入畫面

    • 使用.html()$('#days').html(d);
    • 在小時、分鐘、秒,是以兩位數呈現,需要當數字少於10,要呈現01,02…,所以使用三元運算子$('#hours').html(h < 10 ? '0' + h : h);,當數字小於10,要0+h;反之就只要呈現h。
  • 最後要設定setInterval(updateCountdown, 1000); ,使其每秒倒數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function updateCountdown() {
const currentTime = new Date();
//console.log(currentTime);
const diff = newYearTime - currentTime;
//diff計算出來是毫秒
//days
const d = Math.floor(diff / 1000 / 60 / 60 / 24);
//取得除以24之後的餘數
const h = Math.floor(diff / 1000 / 60 / 60) % 24;
const m = Math.floor(diff / 1000 / 60) % 60;
const s = Math.floor(diff / 1000) % 60;
$('#days').html(d);
//當數字少於10,要呈現01,02...
$('#hours').html(h < 10 ? '0' + h : h);
$('#minutes').html(m < 10 ? '0' + m : m);
$('#seconds').html(s < 10 ? '0' + s : s);

}

//設定每一秒都會循環倒數
setInterval(updateCountdown, 1000);

參考資料:
【JS】javascript時間Date()介紹與補零應用的技巧!!!
js和vue實現時分秒倒計時的方法
三元運算子:
Day19【ES6 小筆記】三元運算子-以哥哥的擇偶條件為例

3.載入圖示

  • 設定在載入之後,要隔多久執行功能
  • 在此設定載入1秒之後,要移除loading畫面,呈現出倒數的時間
1
2
3
4
5
setTimeout(function () {
$('#loading').remove();
//要在css檔案的countdown,先設置display:none
$('#countdown').css('display', 'flex');
}, 1000);

4.放入要倒數的年份

1
$('#year').html(currentYear + 1)

網頁練習 -倒數計時器實作(上)
5 Best Countdown Timer JavaScript Libraries (2020 Update)
網頁練習 -倒數計時器實作(下)

成品

觀察功能需求

1.隨著頁面的往下捲動,會載入新的資料

  • 一開始頁面載入,會呈現5筆資料,捲動往下,會再載入5筆
  • 使用{JSON} Placeholder的資料來串接(運用ajax)
  • 設定捲動位置,並於該位置時,會呈現載入的圖示
  • setTimeout來呈現載入的時間與畫面

2.網頁的各資料是有順序,資料左上會有編碼

  • 利用資料本身的id作為序號
  • 於js時,帶入該變數取值

css部分

1.製作左上方,數字

  • 利用position: absolute;定位位置,並利用並利用topleft調整位置
  • 使用display: flex;調整數字的位置,align-items: center; justify-content: center;使水平、垂直置中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    .post .number {
    position: absolute;
    top: -15px;
    left: -15px;
    font-size: 15px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #fff;
    color: #296ca8;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 7px 10px;
    }

2.載入畫面的原點圖示

  • 先製作load的基本設定,如flex的排列、位置固定於下方,以及透明度的顯示
  • 針對原點訂定背景、原點大小。新增動畫效果,animation: bounce 0.5s ease-in infinite;
  • 動畫名稱@keyframes bounce,設定在某時間點,translateY,的位置。
  • 為了讓圓點的跳動時間不同,針對2、3的原點,設定延遲時間animation-delay: 0.2s;
    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
    42
    43
    44
    45
    .loader {
    /* opacity: 0; */
    display: flex;
    position: fixed; /*位置固定於最下面*/
    bottom: 50px;
    transition: opacity 0.3s ease-in;
    }

    .loader.show {
    opacity: 1;
    }

    /*製作圓點*/


    .circle {
    background-color: #fff;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    margin: 5px;
    animation: bounce 0.5s ease-in infinite;
    }


    .circle:nth-of-type(2) {
    animation-delay: 0.1s;
    }

    .circle:nth-of-type(3) {
    animation-delay: 0.2s;
    }

    @keyframes bounce {
    0%,
    100% {
    transform: translateY(0);
    }

    50% {
    transform: translateY(-10px);
    }
    }


js部分

1.jsonplaceholder:

2.使用ajax串接API

  • Async/Await 非同步流程控制-筆記
  • 參考Using async await with jQuery’s $.ajaxHow to await the ajax request?,使用jQuery’s$.ajax執行async
  • 在try裡面,放入一般$.ajax抓取API資料的方式,如url、type、dataType
  • 首先,在連結的部分,我們希望每次抓取5筆資料,呈現1頁,所以在外面設置let limit = 5;let page = 1於url中加入該變數
  • 在抓取資料成功時,放入function,並用$.each來執行
    • $.each先傳入該資料(data)陣列,其function (index, value)放入索引以及值的內容。
    • 宣告postEl變數,創造名為postdiv=> $('\<div />')
    • 加入postclass
    • 加入html
    • 最後將此變數,放置呈現文章的容器postEl.appendTo('#posts-container');
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
let limit = 5
let page = 1

async function doAjax() {
let result;

try {
result = await $.ajax({
url: `https://jsonplaceholder.typicode.com/posts?_limit=${limit}&_page=${page}`,
type: 'get',
dataType: 'json',
success: function (data) {

$.each(data, function (index, value) {
const postEl = $('<div />').addClass('post').html(`<div class="number">${value.id}</div> <div class="post-info"><h2 class="post-title">${value.title}</h2><p class="post-body">${value.body}</p>
</div>`)
//console.log(postEl)
postEl.appendTo('#posts-container');
//$('#posts-container').append(postEl)

});


}
});

return result;
} catch (error) {
console.error(error);
}

}

doAjax();

查看是否有成功取得資料:console.log(data);

使用jQuery創造div

https://stackoverflow.com/questions/10402567/jquery-div-vs-div

each()

jQuery.each()

3.Infinite Scroll無限捲動(瀑布流)

  • scrollTop網頁右邊的卷軸到最上端網頁的距離有多少
  • scrollHeight 取得整個沒被擋住的高、clientHeight 取得元素的高度(含padding 不含 border)
  • 完整內容高度 (scrollHeight) = 內容頂端與捲軸頂端的距離 (scrollTop) + 捲軸本身高度 (clientHeight) + 內容底端與捲軸底端的距離。

例子:

1
2
3
4
5
6
7
8
9
10
11
$(function () {
$(window).scroll(function () {
var scrollVal = $(this).scrollTop();
$("span.qScrollTop").text(scrollVal);
});
});

if(scrollVal > 500){
/* 如果滾動的物件捲動 > 500 則觸發指定的動作。*/
}

注意:window本身沒有scrollTop/Left這個屬性,所以window.scrollTop是undefined的

所以可能要從body、document來取得

本段落語法:

  • 上述說明各視窗高度得解釋,在此if判斷式中scrollTop + clientHeight >= scrollHeight - 5來計算,當捲軸捲到該位置時,要呈現載入畫面
  • 接著根據載入畫面函式,來放入載入以及呈現接續畫面得設定
    • 使用setTimeout(),在1秒後消除載入圖示,接著在300毫秒後,馬上換頁執行載入新資料
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
$(window).scroll(function () {
var scrollTop = $(this).scrollTop();
var scrollHeight = $('body').prop("scrollHeight");
//一樣 var scrollHeight2 = document.documentElement.scrollHeight;
var clientHeight = document.documentElement.clientHeight;
//https://stackoverflow.com/questions/10423759/plain-javascript-to-jquery-clientheight

// console.log('scrollTop:', scrollTop);
// console.log('scrollHeight:', scrollHeight);
// console.log('clientHeight:', clientHeight);


if (scrollTop + clientHeight >= scrollHeight - 5) {
//console.log('show up 123')
showLoading();
}
})


//顯示載入圖示,並取得更多串接資料
function showLoading() {
$('.loader').addClass('show');


setTimeout(function () {

$('.loader').removeClass('show');
setTimeout(function () {
page++;
doAjax();
}, 300);

}, 1000); //1秒之後消失

}

參考資料:
[筆記] 計算網頁底部位置,當網頁達到底部時才產生效果─jQuery
一次搞懂 clientHeight/scrollHeight/scrollTop的區別
談談 JavaScript 的 setTimeout 與 setInterval

4.篩選輸入框資料

  • 綁定事件為.keyup指放開鍵盤的那個剎那,觸發該事件
  • 執行的函式內容為:
    • var text取得輸入值並轉為小寫
    • 利用迴圈,去搜尋關鍵字,判斷,值轉為小寫文字的內容是否符合條件
    • -1 :意指條件不符合
    • indexOf() 方法用來判斷字串字串變數中是否包含某字串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//輸入框搜尋//https://makitweb.com/jquery-search-text-in-the-element-with-contains-selector/  (=>Loop all .content )
$('#filter').keyup(function () {

// Search text
var text = $('#filter').val().toLowerCase();

// Hide all content class element
$('.post').hide();

// Search
$('.post').each(function () {

if ($(this).text().toLowerCase().indexOf("" + text + "") != -1) {
$(this).closest('.post').show();
}
});

參考資料:
比較 keydown, keypress, keyup 的差異
jQuery – Search text in the Element with :contains() Selector
JavaScript String indexOf()


補充參考:
JS20min Day — 20 AJAX 非同步處理 (Asynchronous JavaScript and XML)
回呼函式 Callbacks、Promises 物件、Async/Await 非同步流程控制 - 彭彭直播 at 2019/04/07

成品

需求觀察

  • 使用fetch來串接API
  • 透過左側按鈕,使用陣列的方法來實現不同事件
    • 運用forEach()來將陣列資料呈現到畫面
    • 運用map()計算金額的倍數
    • filter()篩選出符合條件的資料
    • sort()用來排序資料
    • reduce()計算陣列中資料的累加結果

使用fetch來串接資料

  • 實作專案的資料來源https://randomuser.me/api

1.原本使用fetch

  • 送出 Request,如果得到回應就會回傳帶有 Response 的 Promise 物件,使用 then 將回傳值傳遞下去。
    1
    2
    3
    4
    function getRandomUser() {
    fetch('https://randomuser.me/api').then(response => response.json()).then(data => data)
    }

    JavaScript Fetch API 使用教學

2.Fetch 搭配 async、await

  • 使用async很容易:在定義function時,開頭加上 async
  • 用fetch取得資料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  async function getRandomUser() {
const res = await fetch('https://randomuser.me/api'); //執行完後發出promise
const data = await res.json();
//查看取得的資料,並從中選擇要用的部分
//console.log(data);
const user = data.results[0];

//創造新的使用者,並建立新的物件
const newUser = {
name: `${user.name.first} ${user.name.last}`,
money: Math.floor(Math.random() * 1000000)
};
console.log(newUser);
addData(newUser);
}

3.函式中的撰寫

  • 查看取得的data
  • 變數宣告要取用資料的哪個部分,並用以建立新物件的內容
    • const user = data.results[0];
    • user.name.first

將物件傳入陣列

  • data 為前面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    //將新建立的物件(newUser)放入陣列中
    function addData(newObj) {
    //這裡的data是在最前面所宣告的 初始陣列
    data.push(newObj);

    //不放入參數,使用預設data
    updateDOM();


    }

    將傳入新物件的陣列,顯示於畫面

  • 使用forEach()來呈現畫面的資料

    • 傳入參數為item,為新創的物件資料,內有:name,money
    • 創造新的div並增加class給他
    • innerHTML插入內容
    • 用 appendChild() 把上面新增的內容放入,新增的內容會依序排列在後面,不會被洗掉
1
2
3
4
5
6
7
8
9
10
11
12
13
//將新創立的物件輸出呈現到畫面上(dom)
function updateDOM(provideData = data) {
console.log(provideData);
//清除main區域的div
main.innerHTML = `<h2><strong>Person</strong> Wealth</h2>`;
provideData.forEach(item => {
const element = document.createElement('div');
//在新div增加class
element.classList.add('person');
element.innerHTML = `<strong>${item.name}</strong> ${item.money}`;
main.appendChild(element);
})
}

注意:如果本來 HTML 某元素有既有的內容,但是又用 innerHTML 在同樣的元素上加上內容的時候,innerHTML 會把原本寫的東西覆蓋掉。

  • 注意參數的回傳
    1
    2
    3
    4
    function updateDOM(provideData) {
    console.log(provideData);
    }

    1
    2
    3
    function updateDOM(provideData = data) {
    console.log(provideData);
    }

JavaScript 初心者筆記: DOM - 如何用 JS 新增 HTML 內容

數字轉金錢格式

1
2
3
4
5
//將回傳的隨機數字,轉換為金錢格式 -https://stackoverflow.com/questions/149055/how-to-format-numbers-as-currency-strings
function formatMoney(Money) {
return '$' + Money.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');;
}

  • formatMoney()要放入剛剛forEach所傳入的金額,從這邊去調整數字的格式
    1
    element.innerHTML = `<strong>${item.name}</strong>  ${formatMoney(item.money)}`;

增加人員事件

1
2
//設置點擊事件,按下add user,增加人
addUserBtn.addEventListener('click', getRandomUser);

運用map()來,建立 double money事件

  • loop through array 就像forEach,但不同的是他會回傳東西
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //double money 
    function doubleMoney() {
    data = data.map(item => {
    return { ...item }

    });
    console.log(data);

    }
1
2
3
4
5
6
7
8
9
10
//double money 
function doubleMoney() {
data = data.map(item => {
return { ...item, money: item.money * 2 }

});
console.log(data);

}

呈現於畫面

1
2
3
4
5
6
7
8
9
10
//double money 
function doubleMoney() {
data = data.map(item => {
return { ...item, money: item.money * 2 }

});
console.log(data);
updateDOM();
}

forEach(): 针对每一个元素执行提供的函数(executes a provided function once for each array element)。
map(): 创建一个新的数组,其中每一个元素由调用数组中的每一个元素执行提供的函数得来(creates a new array with the results of calling a provided function on every element in the calling array)。
JavaScript中Map和ForEach的区别

運用sort來建立排名

1
2
3
4
5
6
7
8
9

//sortEvent
function sortEvent() {
data = data.sort((a, b) => {
return b.money - a.money;
});
//console.log(data);
updateDOM();
}

sort()

sort依據字串的 Unicode 編碼進行排序,會改變原本的陣列。

  • 排序進行方式:sort()會將所有元素轉成字串後,且以第一個字元為對象,再進行排序。
  • 比較好的方式:一般會建議,還是以函式傳入參數來當排序條件會比較穩定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const arr = [5, 9, 1, 3, 2, 6];
// 以匿名函式回參數做「升序」排序
arr.sort(function(a, b) {
return a - b; // a - b > 0
});
// [1, 2, 3, 5, 6, 9]



// 如果要反過來做「降序」排序
arr.sort(function(a, b) {
return b - a;
});
// [9, 6, 5, 3, 2, 1]

參考資料:JS 將陣列 Array 重新排列的 sort()淺談 JS sort() 到背後排序方法

filter篩選百萬以上的人員

1
2
3
4
5
6
7
8
function filterMillionaires() {
data = data.filter((item) => {
return item.money > 1000000;
});

updateDOM();

}

PS:filter()的使用是回傳新陣列,原陣列不改變

reduce

拆解內部參數
accumulator
用來累積回呼函式回傳值的累加器(accumulator)或 initialValue(若有提供的話,詳如下敘)。累加器是上一次呼叫後,所回傳的累加數值。
currentValue
原陣列目前所迭代處理中的元素。

  • console.log(accumulator);

  • console.log(currentValue);

  • console.log(currentValue.money);

MDN
JavaScript reduce 在做什麼?

理解完reduce()所回傳的參數資料後…

  • 讓金額加總計算accumulator += currentValue.money
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//calculateMoney
function calculateMoney() {
const reduceArr = data.reduce((accumulator, currentValue) => {
//console.log(accumulator);
// console.log(currentValue.money);
return accumulator += currentValue.money;
}, 0);


const totalEl = document.createElement('div');
totalEl.innerHTML = `<h3>Total Wealth: <strong> ${formatMoney(reduceArr)} </strong> </h3> `;
main.appendChild(totalEl);
}

成品

觀察功能需求

  • Drag & Drop API

    • 使元素可以拖放,在 HTML5,只要為它加上 draggable=”true” 屬性,它就能「被拖曳」了
  • 拖曳該項目時,會使下方的格子顏色變成灰色

  • 拖曳項目到目標位置,會使了者交換
  • 排序最後會有一個檢查順序正確與否的按鈕

JS部分

1.排行榜的設置

  • 設定熱門旅遊縣市排行榜的陣列
  • 並宣告一個空陣列,以利後面使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const bestPlace = [
'台南',
'台中',
'高雄',
'台東',
'屏東',
'新北',
'宜蘭',
'花蓮',
'澎湖',
'桃園'
];


const listItems = [];

2.創建排行列表,並將其呈現於頁面上

  • 宣告新的變數,並利用Fisher-Yates,創造隨機的效果
    • 注意新變數,是帶入展開運算元(把陣列中的元素取出)
  • 利用each跑陣列,創造變數填入<li>,並於其中填入html標籤,使畫面呈現列表。
  • jquery稱增加屬性listItem.setAttribute('data-index', index);,.attr()

  • listItems.push(listItem);將列表項目,放入空陣列中
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
//Fisher-Yates洗牌演算法
function fisherYatesShuffle(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1)); //random index
[arr[i], arr[j]] = [arr[j], arr[i]]; // swap
}
}
/製作實際可以看到的list
function creatList() {
let placeArr = [...bestPlace];
fisherYatesShuffle(placeArr);
$.each(placeArr, function (index, place) {
const listItem = $('<li></li>').appendTo('#draggable-list');
// console.log(listItem);
listItem.attr("data-index", index);
//listItem.addClass('wrong')
listItem.html(`<span class="number">${index + 1}</span>
<div class="draggable" draggable="true">
<p class="place-name">${place}</p>
<i class="fas fa-grip-lines"></i>
</div>`)


listItems.push(listItem);
//insert into dom
$('#draggable-list').append(listItem);
});

addEventListeners()
}

參考資料:
push:
JS 從陣列 Array 尾端新增元素的 push()
JavaScript Array 陣列操作方法大全 ( 含 ES6 )

3.拖曳的監聽事件

  • 拖曳開始的監聽,元素是抓取draggables,後續的drop、dragenter、dragover等事件,元素是.draggable-list li
  • 針對要被拖曳的元素(dragSource)監聽事件
  • 利用forEach方式,允許多個可拖曳的物件
  • 抓取的元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//監聽事件
function addEventListeners() {
const draggables = document.querySelectorAll('.draggable');
const dragListItems = document.querySelectorAll('.draggable-list li');

//Dragable dot 增加監聽事件

draggables.forEach(draggable => {
draggable.addEventListener('dragstart', dragStart);
});
dragListItems.forEach(item => {
item.addEventListener('dragover', dragOver);
item.addEventListener('drop', dragDrop);
item.addEventListener('dragenter', dragEnter);
item.addEventListener('dragleave', dragLeave);
});

}

[筆記] 製作可拖曳的元素(HTML5 Drag and Drop API)

4.針對監聽事件,設置各執行的函式

  • 為了能夠追蹤抓取項目,所以一開始先宣告一個索引變數。
  • dragStart():宣告dragStartIndex變數為抓取到的li->data-index

  • 有拖曳的開始,就會有結束,也就是放置(dragDrop()):dragEndIndex同樣也是以索引來作為依據,並在設置一個交換的函式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//要能夠持續追蹤每個項目的index,所以要有個初始變數
let dragStartIndex;
//拖拉的各函式

function dragStart() {
//console.log('Event: ', 'dragstart');
dragStartIndex = +$(this).closest('li').attr('data-index');

console.log(dragStartIndex);
};
function dragOver(e) {
// console.log('Event: ', 'dragover');
e.preventDefault();
}
//設定開始的index,以及結束的index是為了交換
function dragDrop() {
// console.log('Event: ', 'dragdrop');
const dragEndIndex = +$(this).attr('data-index');
swapItems(dragStartIndex, dragEndIndex);
$(this).removeClass('over');
}

5.交換索引,使物件交換位置swapItems

  • 透過抓取到的開始與結束的索引,來填入要交換的變數
1
2
3
4
5
6
7
8
9
10
function swapItems(fromIndex, toIndex) {
//要設定交換之前,要先到dragover去設定e.preventDefault();(因為dragover會擋到交換的執行)

const itemOne = listItems[fromIndex].find('.draggable');
const itemTwo = listItems[toIndex].find('.draggable');

//console.log(itemOne, itemTwo)
listItems[fromIndex].append(itemTwo);
listItems[toIndex].append(itemOne);
}

6.延續拖曳監聽事件,所要執行的其他函式

  • dragEnter()拖曳元素進入要放置的目標位置,所觸發的事件。在此是設定要放入目標位置時,增加一個class的效果

  • 進入目標位置,其相互搭配的事件就是離開dragLeave(),當拖曳元素離開放置位置時,所發生的事件。在此為設定,去除於原本class的效果。

1
2
3
4
5
6
7
8
9
function dragEnter() {
// console.log('Event: ', 'dragenter');
$(this).addClass('over');
}
function dragLeave() {
// console.log('Event: ', 'dragleave');
$(this).removeClass('over');
}

7.核對順序

  • 抓取listItems,來跑forEach,其要參照的參數是,項目、索引。
  • 宣告placeName為參數listItem,draggable的文字字串。
    • trim()用來去除字串前後的空白
  • 判斷句placeName !== bestPlace[index]
  • console.log(bestPlace[index])會印出,正確的順序。

  • 核對結果:就是分別加入wrong'right'
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //核對排名順序
    function checkOrder() {
    listItems.forEach((listItem, index) => {
    const placeName = listItem.find('.draggable').text().trim();

    if (placeName !== bestPlace[index]) {
    listItem.addClass('wrong');
    } else {
    listItem.removeClass('wrong');
    listItem.addClass('right');
    }
    });
    }

以下為補充資料:

拖曳補充資料:

Top 5: Best Sortable & Draggable List JavaScript and jQuery Plugins
實用網頁工具庫 - jQuery UI (上) 元件篇

使陣列隨機排序

  • 使用map將陣列轉換為新陣列,創造vale、以及加上sort排序值(利用Math.random()來產生亂數)
  • 使用sort隨機排序,剛剛的sort值是隨機產生,再一次使用sort,來使排的順序也隨機。
  • 最後,因為排序後陣列中的物件是{ value: , sort: },而我們要讓畫面只顯示value:,所以再使用map將新陣列轉回只有該資料的陣列。
    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
    function createList() {
    [...richestPeople]
    .map(a => ({ value: a, sort: Math.random() }))
    .sort((a, b) => a.sort - b.sort)
    .map(a => a.value)
    .forEach((person, index) => {
    const listItem = document.createElement('li');

    listItem.setAttribute('data-index', index);

    listItem.innerHTML = `
    <span class="number">${index + 1}</span>
    <div class="draggable" draggable="true">
    <p class="person-name">${person}</p>
    <i class="fas fa-grip-lines"></i>
    </div>
    `;

    listItems.push(listItem);

    draggable_list.appendChild(listItem);
    });

    addEventListeners();
    }
    [筆記] 如何正確實作 JavaScript Array Random Shuffle 亂數排序演算法
    在 JavaScript 中對一個陣列進行隨機化或洗牌

jquery ui

1
2
3
4
5
6
7
8
9
10
11
12
//https://stackoverflow.com/questions/6426795/what-is-disableselection-used-for-in-jquery-ui
function dragdrop() {
$("#draggable-list").sortable({
//connectWith: "ul",
delay: 300,
distance: 15,
//number: "span:not(.number)",
iframeFix: true,

}).disableSelection();

}

顏色

0-255
漸層顏色
colors
Why is my JQuery selector returning a n.fn.init[0], and what is it?

觀察須製作的功能

成品

  1. 播放器API的樣式

<audio>: The Embed Audio element

  1. 播放時,左方的圓型圖示會旋轉。
  • 先於css檔案製作符合動畫的class
  • 在js時,依照符合的情況加入該class

  1. 播放時,上方會跳出播放歌曲的名稱。
  • 先於css檔案製作符合動畫的class,設定opacity1或0,translateY位置的移動
  • 在js時,依照符合的情況加入該class

  1. 左右按鍵可以更換曲目,同時圖片會更換
  • 設定陣列,使歌曲與圖片相符
  • 並讓前後的跳轉鍵,依照順序或條件來變化


html部分

跳轉按鍵

  • 播放鍵、前/後跳轉的按鈕
1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="nav">
<button id="prev" class="action-btn">
<i class="fas fa-backward"></i>
</button>

<button id="play" class="action-btn action-btn-big">
<i class="fas fa-play"></i>
</button>

<button id="next" class="action-btn">
<i class="fas fa-forward"></i>
</button>
</div>

css部分

背景顏色

linear-gradient()

1
2
3
4
/* 傾斜度\顏色、從哪個位置開始 */
background-image:linear-gradient(0deg
,rgb(247, 247, 247,1) 23.8%,
rgba(252,221,221,1) 92% );

圖片圓形以及旋轉

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
.img-container{
position: relative;
width: 110px;
}
/*圖片圓形*/
.img-container img{
border-radius: 50%;
object-fit: cover;
/*填滿元素的寬度及高度(維持原比例),通常會剪掉部分的物件*/
height: 110px;
width: inherit; /*繼承自父層的寬度屬性值。*/
position:absolute;
bottom: 0;
left: 0;

animation: rotate 3s linear infinite;
animation-play-state: paused;
}

/* 播放class的製作 */
.music-container.play .img-container img{
animation-play-state:running;

}
@keyframes rotate{
from{
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

  • 在圖片中間加入圓形點,以看起來像光碟樣式
1
2
3
4
5
6
7
8
9
10
11
.img-container::after{
content: '';
border-radius: 50%;
background-color: #fff;
position: absolute;
left: 50%;
bottom: 100%;
width: 25px;
height: 25px;
transform: translate(-50%,50%);
}

播放進度條的製作

  1. 標題
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    .music-info{
    background-color: rgba(255,255,255,.5);
    border-radius:15px 15px 0 0 ;
    position: absolute;
    top: 0;
    left: 20px;
    opacity: 0;
    transform: translateY(0%);
    transition: transform 0.3s ease-in,opacity 0.3s ease-in;
    z-index: 0;
    }

    .music-container.play .music-info{
    opacity: 1;
    transform: translateY(-100%);
    }
  • 將曲目標題設置背景、圓角
  • 利用 transfor、opacity來呈現位置的變化

  • 修改標題位置
1
2
3
/*放入.music-info之中*/
width: calc(100% - 40px);
padding: 10px 10px 10px 150px;

calc()

calc() 的運算式一樣是按照先乘除後加減進行計算
width: calc(100% - 40px);
/* 意思是 100% 的寬度 - 去 40px */
CSS: calc() 數值運算

1
2
3
4
.music-info h4{
margin: 0;
}

2.進度條(progress)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.progress-container{
background-color: #fff;
border-radius: 5px;
height: 4px;
width: 100%;
margin: 10px 0;
cursor: pointer;
}

.progress{
background-color: #fe8daa;
border-radius: 5px;
height: 4px;
width: 0%;
transition: width .1s linear ;
}

js部分

1.音樂播放鍵(播放與停止)

  • 分別有兩個函式:playMusic()pauseMusic()
  • 在css設定.play的css,並於js綁定事件,加入css的效果(使圖片如同轉盤,會旋轉)
  • 在icon的部分:在播放時:為暫停鍵圖示;在暫停時:為播放鍵的圖示
  • 透過play()、pause(),讓音源播放與暫停

play()pause()

因為play()、pause()是DOM元素,不是jQuery的function,而要用jQuery取得DOM元素
$('#audio').get(0).play() =>$('#audio')[0].play()
$('#audio').get(0).pause() =>$('#audio')[0].pause()

參考資料:
How do I pull a native DOM element from a jQuery object?
Play/pause HTML 5 video using JQuery

1
2
3
4
5
6
7
8
9
//播放音樂
function playMusic() {

$('.music-container').addClass('play');
$('#play').find('i').removeClass('fa-play');
$('#play').find('i').addClass('fa-pause');
$('#audio').get(0).play();

};
1
2
3
4
5
6
7
//停止音樂
function pauseMusic() {
$('.music-container').removeClass('play');
$('#play').find('i').addClass('fa-play');
$('#play').find('i').removeClass('fa-pause');
$('#audio').get(0).pause();
}
  • 綁定事件:

    如果音樂是播放狀態的話,就執行pauseMusic(),反之,音樂不處於波方時,執行playMusic()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $('#play').click(function (e) {
    var hasPlay = $('#music-container').hasClass('play');
    //var a = $('#audio').paused;
    if (hasPlay) {
    pauseMusic();
    } else {
    playMusic();
    }
    });

2.載入音樂

  • 將歌曲名稱建立陣列,並將此回傳到建立的函式中
  • 設定預設的索引let songIndex = 1
  • 使用 attr( 屬性名, 屬性值 ),獲取屬性的值,在這部分,透過歌曲名稱來取得歌曲與照片
1
2
3
4
5
6
7
8
9
10
11
12
13
var songs_name = ['hey', 'summer', 'ukulele'];
let songIndex = 1;

function loadSongs(song) {
//console.log(song);
$('#title').text(song);

$('#audio').attr('src', `music/${song}.mp3`);
$('#cover').attr('src', `img/${song}.jpg`);
}

// Initially load song details into DOM
loadSongs(songs_name[songIndex]);

綁定於下一首、前一首的圖示被點擊時

1
2
3
$('#next').click(nextSong)
$('#prev').click(function () { prevSong();
});
  • 查看函式回傳

    1
    loadSongs(songs_name);

  • 要抓取到該索引

    1
    loadSongs(songs_name[songIndex]);


    此段落執行時,出現問題:

  • 搜尋到的處理方式:在html的head
    I’m getting favicon.ico error

參考資料:
jquery prop和attr的區別
jQuery .attr() vs .prop()

3.左右鍵的跳轉

  • 下一首歌曲,與跳至前一首歌曲,兩個函式的撰寫有異曲同工之妙,只有在設條條件上的差異。
  • nextSong():設定條件為歌曲索引songIndex比 歌曲陣列長度-1songs_name.length - 1還要小,我們就要將索引+1。
    • 例如這裏預設的索引為1,所以在點擊當下,判斷<songs_name.length - 1,所以需加1,索引變成2,跳至下一首 ‘ukulele’
  • prevSong():設定條件為歌曲索引songIndex比0大,就要索引-1,跳轉至上一首歌曲。若沒符合的話,索引就等於songs_name.length - 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//歌曲的索引,如果比歌曲總數-1還小,就將索引加1,跳轉下一首
function nextSong() {

if (songIndex < songs_name.length - 1) { songIndex += 1; } else {
songIndex = 0
};
loadSongs(songs_name[songIndex]);
playMusic();
}


//跳轉前一首


function prevSong() {
//如果索引沒有大於0。就會播索引[2]
if (songIndex > 0) { songIndex -= 1; } else {
songIndex = songs_name.length - 1;
};
loadSongs(songs_name[songIndex]);
playMusic();
}

參考資料:
Create a Music Player using JavaScript

4.點擊進度條,可以跳轉音樂位置

(1).顯示粉色進度條

  • duration:音源的時間(一樣是使用get方式取得)
  • currentTime:音樂播放的當前位置(以秒計)
  • progressBar:進度條
  • 計算百分比:(currentTime / duration) * 100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//顯示進度條
//為何取[0]
function handleProgress() {
var duration = $('#audio').get(0).duration;
//console.log(duration)
var currentTime = $('#audio')[0].currentTime;
//console.log(currentTime)
const progressBar = $('#progress');
const progressPercent = (currentTime / duration) * 100;

//currentTime目前播放時間去與影片長度duration轉換成百分比,即可得到目前播放時間的百分比

progressBar.css('width', `${progressPercent}%`);
};

(2).點擊進度條,會跳轉到該位置播放

  • width:獲取該進度條容器的寬度
  • 抓取點擊位置:
    • e.pageX:取得滑鼠在頁面裡的位置
    • elm.offset().left:絕對座標X軸
  • 將(點擊位置除以進度條的總長)乘以 音樂的時間長度,就可以取得點擊的時間位置theTime = ((xPos / width))* duration;
  • 最後現在時間設為,計算好的點擊時間
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//點擊進度條

//https://ithelp.ithome.com.tw/articles/10194871
//https://tools.wingzero.tw/article/sn/102


$('#progress-container').click(function (e) {
const width = $('#progress-container').width(); //216.25
//console.log(width);
//取得點擊位置
var elm = $(this);
var xPos = e.pageX - elm.offset().left;

//console.log(xPos);

var duration = $('#audio')[0].duration;
var theTime = ((xPos / width))
* duration;
//console.log(theTime);
$('#audio')[0].currentTime = theTime;
});

執行過程,利用console來查看

1
2
3
console.log(e.pageX);
console.log(elm.offset().left);
console.log(xPos)

參考資料:
取得滑鼠位置、元素位置與區塊內的相對位置

5.歌曲播完後,換下一首

1
$('#audio').on('ended',nextSong);

參考資料:
audio auto play next song when previous is finished

6.增加隨機的變換背景色彩

  • 色彩為0-256;而因為想取的較亮的色系,所以取64-256
    • Math.random() 會回傳一個偽隨機小數 (pseudo-random) 介於0到1之間(包含 0,不包含1)
    • Math.floor()會將所有的小數無條件捨去到比自身小的最大整數
    • Math.floor(Math.random() * 255) + 64就會取得64 to 256
  • 宣告變數bgColor為rdba(red,green,blue,a)
  • 最後將此函式,放入loadSongs()函式當中,每次播放、跳轉歌曲,同時背景顏色也會改變
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function random_bg_color() {
// Get a random number between 64 to 256

// (for getting lighter colors)
let red = Math.floor(Math.random() * 255) + 64;
let green = Math.floor(Math.random() * 255) + 64;
let blue = Math.floor(Math.random() * 255) + 64;
let a = 0.5

// Construct a color withe the given values
let bgColor = "rgba(" + red + ", " + green + ", " + blue + ", " + a + ")";

var bg = $(document.body).css('background', `${bgColor}`);
// Set the background to the new color
bg = bgColor;
console.log(bg)

}

參考資料:
Create a Music Player using JavaScript
Won’t Math.floor(Math.random() * 255) generate uneven probabilities? [筆記][JavaScript]用Math.random()取得亂數的技巧