0%

成品

觀察功能需求

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()取得亂數的技巧

觀察功能

  1. 製作一個會員申請的表單
    • 帳號
    • email
    • 密碼
  2. 輸入框的顏色變化
  3. 判斷條件的設置

任務拆解

抓出要互動的元素

1
2
3
4
5
const form = document.querySelector('#form');
const username = document.querySelector('#username');
const email = document.querySelector('#email');
const password = document.querySelector('#password');
const password2 = document.querySelector('#password2');
  • 獲取物件層次中的父物件
  • 針對指定元素取得或設置指定的class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//show input error message
function showError(input, message) {
const formControl = input.parentElement;
formControl.className = 'form-control error';
}


form.addEventListener('submit', function (e) {
e.preventDefault();
if (username.value === '') {
showError(username, 'Username is required');
} else {
showSuccess(username);
}
})

1
2
const small = formControl.querySelector('small');
small.innerText = 'Username is required';
  • 修改顯示文字

成功輸入

1
2
3
4
5
//show success outline
function showSuccess(input) {
const formControl = input.parentElement;
formControl.className = 'form-control success';
}

複製相同條件判斷

  • 事件為點擊送出之後,會進行條件判斷
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
//event listener
form.addEventListener('submit', function (e) {
e.preventDefault();

if (username.value === '') {
showError(username, 'Username is required');
} else {
showSuccess(username);
};

if (email.value === '') {
showError(email, 'Email is required');
} else {
showSuccess(email);
};

if (password.value === '') {
showError(password, 'Password is required');
} else {
showSuccess(password);
};

if (password2.value === '') {
showError(password2, 'Password 2 is required');
} else {
showSuccess(password2);
};
})

設定符合的驗證

  • email的輸入應有規範樣式
  • 使用正則表達式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//check email is valid
//js email regex
//https://ithelp.ithome.com.tw/articles/10094951

function isValiEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}

//
if (email.value === '') {
showError(email, 'Email is required');
} else if (!isValiEmail(email.value)) {
showError(email, 'Email is not valid');
}
else {
showSuccess(email);
};

整合簡化 =>forEach

1
2
3
4
5
6
7
8
9
10
11
//Check required fields
function checkRequired(inputArr) {
inputArr.forEach(function (input) {
if (input.value.trim() === '') {
showError(input, 'is required');
} else {
showSuccess(input);
}
});

}
  • 修改showError,套入名稱
1
2
3
if (input.value.trim() === '') {
//console.log(input.id);//會顯示input的所有id
showError(input, `${input.id}is required`);

將getFieldName() function拉出

  • 製作訊息的第一個字母大寫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Check required fields
function checkRequired(inputArr) {
inputArr.forEach(function (input) {
if (input.value.trim() === '') {
//console.log(input.id);//會顯示input的所有id
showError(input, `${getFieldName(input)} is required`);
} else {
showSuccess(input);
}
});

}


//getFieldName
function getFieldName(input) {
return input.id.charAt(0).toUpperCase() + input.id.slice(1);
//將第一個字charAt(0),變大寫,並再接續後面的字slice(1)
}

長度檢查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// Check input length
function checkLength(input, min, max) {
if (input.value.length < min) {
showError(
input,
`${getFieldName(input)} must be at least ${min} characters`
);
} else if (input.value.length > max) {
showError(
input,
`${getFieldName(input)} must be less than ${max} characters`
);
} else {
showSuccess(input);
}
}

密碼檢查

1
2
3
4
5
6
// check password match
function checkPasswordMatch(input1, input2) {
if (input1.value !== input2.value) {
showError(input2, 'Password do not match')
}
}

參考資料:
JavaScript初學:DOM常用屬性與方法

認識 Web Storage:

HTML5 的 Web Storage 是一種可讓網頁將資料儲存於本地端的技術,其作用如同 cookie。

Cookie 儲存空間很小,最多僅能儲存4KB的資料。
HTML5 Web Storage 的儲存空間則大得多(儲存資料容量至少5MB以上)。

localStorage 與 sessionStorage

Web Storage 提供兩個物件可以將資料存在 client 端,一種是 localStorage,另一種是 sessionStorage

  • localStorage:可以跨瀏覽器分頁(tab)存取,且資料存取沒有期限,除非手動從本地端清除。
  • sessionStorage:生命週期較短,每次分頁或瀏覽器關掉後就會清除。

基本操作:

  • 設置資料:localStorage.setItem(key, value)
  • 取得資料:let storageValue = localStorage.getItem(key, value)
  • 清除資料:localStorage.removeItem(key)
  • 清除全部資料:localStorage.clear()

    只要將localStorage替換sessionStorage即可

注意:

儲存在本地端的資料,格式為 key : value,需注意的是「value 的型態只有 String」!

找到儲存的資料

  • 在 DevTools console

JSON 字串轉換

1
2
3
4
var arr = ['1','2','c'];

console.log( arr + " is " + typeof(arr));
// 1,2,c is object

1.轉換成字串 JSON.stringify

1
var arrToStr = JSON.stringify(arr);

2.轉換回原本格式 JSON.parse

  • 會把一個JSON字串轉換成 JavaScript的數值或是物件
1
var parse = JSON.parse(arrToStr);

參考資料:
[JavaScript] localStorage 的使用
[第七週] 瀏覽器資料儲存 - Cookie、LocalStorage、SessionStorage
[Day16] JavaScript - 前端資料存
HTML5 的 Web Storage- local storage 與 session storage
Day20 localStorage、sessionStorage
[JavaScript] JSON.stringify() and JSON.parse():變 JSON 和變物件

完成後的成品

觀察須製作的功能

  1. 重新整理網頁後,保有過去執行的狀態:localstorage
    • 實作內有使用:
    • localstorage:setItem,getItem
    • jason.stringfy/jason.parse

  1. 在new transation輸入資料後,會將資料傳入history中
    • add transation之後,頁面不會刷新(spa)(e.preventdefault)
  2. 摸到history中的項目,會出現刪除符號,點擊後刪除

  1. 輸入的金額,會顯示於上方income、expense,並且計算總額
    • 有正負值
    • 計算收入、花費

呈現完整語法

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
$(document).ready(function () {
//預設值的設置,取得存在本地的資料或是空陣列
var transactions = JSON.parse(localStorage.getItem('Transactions')) || [];

//陣列初始
if (transactions.length > 0) {
initHistory(transactions);
}
//點擊按鈕的事件
$('.btn').click(function (e) {
e.preventDefault();
//console.log('click');
//取得表格中的值
const text_val = $('#text').val();
const amount_val = $('#amount').val();
let id = generateID();

addTransactions(id, text_val, amount_val, transactions);

//推入陣列
transactions.push({
id: id,
name: text_val,
amount: amount_val
})


localStorage.setItem('Transactions', JSON.stringify(transactions));


})
updateValue(transactions);

});


//在list中插入
function addTransactions(id, name, amount, transactions) {
console.log('amount:', amount);

const Transaction_str = $('<li></li>').appendTo('#list');

Transaction_str.addClass(amount < 0 ? 'minus' : 'plus');
Transaction_str.html(`${name}<span> ${amount}</span><button class="delete-btn" data-id="${id}">x</button>`);
$('#list').append(Transaction_str);


$('#text').val('');
$('#amount').val('');

//刪除鈕 要交易交出後再綁事件
$('.delete-btn').last().click(function () {
$(this).parent().remove();
let id = $(this).data('id');
//console.log(id);
deleteFromLocalstorage(transactions, id); //要記得傳入id

});

}
//從localstorage刪除
//要記得在參數放入id
function deleteFromLocalstorage(transactions, id) {
transactions.forEach(function (item, index, arr) {
//console.log('item', item);
//console.log('index', index);
//console.log('arr', arr);
if (item.id === id) {
arr.splice(index, 1);
}
});
//迴圈刪除後,要儲存到localStorage才有確實刪去
localStorage.setItem('Transactions', JSON.stringify(transactions));
}


// Generate random ID
function generateID() {
return Math.floor(Math.random() * 100000000);
}


//數值更新計算
function updateValue(transactions) {
const amounts_arr = transactions.map(function (transaction) {
return transaction.amount
})

console.log(amounts_arr);

//計算加總
var total = 0;
$.each(amounts_arr, function () { total += parseFloat((this)) || 0; });
//傳回YOUR BALANCE
$('#balance').text(`$${total}`);
console.log(total);


//*************************** */
//從陣列找出>0的值,放置income
//console.log(amounts)
var income = amounts_arr.filter(
function (item) {
return item > 0
}
)
console.log(income);

var totalIncome = 0;
$.each(income, function () { totalIncome += parseFloat((this)) || 0; });

console.log(totalIncome); //回傳220
//傳回到income
$('#money-plus').text(`$${totalIncome}`);
//***************************** */
//從陣列找出<0的值,放置income
var expense = amounts_arr.filter(
function (item) {
return item < 0
}
)
console.log(expense);

var totalExpense = 0;
$.each(expense, function () { totalExpense += parseFloat((this)) || 0; });

console.log(totalExpense);
//傳回到expense
$('#money-minus').text(`$${totalExpense}`)


}

//init
function initHistory(transactions) {
transactions.forEach(transaction => {
addTransactions(transaction.id, transaction.name, transaction.amount, transactions);
});
updateValue(transactions);

}


各項目拆解:

add transations按下去之後,頁面不刷新,直接將資料傳入紀錄中

1
2
3
4
5
6
$(document).ready(function () {
$('.btn').click(function () {
console.log('click');
})
});
//點擊後,有印出click,但很快的刷新頁面
  • 加入e.preventDefault(); 用來阻止預設動作的發生

讓transation輸入後顯示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(document).ready(function () {
$('.btn').click(function (e) {
e.preventDefault();
console.log('click');
addTransactions('cash', '200');
})
});


function addTransactions(name, amount) {
var Transaction_str = `<li class='plus' >${name}<span> ${amount}</span><button class="delete-btn">x</button></li>`
$('#list').append(Transaction_str);
}

//

  • 提交之後清空
    • 選擇該輸入框,並利用val(),使內部的值清空
1
2
3
$('#text').val('');
$('#amount').val('');

從表格中取得值

  • 製作按鈕的點擊事件,取得表格中的值
  • 將值傳入函式的參數addTransactions(text_val, amount_val);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $(document).ready(function () {
    $('.btn').click(function (e) {
    e.preventDefault();
    console.log('click');

    //取得表格中的值
    const text_val = $('#text').val();
    const amount_val = $('#amount').val();
    addTransactions(text_val, amount_val);
    })

    });

製作刪除紐

  • 原本的撰寫方式,為選到最後一個刪除鈕,並點擊加以刪除
  • 此方式造成,無法確實刪除資料
1
2
3
4
$('.delete-btn').last().click(function () {
console.log('del');
})
}

  • 修改方式,只能綁定一筆transatin或是綁定最後一筆

  • 查看若實現點擊後刪除,是會刪除什麼?

    1
    2
    3
    4
     $('.delete-btn').last().click(function () {
    console.log(this);
    })
    }

  • 要刪除的是整筆資料,所以要去尋找他鄰近的元素

    1
    2
    3
    $('.delete-btn').last().click(function () {
    $(this).parent().remove();
    })

localstorage 執行

筆記補充:LocalStorage介紹

從此實作,可以看到它是一個陣列,裡面是物件

  • 設置一個空陣列
  • 並將物件push進去
1
2
3
4
5
6
7
8
9
10
//預設值的設置,取得存在本地的資料或是空陣列
var transactions = JSON.parse(localStorage.getItem('Transactions')) || [];

//推入陣列
transactions.push({
name: text_val,
amount: amount_val
})
localStorage.setItem('Transactions', JSON.stringify(transactions));

  • 增加初始function
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //init
    function initHistory(transactions) {
    transactions.forEach(transaction => {
    addTransactions(transaction.name, transaction.amount);
    });

    }

    //將此函式放於 $(document).ready()之中

    //陣列初始
    if (transactions.length > 0) {
    initHistory(transactions);
    }

將transation刪除後,並沒有完全從localstorage刪除

運用id來刪除

  • 從此段落:localstorage 執行,圖片中的存取陣列可以看到,沒有設置id

  • id是在addTransactions使用

    1
    2
    3
    4
    5
    6
    7
    8
    // Generate random ID
    function generateID() {
    return Math.floor(Math.random() * 100000000);
    }

    //floor:無條件捨去
    //ceil:無條件進位

  • 在創造的陣列中\物件,都加入id

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const id = generateID();
    addTransactions(id, text_val, amount_val);

    //推入陣列
    transactions.push({
    id: id,
    name: text_val,
    amount: amount_val
    })

  • 在addTransactions的函式中也要加入id

    1
    2
    3
    4
    function addTransactions(id, name, amount) {

    console.log(id, name, amount);}

  • 在button加入data-id

和資料有關的時候:data-原來欄位名稱

1
const Transaction_str = `<li class='plus' >${name}<span> ${amount}</span><button class="delete-btn"  data-id="${id}">x</button></li>`

  • 確定點擊刪除鈕的時後,是有抓到該id
    1
    2
    3
    4
    5
    6

    $('.delete-btn').last().click(function () {
    $(this).parent().remove();
    let id = $(this).data('id');
    console.log(id);
    })
  • let id = $(this).data(); 查看id時 取得的是物件
  • 應改為

此時還沒有真正刪除id,只是確定有抓到id :point_up_2:

jquery data(): 自定義屬性 (data attributes),讓我們可以任意讀寫資料在元素上,而且不會影響頁面的 layout
jQuery Data

  • 利用forEach、splice()刪除陣列內的資料

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function deleteFromLocalstorage(transactions, id) {
    transactions.forEach(function (item, index, arr) {
    //console.log('item', item);
    //console.log('index', index);
    //console.log('arr', arr);
    if (item.id === id) {
    arr.splice(index, 1);
    }
    });
    //迴圈刪除後,要儲存到localStorage才有確實刪去
    localStorage.setItem('Transactions', JSON.stringify(transactions));
    }
    ````


    * 查看迴圈的資料
    ![](https://i.imgur.com/FexS5lt.png)


    * 確實刪除存在localstorage的資料
    ```javascript
    localStorage.setItem('Transactions', JSON.stringify(transactions));

判斷輸入的數字大小,並給予class

  • 放在addTransactions()函式之中
1
2
3
4
5
6
const Transaction_str = $('<li></li>').appendTo('#list');

Transaction_str.addClass(amount < 0 ? 'minus' : 'plus');
Transaction_str.html(`${name}<span> ${amount}</span><button class="delete-btn" data-id="${id}">x</button>`);
$('#list').append(Transaction_str);

數值的更新

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 updateValue(transactions) {
const amounts_arr = transactions.map(function (transaction) {
return transaction.amount
})

console.log(amounts_arr);

//計算加總
var total = 0;
$.each(amounts_arr, function () { total += parseFloat((this)) || 0; });
//傳回YOUR BALANCE
$('#balance').text(`$${total}`);
console.log(total);


//*************************** */
//從陣列找出>0的值,放置income
//console.log(amounts)
var income = amounts_arr.filter(
function (item) {
return item > 0
}
)
console.log(income);
var totalIncome = 0;
$.each(income, function () { totalIncome += parseFloat((this)) || 0; });

//console.log(totalIncome); //回傳220
//傳回到income
$('#money-plus').text(`$${totalIncome}`);
//***************************** */
//從陣列找出<0的值,放置income
var expense = amounts_arr.filter(
function (item) {
return item < 0
}
)
console.log(expense);

var totalExpense = 0;
$.each(expense, function () { totalExpense += parseFloat((this)) || 0; });

console.log(totalExpense);
//傳回到expense
$('#money-minus').text(`$${totalExpense}`)


}
  • 創造一個amount的陣列
1
2
3
4
5
const amounts_arr = transactions.map(function (transaction) {
return transaction.amount
})
console.log(amounts_arr);

  • 計算加總數值
    1
    2
    3
    4
    5
    6
    var total = 0;
    $.each(amounts_arr, function () { total += parseFloat((this)) || 0; });
    //傳回YOUR BALANCE
    $('#balance').text(`$${total}`);
    console.log(total);

  • income 收入加總計算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//從陣列找出>0的值,放置income
//console.log(amounts)
var income = amounts_arr.filter(
function (item) {
return item > 0
}
)
console.log(income);
var totalIncome = 0;
$.each(income, function () { totalIncome += parseFloat((this)) || 0; });

//console.log(totalIncome); //回傳220
//傳回到income
$('#money-plus').text(`$${totalIncome}`);

  • expense支出加總
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//從陣列找出<0的值,放置expense
var expense = amounts_arr.filter(
function (item) {
return item < 0
}
)
console.log(expense);

var totalExpense = 0;
$.each(expense, function () { totalExpense += parseFloat((this)) || 0; });

console.log(totalExpense);
//傳回到expense
$('#money-minus').text(`$${totalExpense}`)


錯誤修正:

  • 調換推入陣列以及addTransactions的順序
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
//點擊按鈕的事件
$('.btn').click(function (e) {
e.preventDefault();
//console.log('click');
//取得表格中的值
const text_val = $('#text').val();
const amount_val = $('#amount').val();
let id = generateID();

//推入陣列
transactions.push({
id: id,
name: text_val,
amount: amount_val
});

addTransactions(id, text_val, amount_val, transactions);




localStorage.setItem('Transactions', JSON.stringify(transactions));

updateValue(transactions);
})
  • 將updateValue()放入事件中
1
2
3
4
5
6
7
8
//刪除鈕 要交易交出後再綁事件
$('.delete-btn').last().click(function () {
$(this).parent().remove();
let id = $(this).data('id');
//console.log(id);
deleteFromLocalstorage(transactions, id); //要記得傳入id
updateValue(transactions);
});

實作需求分析

  • 抓取api提供的匯率資料:使用jQuery,ajax
  • 選擇不同幣別,進行匯率換算
  • swap按鈕,使幣別交換

完成後的成品

jQuery.ajax 使用

1
2
3
4
5
6
7
8
9
10
11
$.ajax({
method: "get",
url: "https://v6.exchangerate-api.com/v6/06f04b805743079a1966771a/latest/USD",

})
.done(function( some_data ) {
console.log(data);
//成功抓到值的話,就會印出抓取到的資料
}).fail(function(){

})

  • 應用於exchangeRate:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $.ajax({
    url: `https://v6.exchangerate-api.com/v6/06f04b805743079a1966771a/latest/${currency_one}`, //注意符號
    method: 'get',//get,post,put
    dataType: 'json',
    success: function (data) {
    //console.log(data);
    const rate = data.conversion_rates[currency_two];
    //console.log(rate);
    $('#rate').text(`1 ${currency_one} = ${rate} ${currency_two}`);
    $('#amount-two').val((amountOne * rate).toFixed(2));
    }
    })

  • 詳細說明可以參照內文:計算匯率calculate函式

其他參考資料:

[ Alex 宅開發 ] 👨‍💻從 jQuery 入門到認識 JavaScript #5 Ajax 與非同步功能操作與原始碼探索

1
2
3
4
- `url`:來源,api的位置在哪裏
- `type`:api的呼叫方式
- `dataType`:它回給你的資料類型
- `data`:你要傳給它什麼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


$.ajax({
url: data.json,
type: 'get',//get,post,put,path,delete
dataType: 'json', //html,xml,text,jsonp
data: {}

}).then(function (res) {
//response <-> request
console.log(res);
return def.resolve();
}), function (err) {
console.log(err);
return def.reject();
}

計算匯率calculate 函式

  • 此函式為抓取匯率api的資料並帶入計算匯率
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
function calculate() {

var currency_one = $("#currency-one").val();
var currency_two = $('#currency-two').val();
var amountOne = $('#amount-one').val();
var amountTwo = $('#amount-two').val();

//https://app.exchangerate-api.com/dashboard/confirmed

$.ajax({
url: `https://v6.exchangerate-api.com/v6/06f04b805743079a1966771a/latest/${currency_one}`,//注意符號
method: 'get',//抓取值
dataType: 'json',
success: function (data) {
//console.log(data); //查看抓到的物件

const rate = data.conversion_rates[currency_two];
//console.log(rate);

$('#rate').text(`1 ${currency_one} = ${rate} ${currency_two}`);

//將值帶入
$('#amount-two').val((amountOne * rate).toFixed(2));
}
})


}

函式說明:

1.先將要抓取的值,宣告變數;分別有選擇的國家幣別、兌換的數量

2.串接資料

(1). 抓取資料的位置
url: https://v6.exchangerate-api.com/v6/06f04b805743079a1966771a/latest/${currency_one}

注意網址:
"https://v6.exchangerate-api.com/v6/06f04b805743079a1966771a/latest/USD"
注意網址後面有修改,要抓取會變動的變數: ${currency_one}

(2) 檢查是否有抓到要的資料:

  • 宣告:const rate = data.conversion_rates[currency_two];
  • console.log(rate);

  • 確定抓取的值是否正確:console.log($('#amount-two').val());

  • 將計算匯率帶入
    $('#amount-two').val((amountOne * rate).toFixed(2));

其他輸入值的抓取與連動

  • #currency-one的改變,會連動上面的函式計算結果

1
2
3
4
$("#currency-one").change(function () {
$("#rate").html("");
calculate();
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

//抓取amountone
$("#amount-one").on("input", function () {
$("#rate").html("");
calculate();
})

$("#currency-two").change(function () {
$("#rate").html("");
calculate();
})

//抓取amountTwo
$("#amount-two").on("input", function () {
$("#rate").html("");
calculate();
})
  • 發現使用.append(),會出現累積div文字的問題

  • (1) 清除函式.append(),累積div文字呈現的問題
    $("#rate").html("");

  • (2) 將原本使用.append()的地方,修改為.text(), $('#rate').text(`1 ${currency_one} = ${rate} ${currency_two}`);

swap按鈕 (使幣別交換)

  • 宣告一個變數,紀錄交換前的幣別let temp = $("#currency-one").val();
  • 確認是否宣告正確:console.log(temp);

1
2
3
4
5
6
7
8
9
//swap按鈕
$('#swap').click(function () {
let temp = $("#currency-one").val();//設一個變數來存放currency-one
$("#currency-one").val($("#currency-two").val());
$("#currency-two").val(temp);
calculate();
})

calculate(); //放於最後
  • console.log($("#currency-one").val());
  • console.log($("#currency-two").val());


參考資料:
重新認識 JavaScript: Day 16 那些你知道與不知道的事件們

1. DOM 與樹狀結構

  • DOM: document:網頁內部

    KURO 老師:
    DOM 是一個將 HTML 文件以樹狀的結構來表示的模型,而組合起來的樹狀圖,我們稱之為「DOM Tree」。

Huli 老師:瀏覽器提供該橋樑,讓我們用js去改面畫面的東西

API (Application Programming Interface)

  • 應用程式 編程 介面

    document 物件 API 文件

  • getElementsByTagName : 抓取html中某tag的元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!DOCTYPE html>
    <html lang="en">
    <body>
    <div>
    hello~

    </div>
    <div>
    yo!
    </div>
    <script>

    const elements = document.getElementsByTagName('div');
    console.log(elements);
    </script>
    </body>
    </html>

  • getElementsByClassName: 抓取html中某class元素(有好幾個相同名稱的class,會一並抓取)

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>

<body>
<h1>DOM
</h1>
<div class="block1">
hello~

</div>
<div>
yo!
</div>
<script>

const elements = document.getElementsByClassName('block1');
console.log(elements);
</script>
</body>

</html>

  • getElementById : 注意:getElement 沒有加s!這是用來抓取指定ID

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <body>
    <h1>DOM
    </h1>
    <div class="block1">
    hello~

    </div>
    <div id="myyo">
    yo!
    </div>
    <script>
    const elements = document.getElementById('myyo');
    console.log(elements);
    </script>
    </body>

  • querySelector: 後面接的是css選擇器

  • 只會針對元素的第一筆資料,其他並不會被選入這時候可以使用

    • 選擇tag標籤:(‘div’):注意:使用此方式當html有多個div,他只會選擇到第一個符合的
    • 選擇cass : (‘.calssName’)
    • 選擇id : (‘#idName’)

  • 也可以選區某一tag下的tag

    • querySelector('div > a')
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <body>
      <h1>DOM
      </h1>
      <div class="block1">
      hello~

      </div>
      <div id="myyo">
      yo!
      <a href="#">hello</a>
      </div>
      <script>

      const elements = document.querySelector('div > a');
      console.log(elements);
      </script>
      </body>
  • querySelectorAll: 可以選取多個元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<h1>DOM
</h1>
<div class="block1">
hello~

</div>
<div id="myyo">
yo!
<a href="#">hello</a>
</div>
<script>

const elements = document.querySelectorAll('div');
console.log(elements);
</script>

Eloquent JavaScript, 3rd Edition

順利選則元素之後,嘗試做些改變

可以看看,js如何與瀏覽器互動 ->JavaScript 與瀏覽器的溝通

BOM 與 window 物件

  • BOM (Browser Object Model,瀏覽器物件模型),是瀏覽器所有功能的核心,與網頁的內容無關。
    • window是瀏覽器的根物件,也是BOM的瀏覽器模型
  • window.location.hrrf => 可以知道現在網址
  • window.history.back(); => 回到網頁瀏覽的上一頁

參考資料

Day03-深入理解網頁架構:DOM
JS 筆記 - 認識 DOM 文件物件模型
MDN-Document
The HTML DOM Document Object
JavaScript 基礎知識-querySelectorAll
The Document Object Model
JavaScript入門系列:BOM和DOM筆記

程式柴

Function、array都屬於物件

物件的例子

1
2
3
4
5
6
7
8
9
var person = {
name: ['Bob', 'Smith'],
age: 32,
gender: 'male',
interest: ['music', 'movie'],
greeting: function () {
console.log('hi I\'m' + this.name[0] + '.');
}
}

物件的創建

var x={}

  • 有一隻狗,其名字、年齡,函式
    1
    2
    3
    4
    5
    6
    7
    8
    var dog = {
    name: 'tracy',
    age: 20,

    bow: function () {
    console.log('bow!bow!');
    }
    }
  • 用.呼叫屬性,或是dog[‘key’] :point_down:
  • 物件內的函式

添加屬性至物件上

  • 增加值的方式

    • dog.color = 'white';
    • dog['size'] = 'small'; =>較常用
  • 增加函式 => 一樣用.的方式

    • dog.bowbow = function () { console.log('bow!bow!~~') }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var dog = {
name: 'tracy',
age: 7,
bow: function () {
console.log('bow!');
}
}

//增加值的方式
//1
dog.color = 'white';
dog.eyes = 'big';
//2
dog['size'] = 'small';
//增加函式
dog.bowbow = function () {
console.log('bow!bow!~~')
}

陣列

程式柴

  • 陣列用 中括號[ ]

  • 陣列的計算是從零開始(取陣列的索引)

    • 呼應for 迴圈,從0開始,從索引第一個
      1
      2
      3
      4
      arr = [111, 222, 333]
      console.log(arr);
      console.log(arr[1]); //222
      //呼應for 迴圈,從0開始,從索引第一個
  • 陣列中的東西都是相同屬性,比較好操作

    1
    2
    3
    arr2 = ['dog', 'cat', 'sth']
    console.log(arr.length); //3
    arr3 = [111, 'cat', 'sth']

  • 練習:要紀錄10位學生的分數,並加總

    1
    2
    var score = [1, 3, 4, 5, 100];
    console.log(score);

    陣列基本操作 -> poppush 較常用

  • score.push(1000) -> 新增元素到現有陣列中,加入尾端

  • score.unshift(888) -> 加入陣列最前面

  • pop:從最末端抽走元素

1
2
3
4
5
6
7
var myHeros=['孫悟空', '佐助', '女超人', '美國隊長'];
//從最末端抽走元素
myHeros.pop();

//從頭抽走元素
myHeros.shift();
console.log(myHeros);

陣列替換元素

  • 在原本陣列中,選取要取代的該元素,後面加=,並輸入內容

陣列取得最後一個元素

  • 該陣列長度-1,就可以取得最後一個元素
    • 長度為七,最後一個的索引是6 :point_down:

用 indexOf 取得元素的索引

  • Of的o要大寫
1
2
3
4
var myHeros=['孫悟空', '佐助', '女超人', '美國隊長'];
//檢查誰誰誰有在陣列中嗎
console.log(myHeros.indexOf('達爾')); //印出-1,意指此元素不存在陣列中
console.log(myHeros.indexOf('美國隊長')); //印出3(在陣列中第幾個索引

切片 slice 與方法

1
2
3
4
var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
//用索引
console.log(arr.slice(2, 4));//不包含最後一個
console.log(arr.slice(2, 5));

  • 複製陣列
    1
    2
    var shallowCopy = fruits.slice(); // 這就是複製陣列的方式
    // ["Strawberry", "Mango"]

MDN slice
MDN array
JavaScript Array 陣列操作方法大全 ( 含 ES6 )

陣列與物件

  • 例子:被老師要求要記錄,學生的名字,分數,地址…

  • 以陣列的想法,會將各類別分類,放入同屬性的結果

    • 學生的分數,名字個一個陣列
    • 要取得同一位學生的所有資料,要個別從矩陣中拿取
  • 一個資料集合,就能代表一個學生 –>物件

    • 以下面的資料結構來代表學生的一些屬性

補充

  • 物件與陣列,與等號的關係,理解
    • 下方的結果都是false

因為:

  • var obj=[a:1],會將它放在某一個記憶體位置
  • var obj2=[a:1],會將它放在另一個記憶體位置

PS:即便內容,數值一樣,但js判斷的是記憶體位置不一樣