0%

要求

製作一個顯示各個時區的時鐘

攻略

  • 將時區轉成timestamp
  • 在依各時區做計算,再轉回需要的格式
  • 利用setInterval()達到每秒更新

    將時區轉換成timestamp

    利用new Date()宣告一個時間物件,在轉換成時間戳
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let d =new Date()
    // 當前時區的時間
    // Wed Jan 13 2021 19:05:13 GMT+0800 (台北標準時間)

    new Date().valueOf()
    //這裡的時間顯示至毫秒
    //1610536513697

    new Date(1610536513697)
    // Wed Jan 13 2021 19:05:13 GMT+0800 (台北標準時間)

    各時區計算

    1
    2
    3
    4
    5
    6
    // 因為 GMT+0800 (台北標準時間) 代表 台北比格林威治時間快 8 小時
    // GMT-0500 (紐約標準時間) 代表 紐約比格林威治時間慢 5 小時
    let localTime=new Date(timestamp + (timeZone-8)*60*60*1000)
    // 因為 timestamp 顯示為毫秒,所以要乘 1000 轉換為秒,
    // 在乘 60 轉換為分,在乘 60 轉換為小時

    toLocaleString()

    1
    2
    3
    //toLocaleString('語系',自由參數)
    new Date().toLocaleString('ko-KR', { timeZone: 'UTC' });
    // "2021. 1. 13. 오전 11:35:04"
    最後附上程式碼

要求:製作一個計算機
攻略:判斷輸入的字元為何,以及如何計算算式


HTML 結構

個人習慣先把版型切好,在撰寫程式塞入資料,之前習慣用class 名稱 來挖 js DOM
這次改用屬性 [],使得程式碼容易維護查找,
所以將計算機結構 分為 數字、計算符號、歸零鍵、刪除鍵、等於鍵、目前紀錄、過去紀錄

JS 結構

  • 利用toString()轉為字串,再用parseFloat()轉為數字計算,並搭配isNaN()判斷
  • toLocaleString()能將劃分三位數
  • 最後是 Class 把功能物件化。

最後附上程式碼連結

進階指令

v-class

button v-on
input v-model

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
<div id="app">
<h4>物件寫法</h4>
<div class="box" :class="{'rotate':isTransform,'bg-danger':boxColor}"></div>
<p>請為此元素加上動態 className</p>
<hr>
<button class="btn btn-outline-primary" @click="isTransform = !isTransform">選轉物件</button>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="classToggle1" v-model="boxColor">
<label class="form-check-label" for="classToggle1">切換色彩</label>
</div>
<hr>
<h5>物件寫法 2</h5>
<div class="box" :class="objectClass"></div>
<p>請將此範例改為 "物件" 寫法</p>
<hr>
<button class="btn btn-outline-primary" @click="objectClass.rotate =!objectClass.rotate">選轉物件</button>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="classToggle2" v-model="objectClass['bg-danger']">
<label class="form-check-label" for="classToggle2">切換色彩</label>
</div>
<hr>
<h4>陣列寫法</h4>
<button class="btn" :class="arrayClass">請操作本元件</button>
<p>請用陣列呈現此元件 className</p>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="classToggle3" v-model="arrayClass" value="btn-outline-primary">
<label class="form-check-label" for="classToggle3">切換樣式</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="classToggle4" v-model="arrayClass" value="active">
<label class="form-check-label" for="classToggle4">啟用元素狀態</label>
</div>
<hr>
<h4>綁定行內樣式</h4>
<p>請用不同方式綁定以下行內樣式</p>
<div class="box" :style="styleObject"></div>
<div class="box" :style="[styleObject,styleObject2,styleObject3]"></div>
<div class="box" :style="{backgroundColor: styleObject2.boxShadow}"></div>
<hr>
<h5>自動加上 Prefix (每個版本結果不同)</h5>
<div class="box" :class="styleObject3"></div>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
isTransform: false,
boxColor: false,
objectClass: {
'rotate': false,
'bg-danger': false,
},

// Array 操作
arrayClass: [],

// 行內樣式
// 使用駝峰式命名
styleObject: {
backgroundColor: 'red',
borderWidth: '5px'
},
styleObject2: {
boxShadow: '3px 3px 5px rgba(0, 0, 0, 0.16)'
},
styleObject3: {
userSelect: 'none'
}
},
});
</script>

<style>
.box {
transition: all .5s;
}
.box.rotate {
transform: rotate(45deg)
}
</style>

v-for

v-for 加上 key 綁id
修改陣列資料Vue.set(array,index,value)

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
144
145
146
147
148
149
150
151
152
153
154
<div id="app">
<h4>陣列與物件的迴圈</h4>
<p>請使用 v-for 在陣列與物件上,並且加上索引</p>
<ul>
<li v-for="(item, key) in objectData">
{{ key }} - {{ item.name }} {{ item.age }} 歲
</li>
</ul>
<ul>

</ul>
<hr>
<h4>Key</h4>
<p>請在範例上補上 key,並觀察其差異</p>
<ul>
<li v-for="(item, key) in arrayData":key="item.age">
{{ key }} - {{ item.name }} {{ item.age }} 歲 <input type="text">
</li>
</ul>
<button class="btn btn-outline-primary" @click="reverseArray">反轉陣列</button>

<h4>Filter</h4>
<p>請製作過濾資料</p>
<input type="text" class="form-control" v-model="filterText" @keyup.enter="filterData">
<ul>
<li v-for="(item, key) in arrayData" :key="item.age">
{{ key }} - {{ item.name }} {{ item.age }} 歲 <input type="text">
</li>
</ul>

<h4>不能運作的狀況</h4>
<p>講師說明</p>
<button class="btn btn-outline-primary" @click="cantWork">無法運行</button>
<ul>
<li v-for="(item, key) in arrayData" :key="item.age">
{{ key }} - {{ item.name }} {{ item.age }} 歲 <input type="text">
</li>
</ul>

<h4>純數字的迴圈</h4>
<ul>
<li v-for="item in 10">
{{ item }}
</li>
</ul>

<h4>Template 的運用</h4>
<p>請將兩個 tr 一組使用 v-for</p>
<table class="table" v-for="(item, index) in arrayData" :key="index">
<template>
<tr>
<td>{{item.age}}</td>
</tr>
<tr>
<td>{{item.name}}</td>
</tr>

</template>
</table>

<h4>v-for 與 v-if</h4>
<p>請加上 v-if 判斷式</p>
<ul>
<li v-for="(item, key) in arrayData" v-if="item.age>20">
{{ key }} - {{ item.name }} {{ item.age }} 歲
</li>
</ul>

<h4>v-for 與 元件</h4>
<p>講師說明</p>
<ul>
<list-item :item="item" v-for="(item, key) in arrayData" :key="key"></list-item>
</ul>
<p>注意:現在建議元件使用 v-for 都加上 key。<a href="https://cn.vuejs.org/v2/guide/list.html#%E4%B8%80%E4%B8%AA%E7%BB%84%E4%BB%B6%E7%9A%84-v-for">參考</a></p>
</div>

<script>
Vue.component('list-item', {
template: `
<li>
{{ item.name }} {{ item.age }} 歲
</li>
`,
props: ['item']
});

var app = new Vue({
el: '#app',
data: {
arrayData: [
{
name: '小明',
age: 16
},
{
name: '漂亮阿姨',
age: 24
},
{
name: '杰倫',
age: 20
}
],
objectData: {
ming: {
name: '小明',
age: 16
},
auntie: {
name: '漂亮阿姨',
age: 24
},
jay: {
name: '杰倫',
age: 20
}
},
filterArray: [],
filterText: ''
},
methods: {
// 請在此練習 JavaScript

// 解答:
reverseArray: function () {
this.arrayData.reverse()
console.log(this.arrayData)
},
filterData: function () {
var vm = this;
vm.filterArray = vm.arrayData.filter(function (item) {
console.log(vm.filterText, item.name, item.name.match(vm.filterText))
return item.name.match(vm.filterText);
});
},
cantWork: function () {
// // 情境一
// this.arrayData.length = 2;

// 情境二
this.arrayData[0] = {
name: '阿強',
age: 99
}
//解法
Vue.set(this.arrayData, 0, {
name: '阿強',
age: 99
})
console.log(this.arrayData)
}
}
});
</script>

v-if v-else-if v-else

v-if 移除整個 DOM
v-show display:none

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
<div id="app">
<h4>v-if, v-else</h4>
<p>使用 v-if, v-else 切換物件呈現</p>
<div class="alert alert-success" v-if="isSuccess">成功!</div>
<div class="alert alert-danger" v-else>失敗!</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="isSuccess" v-model="isSuccess">
<label class="form-check-label" for="isSuccess">啟用元素狀態</label>
</div>

<h4>template 標籤</h4>
<p>使用 template 切換多數 DOM 呈現</p>
<table class="table">
<thead>
<th>編號</th>
<th>姓名</th>
</thead>
<template v-if="showTemplate">
<tr>
<td>1</td>
<td>安妮</td>
</tr>
<tr>
<td>2</td>
<td>小明</td>
</tr>
</template>
</table>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="showTemplate" v-model="showTemplate">
<label class="form-check-label" for="showTemplate">啟用元素狀態</label>
</div>
<hr>
<h4>v-else-if</h4>
<p>使用 v-else-if 做出分頁頁籤</p>
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link" href="#" @click.prevent="link='a'" :class="{'active': link === 'a'}">標題一</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" @click.prevent="link='b'" :class="{'active': link === 'b'}">標題二</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" @click.prevent="link='c'" :class="{'active': link === 'c'}">標題三</a>
</li>
</ul>
<div class="content">
<div v-if="link === 'a'">A</div>
<div v-else-if="link === 'b'">B</div>
<div v-else-if="link === 'c'">C</div>
</div>
<hr>
<h4>KEY</h4>
<p>講師說明 Vue 切換狀態可能遇到的問題</p>
<template v-if="loginType === 'username'">
<label>Username</label>
<input class="form-control" placeholder="Enter your username" :key="1">
</template>
<template v-else>
<label>Email</label>
<input class="form-control" placeholder="Enter your email address" :key="2">
</template>
<button class="btn btn-outline-primary mt-3" @click="toggleLoginType" >切換狀態</button>
<hr>
<h4>v-if 與 v-show</h4>
<p>講師說明 v-if 與 v-show 的差異</p>
<div class="alert alert-success" v-show="isSuccess">成功!</div>
<div class="alert alert-danger" v-show="!isSuccess">失敗!</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="isSuccess2" v-model="isSuccess">
<label class="form-check-label" for="isSuccess2">啟用元素狀態</label>
</div>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
isSuccess: true,
showTemplate: true,

link: 'a',

loginType: 'username'
},
methods: {
toggleLoginType: function () {
return this.loginType = this.loginType === 'username' ? 'email' : 'username'
}
}
});
</script>

Computed Watch

Computed 是監聽整體資料變化
Watch 是監聽 Vue 變數變化

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
<div id="app">
<h4>Computed</h4>
<p>使用 Computed 來過濾資料。</p>
<input type="text" class="form-control" v-model="filterText">
<ul>
<li v-for="(item, key) in filterArray" :key="item.age">
{{ key }} - {{ item.name }} {{ item.age }} 歲 <input type="text">
</li>
</ul>
<p>使用 Computed 來呈現時間格式。</p>
<p>{{formatTime}}</p>
<h4>Watch</h4>
<p>使用 trigger 來觸發旋轉 box、並在三秒後改變回來</p>
<div class="box" :class="{'rotate': trigger }"></div>
<hr>
<button class="btn btn-outline-primary" @click="openTrigger">Counter</button>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
arrayData: [
{
name: '小明',
age: 16
},
{
name: '漂亮阿姨',
age: 24
},
{
name: '杰倫',
age: 20
}
],
filterText: '',
trigger: false,
newDate: 0
},
methods: {
openTrigger: function() {
var vm = this;
vm.trigger = true
},
closeTrigger: function() {
var vm = this;
setTimeout(function() {
vm.trigger = false
}, 3000);
}
},
watch: {
trigger: function() {
this.closeTrigger();
}
},
computed: {
filterArray: function () {
var vm = this;
return vm.arrayData.filter(function(item) {
return item.name.match(vm.filterText);
});
},
formatTime: function () {
console.log(this.newDate)
var dates = new Date(this.newDate * 1000);
var year = dates.getFullYear();
var month = dates.getMonth() + 1;
var date = dates.getDate() + 1;
var hours = dates.getHours();
var minutes = dates.getMinutes();
var seconds = dates.getSeconds();
return `${year}/${month}/${date} ${hours}:${minutes}:${seconds}`
}
},
mounted: function () {
this.newDate = Math.floor(Date.now() / 1000);
}

});
</script>

<style>
.box {
transition: all .5s;
}
.box.rotate {
transform: rotate(45deg)
}
</style>

表單細節操作

select+multiple = 複選

修飾符

v-model.lazy =離開元素才會顯現
v-model.number =value轉數字
v-model.trim =最後不留白

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
<div id="app">
<h4>Select</h4>
<select name="" id="" class="form-control" v-model="selected">
<option disabled value="">請選擇</option>
<option value="小美">小美</option>
<option value="可愛小妞">可愛小妞</option>
<option value="漂亮阿姨">漂亮阿姨</option>
</select>
<p>小明喜歡的女生是 {{ selected }}。</p>
<hr>
<select name="" id="" class="form-control" v-model="selected2" >
<option disabled value="">請選擇</option>
<option :value="item" v-for="(item, index) in selectData" :key="index">{{item}}</option>
</select>
<p>小明喜歡的女生是 {{ selected2 }}。</p>
<hr>
<h4 class="mt-3">多選</h4>
<select name="" id="" class="form-control" multiple v-model="multiSelected" >
<option value="小美">小美</option>
<option value="可愛小妞">可愛小妞</option>
<option value="漂亮阿姨">漂亮阿姨</option>
</select>
<p>小明喜歡的女生是 <span v-for="item in multiSelected">{{ item }} </span>。</p>
<hr>
<h4 class="mt-3">複選框</h4>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="sex" v-model="sex" true-value="boy" false-value="girl">
<label class="form-check-label" for="sex">{{ sex }}</label>
</div>
<h4 class="mt-3">修飾符</h4>
{{ lazyMsg }}
<input type="text" class="form-control" v-model.lazy="lazyMsg">
<br>
<pre>{{ age }}</pre>
<input type="number" class="form-control" v-model.number="age">
<br>
{{ trimMsg }}緊黏的文字
<input type="text" class="form-control" v-model.trim="trimMsg">
</div>

<script>
var app = new Vue({
el: '#app',
data: {
singleRadio: '',
selected: '',
selectData: ['小美', '可愛小妞', '漂亮阿姨'],
selected2: '',
multiSelected: [],
sex: '男生',

// 修飾符
lazyMsg: '',
age: '',
trimMsg: ''
},
});
</script>

v-on

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
<div id="app">
<p>請切換下方 box 的 className</p>
<div class="box" :class="{'rotate': isRotate }"></div>
<hr>
<button class="btn btn-outline-primary" @click="changeRotate">切換 box 樣式</button>
<hr>
<h4>帶入參數</h4>
<ul>
<li v-for="item in arrayData" class="my-2">
{{ item.name }} 有 {{ item.cash }} 元
<button class="btn btn-sm btn-outline-primary" @click="storeMoney(item)">儲值</button>
</li>
</ul>
<h4>修飾符</h4>
<h5>事件修飾符</h5>
<ul>
<li>.stop - 調用 event.stopPropagation()。</li>
<li>.prevent - 調用 event.preventDefault()。</li>
<li>.capture - 添加事件偵聽器時使用 capture 模式。</li>
<li>.self - 只當事件是從偵聽器綁定的元素本身觸發時才觸發回調。</li>
<li>.once - 只觸發一次回調。</li>
</ul>

<h5>按鍵修飾符</h5>
<ul>
<li>.{keyCode | keyAlias} - 只當事件是從特定鍵觸發時才觸發回調。</li>
<li>別名修飾 - .enter, .tab, .delete, .esc, .space, .up, .down, .left, .right</li>
<li>修飾符來實現僅在按下相應按鍵時才觸發鼠標或鍵盤事件的監聽器 - .ctrl, .alt, .shift, .meta</li>
</ul>
<h6 class="mt-3">keyCode</h6>
<input type="text" class="form-control" v-model="text" @keyup.enter="trigger(13)">

<h6 class="mt-3">別名修飾</h6>
<input type="text" class="form-control" v-model="text" @keyup.space="trigger('space')">

<h6 class="mt-3">相應按鍵時才觸發的監聽器</h6>
<input type="text" class="form-control" v-model="text" @keyup.shift.enter="trigger('shift + Enter')">

<h5>滑鼠修飾符</h5>
<ul>
<li>.left - (2.2.0) 只當點擊鼠標左鍵時觸發。</li>
<li>.right - (2.2.0) 只當點擊鼠標右鍵時觸發。</li>
<li>.middle - (2.2.0) 只當點擊鼠標中鍵時觸發。</li>
</ul>
<h6 class="mt-3">滑鼠修飾符</h6>
<div class="p-3 bg-primary">
<span class="box" @click.left="trigger('Right button')">
</span>
</div>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
arrayData: [
{
name: '小明',
age: 16,
cash: 500
},
{
name: '漂亮阿姨',
age: 24,
cash: 1000
},
{
name: '杰倫',
age: 20,
cash: 5000
}
],
isRotate: false,
text: ''
},
methods: {
changeRotate: function() {
this.isRotate = !this.isRotate;
},
storeMoney: function(item) {
item.cash = item.cash + 500;
},
trigger: function(name) {
console.log(name, '此事件被觸發了')
}
}
// 解答
});
</script>

<style>
.box {
display: block;
transition: all .5s;
}
.box.rotate {
transform: rotate(45deg)
}
</style>

基礎指令

v-text v-html v-modal

v-text == textContent
v-html == innerHtml
v-modal == 宣告變數 雙向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="app">
{{text}} //textContent
<div v-text="text"></div> //textContent
<div v-html="text"></div> //innerHtml
<input type="text" v-model="text"> //雙向綁定 input.value=Vue data.text
</div>

<script>
let app =new Vue({
el:'.app', //綁定DOM
data:{
text:'<span>hello</span>'
}
})
</script>

v-bind

可縮寫成 :

1
<img :src="imgSrc"  :class="className" alt=""> //setAttribute

v-bind == setAttribute

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<img v-bind:src="imgSrc" v-bind:class="className" alt=""> //setAttribute
</div>

<script>
var app = new Vue({
el: '#app',
data: {
imgSrc: 'https://images.unsplash.com/photo-1479568933336-ea01829af8de?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=d9926ef56492b20aea8508ed32ec6030&auto=format&fit=crop&w=2250&q=80',
className: 'img-fluid'
}
})
</script>

v-for v-if

pre 類似console.log(),但直接渲染在網頁上。
v-for == forEach
v-if == if

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
<div id="app">
<pre>
{{list}}
</pre>

<ul>
<li v-for="(item, index) in list" v-if="item.age<25">
<h5>{{item.name}}</h2>
<p>年齡:{{item.age}}</p>
</li>
</ul>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
list: [
{
name: '小明',
age: 16
},
{
name: '媽媽',
age: 38,
},
{
name: '漂亮阿姨',
age: 24
}
]
}
})
</script>

v-on

可縮寫成

1
<button class="btn btn-primary mt-1" @click="reverseText">轉字串</button>

v-on == addEventListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<input type="text" class="form-control" v-model="text"> // 雙向綁定
<button class="btn btn-primary mt-1" v-on:click="reverseText">轉字串</button> // v-on:事件="執行函式"
<div class="mt-3">
{{ newText }}
</div>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
text: '',
newText: ''
},
methods: {
reverseText(params) { // 不用寫 function
console.log(this.text);
this.newText=this.text.split('').reverse().join('')
}
},
});
</script>

v-class

v-class == addClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<div class="box" :class="{'rotate':isTransform}"></div>
//{'加入class名稱': true or false ,可加入判斷式}
<hr>
<button class="btn btn-outline-primary" @click="isTransform = !isTransform">轉物件</button>
</div>

<script>
var app = new Vue({
el: '#app',
data: {
isTransform: false
},
});
</script>
<style>
.box {
transition: transform .5s;
}
.box.rotate {
transform: rotate(45deg)
}
</style>

Vue component

Vue.component(‘自定義名稱’,{})

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
<div id="app">
<div>
你已經點擊 <button class="btn btn-outline-secondary btn-sm" @click="counter += 1">{{ counter }}</button> 下。
</div>
<counter-component></counter-component>
</div>

<script>
// 請在此撰寫 JavaScript
Vue.component('counter-component', {
// 取用的功能
data:function(){
return {counter: 0}
},
// 顯現的方式
template:`
<button class="btn btn-outline-secondary btn-sm" @click="counter += 1">{{ counter }}</button>
`
})

let app = new Vue({
el: '#app',
data: {
counter: 0
},
});
</script>

DatePicker

日曆選擇器在 BootStrapjQuery UI 等其實都已經有相關的套件,不過這次是使用Date Range Picker
,因為要使用在訂房網上,所以需要用到選取範圍功能。

Date Range Picker

開始使用

只要至官網插入 cdn 或下載檔案下來,
並宣告.daterangepicker();即可使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
</head>
<body>
<input type="text" class="date">
<script>
$('.date').daterangepicker()
</script>
</body>
</html>

常用選項

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
$('.date').daterangepicker({
"startDate":開始日期,
"endDate": 結束日期,
"locale": {
"format": "YYYY年MM月DD日", // 日期格式
"separator": " - ",
"applyLabel": "確認", //自定義 apply 按鈕名稱
"cancelLabel": "取消", //自定義 cancel 按鈕名稱
"daysOfWeek": [ //自定義星期名稱
"日",
"一",
"二",
"三",
"四",
"五",
"六"
],
"monthNames": [ //自定義月份名稱
"1 月",
"2 月",
"3 月",
"4 月",
"5 月",
"6 月",
"7 月",
"8 月",
"9 月",
"10 月",
"11 月",
"12 月"
],
"firstDay": 1 //自定義開始星期,0為星期日
},
})

客製化

因為有太多功能可以選擇,官網下方也有 UI 介面,供大家客製化選擇。

開始客製化日期選擇器

如何取出日期範圍的值

我們可以利用 while 迴圈,再利用 new Date()產生日期格式。

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
const getDatesBetween = (startDate, endDate) => {
let reserveDayData=[]
let result=[]
let currentDate = startDate
let holiday=0
let weekday=0
console.log(startDate,endDate)
while(currentDate < endDate){ //開始日期+1天 < 結束日期
reserveDayData.push(new Date(currentDate))
// 台北時間跟國際時間差八小時
result.push(new Date(currentDate+ 8 * 3600 * 1000).toISOString().split("T")[0])
// 現在日期加一天
currentDate = moment(currentDate).add(1, 'days');
}
console.log(reserveDayData)
console.log(result)
// 計算幾晚
// 最後一天沒住,所以扣除
reserveDayData.pop()
reserveDayData.forEach(function (item) {
if(item.getDay()===0 ||item.getDay()===6){
return holiday+=1
}else{
return weekday+=1
}
})
console.log(holiday)
console.log(weekday)
}

範例

JavaScript match()

通常都會搭配正規表達式使用,若找到匹配的內容會回傳內容,
反之則傳回null.

1
2
3
4
5
6
let str='abc123'
// 含數字或小寫字母之字串
let re=/[a-z0-9]/
// 篩選
let found=str.match(re)
console.log(found)

正規表達式


/要驗證的內容/


帳號註冊範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 帳號驗證
if (emailStr === '') {
reportOfEmail.innerHTML = 'Email不能為空'
} else if (emailStr.match(/[@]+/) === null ) {
reportOfEmail.innerHTML = 'Email必須包含 @符號'
} else if (emailStr.match(/[.]+/) === null ) {
reportOfEmail.innerHTML = 'Email格式錯誤'
} else {
reportOfEmail.innerHTML = ''
account.email=emailStr;
}
// 密碼驗證
if (passwordStr === '') {
reportOfPassword.innerHTML = '密碼不能為空'
} else if (passwordStr.match(/[a-zA-Z]+/) === null || passwordStr.match(/[0-9]+/) === null) {
reportOfPassword.innerHTML = '密碼需包含英數'
} else {
reportOfPassword.innerHTML = ''
account.password=passwordStr;
}

參考文章

https://jonas1011.pixnet.net/blog/post/26065399
https://www.runoob.com/jsref/jsref-match.html
https://5xruby.tw/posts/learn-regular-expression/

如何利用 JavaScript 做出一個時鐘

HTML

我們要創建一個 div,裡面要包裹著時針、分針,秒針和 數字 1 ~ 12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="clock">
<div class="hand hour data-hour-hand"></div>
<div class="hand minute data-minute-hand"></div>
<div class="hand second data-second-hand"></div>
<div class="number number1">1</div>
<div class="number number2">2</div>
<div class="number number3">3</div>
<div class="number number4">4</div>
<div class="number number5">5</div>
<div class="number number6">6</div>
<div class="number number7">7</div>
<div class="number number8">8</div>
<div class="number number9">9</div>
<div class="number number10">10</div>
<div class="number number11">11</div>
<div class="number number12">12</div>
</div>

CSS

時鐘介面

我們先給背景色,在給 .clock 寬高和外框把它變圓形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*, *::after, *::before {
box-sizing: border-box;
}
body{
background-color: #D9AFD9; //設置背景色
}
// 時鐘
.clock{
width: 300px; //寬
height: 300px; //高
background-color: rgba(255,255,255,.8); //時鐘的背景色
border-radius: 50%; //變圓
border: 2px solid black; //給個外框
}

時鐘數字

利用絕對定位,把數字定位在時鐘上,所以先給.clockposition:relative;
設置--rotation變數,一個圓為 360°,時鐘為 12 小時制,所以360°/12=30°
每前進一格為30度,再把個角度帶入變數中。

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
// 時鐘
.clock{
width: 300px;
height: 300px;
background-color: rgba(255,255,255,.8);
border-radius: 50%;
border: 2px solid black;
position: relative; // 要定位在時鐘上
}
// 時鐘數字
.clock .number{
--rotation:0; //設置變數,起始為 0 度
position: absolute;
width: 100%;
height: 100%;
text-align: center;
transform: rotate(var(--rotation));
font-size: 1.5rem;
}
// 帶入角度
// 360deg/12=30deg
.clock .number1{--rotation:30deg;}
.clock .number2{--rotation:60deg;}
.clock .number3{--rotation:90deg;}
.clock .number4{--rotation:120deg;}
.clock .number5{--rotation:150deg;}
.clock .number6{--rotation:180deg;}
.clock .number7{--rotation:210deg;}
.clock .number8{--rotation:240deg;}
.clock .number9{--rotation:270deg;}
.clock .number10{--rotation:300deg;}
.clock .number11{--rotation:330deg;}

時針、分針、秒針

先設定共通 .hand,因為我們有設 left:50%,所以要下個transform: translateX(-50%)使它置中,再利用transform-origin:bottom,把中心點定成下方。

時針、分針、秒針 再給予寬高、背景色。

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
//共通

.clock .hand{
--rotation:0;
position: absolute;
left: 50%;
bottom: 50%;
background-color: black;
border: 1px solid white;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
z-index: 10; //保證在最上面
transform-origin: bottom; // 中心點定在下方
// 使它置中 和帶入函數及變數
transform: translateX(-50%) rotate(calc(var(--rotation)*1deg));
}

// 秒針
.clock .hand.second {
width: 3px;
height: 45%;
background-color: red;
}
// 分針
.clock .hand.minute {
width: 7px;
height: 40%;
background-color: black;
}
// 時針
.clock .hand.hour {
width: 10px;
height: 35%;
background-color: black;
}

中心點

我們利用偽元素製作出一個中心圓,並利用transform: translate(-50%, -50%);使它置中。

1
2
3
4
5
6
7
8
9
10
11
12
.clock::after {
content: '';
position: absolute;
background-color: black;
z-index: 11; //要使它在最上面
width: 15px;
height: 15px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); //置中
border-radius: 50%;
}

時鐘置中

我們要使時鐘在瀏覽器置中,利用 flex 並給予高度,避免卷軸滾動,
在加入overflow:hidden;

1
2
3
4
5
6
7
8
body{
background-color: #D9AFD9;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh; //給予高度
overflow: hidden; //避免卷軸滾動
}

JavaScript

我們利用setInterval達到每秒更新,在創建一個setClock函數算份數,
分數要再加入當前的秒數。ex:12分12秒為 0.22份,
時數要再加入當前的分數,但時鐘為 12 小時一圈,故除以12,
在帶入setRotation函數,計算角度並把角度值加入到 css 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const hourHand=document.querySelector('.data-hour-hand');
const minuteHand=document.querySelector('.data-minute-hand');
const secondHand=document.querySelector('.data-second-hand');
setInterval(setClock,1000);

function setClock(params) {
const currentDate=new Date();
const secondsRatio= currentDate.getSeconds()/60
// 分數要再加入當前的秒數。ex:12分12秒為 0.22份
const minutesRatio= (secondsRatio+currentDate.getMinutes())/60
// 時數要再加入當前的分數,但時鐘為 12 小時一圈,故除以12
const hourRatio= (minutesRatio+currentDate.getHours())/12
setRotation(secondHand,secondsRatio)
setRotation(minuteHand,minutesRatio)
setRotation(hourHand,hourRatio)
}
// 角度
function setRotation(element,rotationRatio) {
element.style.setProperty('--rotation',rotationRatio*360)
}

See the Pen JavaScript Clock by 曾慶榮 (@jasonuse) on CodePen.

參考資料

https://www.youtube.com/watch?v=Ki0XXrlKlHY
https://ithelp.ithome.com.tw/articles/10136005

常見的非同步

setTimeoutXMLHttpRequest
這裡以 setTimeout 做示範:點擊按鈕,會兩數相加,兩秒後顯示結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let btn =document.querySelector('#btn');

// 監聽
btn.addEventListener('click',test)

// 非同步的程式
function delayAdd(n1,n2,sec) {
setTimeout(function name(params) {
return n1 + n2
},sec)
}
function test(params) {
let result=delayAdd(1,2,2000)
console.log(result)
}

解決方式

回呼函式 Callbacks

1
2
3
4
5
6
7
8
9
10
function delayAdd(n1,n2,sec,callback) {
setTimeout(function (params) {
callback(n1 + n2)
},sec)
}
function test(params) {
delayAdd(1,2,2000,function (result) {
console.log(result)
})
}

Promise

  • 創建 new Promise
  • reslove 對應 than
  • reject 對應 cath
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function delayAdd(n1,n2,sec) {
    return new Promise(function (reslove,reject) {
    setTimeout(function (params) {
    reslove(n1 + n2)
    },sec)
    })
    }
    function test(){
    let promise=delayAdd(1,2,2000)
    promise.then(function (result) {
    console.log(result)
    })
    }

    Promise.all

    1
    2
    3
    4
    5
    6
    7
    function test(params) {
    let promise1=delayAdd(1,2,2000)
    let promise2=delayAdd(3,4,3000)
    Promise.all([promise1,promise2]).then(function (results) {
    console.log(results)
    })
    }

    Async/Await

    本質還是 Promise,簡化 Promise 的語法糖。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function delayAdd(n1,n2,sec) {
    return new Promise(function (reslove,reject) {
    setTimeout(function (params) {
    reslove(n1 + n2)
    },sec)
    })
    }
    async function test(params) {
    let result=await delayAdd(1,2,2000)
    console.log(result)
    }

    範例

    本文範例程式碼
    XMLHttpRequest + Promise 範例

    參考文章

    https://www.youtube.com/watch?v=NOprCnnjHm0

AJAX

XMLHttpRequest

open 格式

onlaod

確定資料回傳,執行以下函式

1
2
3
4
5
6
7
8
9
function getData() {
let xhr=new XMLHttpRequest();
xhr.open('get',url,true);
xhr.send(null);
console.log(xhr.responseText)
xhr.onload=function () {
let data1 = JSON.parse(xhr.responseText)
return data=data1.ROOT.RECORD
}}

asios

1
2
3
4
5
6
7
// axios
function getData(){
axios.get(url)
.then(function (response) {
return data=response.data.ROOT.RECORD
});
}

jQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
function getData() {
$(document).ready(function () {
$.ajax({
type: "get",
url: url,
data: "data",
dataType: "json",
success: function (response) {
return data=response.ROOT.RECORD
}
});
});
}

範例

XMLHttpRequest

readyState 狀態說明

0 已經產生XMLHttpRequest,但還沒連結要撈的資料
1 用了open,但還沒把資料傳送過去
2 偵測到你有用send
3 loading
4 你撈到資料了,數據已經完全接收

open的格式

get 讀取資料
post 傳送資料到伺服器
true 同步
false 非同步

onload

確定有資料回傳了,可使用onload來取得回傳的值

get

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
let xhr = new XMLHttpRequest();
// readyState 狀態
// 0 已經產生XMLHttpRequest,但還沒連結要撈的資料
// 1 用了open,但還沒把資料傳送過去
// 2 偵測到你有用send
// 3 loading
// 4 你撈到資料了,數據已經完全接收

// open('格式(get:讀取資料)','要讀取的網址',true(同步)false(非同步))
xhr.open('get','https://hexschool.github.io/ajaxHomework/data.json',true);

// true 非同步, (多數使用true)
// false 同步

// 只要讀取資料的話,填null空值即可
xhr.send(null);

console.log(xhr.responseText);

// onload 當資料確定有回傳了,則開始執行以下function
xhr.onload = function() {
console.log(xhr.responseText);
let data = JSON.parse(xhr.responseText);
console.log(data);
}

post

1
2
3
4
5
6
7
8
let xhr=new XMLHttpRequest();
xhr.open('post','https://hexschool-tutorial.herokuapp.com/api/signup',true);

//傳送的格式
xhr.setRequestHeader("Content-type","application/json")
//xhr.setRequestHeader("Content-type","application/www-form-urlencoded")

xhr.send('email=abcde@gmail.com&password=1234');

Post example