兩行以上的text-overflow加「…」

text-overflow 的一般用法

一般來說 text-overflow 用在文字只放了一行的情況下,搭配 white-space: nowrap 做使用

.box 本行保留核貸與否、詮釋、裁決及終止本活動辦法之權利。本行保留核貸與否、詮釋、裁決及終止本活動辦法之權利。本行保留核貸與否、詮釋、裁決及終止本活動辦法之權利。本行保留核貸與否、詮釋、裁決及終止本活動辦法之權利。本行保留核貸與否、詮釋、裁決及終止本活動辦法之權利。本行保留核貸與否、詮釋、裁決及終止本活動辦法之權利。
.box
 width: 300px
 border: 1px solid #000
 text-overflow: ellipsis
 overflow: hidden
 white-space: nowrap

如此作法,雖然能夠做出超過寬度的文字加「…」效果,但也只能適用於一行的範圍

兩行以上的作法

.box
 width: 300px
 border: 1px solid #000
 display: -webkit-box
 text-overflow: ellipsis
 overflow: hidden
 -webkit-line-clamp: 2
 -webkit-box-orient: vertical

更改 webkit-line-clamp 的數值,自由調整要顯示的行數

display: -webkit-box 的各家瀏覽器相容度

ChromeFireFoxSafariIEAndroidiOS
✔︎✔︎✔︎×✔︎✔︎

CSS原生模組化工具 @namespace

結論

在HTML架構上@namespace只能拆出htmlsvg兩種模組(以檔案類別拆),雞肋到等於沒有用。本文探討@namespace的設計初衷並解釋為何他在HTML架構上會如此沒用

CSS模組化

h1 我是首頁標題
p 我是首頁文字

模組化設計的考量重點為確保程式碼的「獨特性」,比如說在用首頁的CSS,因為首頁很多UI元件只會出現在首頁而已,其他頁面不需要用到,也怕其他頁面會不小心混到,所以市面上常見的做法是給class特殊前綴,就會變成

h1.homepage-heading 我是首頁標題
p.homepage-text 我是首頁文字

諸如上圖,將首頁的所有特殊樣式class都冠上.homepage-確保其他頁面在用CSS時不會沾到首頁的東西

理想中的namespace

一個完整的<html>需要包含xmlns宣告,因此會長成這樣
(HTML5表示:xmlns不寫也沒關係)

<html lang="zh-tw" xmlns="http://www.w3.org/1999/xhtml">

然後,在xmlns宣告中定義namespace,取名為homepage

<html lang="zh-tw" xmlns:homepage="http://www.w3.org/1999/xhtml">

接下來在CSS使用@namepsace宣告

@namespace homepage url(http://www.w3.org/1999/xhtml);
homepage|body {
    background: pink !important;
}

寫到這裡,我們預期他在首頁時由於吃到homepage的命名空間所以套用粉紅色背景,但在其他頁面時保持白色背景

實際上的namespace

很遺憾的是,由於HTML不提供xmlns的命名空間(可以設定,但等於沒效)
所以homepage有寫等於沒寫,所有頁面的背景也都會套成粉紅色了

目前namespace可以應用的範圍僅侷限在htmlsvg的區分而已,但svg會寫成共用class的人也很少,所以幾乎沒法派上用場

Quote Tag的多國語適應解法

<q> 是 CSS 2.1 標準的古早標籤,本意是用來提示引言(與多語系客製化),同時也能活用他客制接頭詞、接尾詞的特性,解決多國語常見的空格問題

多國語的空格問題

這是常見的紅字強調排版,通常會這樣處理

<p>
  我是一段字,而且想要<span class="txt-red">強調</span>某些內容
</p>
.txt-red
 color: red

這樣做在方塊字體系中沒什麼問題(中文、日文、韓文等),但在以空格區別單字的字母體系中(如英文、德文)就會發生以下事故

看官可以看到紅色的字跟前後文連在一起的事故發生了(汗)

神奇的<q>標籤

讓我們用神奇<q>改寫一次這個範例

<p>
  我是一段字,而且想要<q class="txt-red">強調</q>某些內容
</p>

<q>在沒定義quote時會給出蝌蚪上下引號框住內容,但這題不需要蝌蚪引號所以我們給他拿掉

q
 quotes: '' ''

然後,在英語的環境下則需要用上下各一個半形空白框住紅色字
為了區別語系差異所以加上lang選取器

<p lang="zh-TW">
  我是一段字,而且想要<q class="txt-red">強調</q>某些內容
</p>
<p lang="en-US">
 I am a paragraph and want to<q class="txt-red">emphasize</q>something
</p>
.txt-red
 color: red
 
q
 &:lang(zh-TW)
  quotes: '' ''
 &:lang(en-US)
  quotes: ' ' ' '

實務上多語系的切換往往是一頁一頁換,所以lang通常都會掛在最上層的<html>然後讓他一層一層繼承下去(CSS這樣一樣可以選到)

<html lang="zh-TW">

手機modal背景防滑

手機的modal背景防滑一直是個千古難題,在PC裝置適用的overflow: hidden在手機上卻不管用。

一般來說主流的作法有幾個

  • 禁止touch事件(缺點:modal如果自帶拉霸也會變得不能滑)
  • modal外層那塊灰的用position: fixed處理(缺點:當同時遇到<input>小鍵盤時會衍生手機端的fixed靈肉分離問題)
  • 紀錄好現在的scrollTop參數,只要body一滑,就套上那個參數(原地滑動0的概念)(缺點:某些機型的手機可以從下方可以往上滑呼叫出小功能面板,那一塊戳久了還是可以拖動背景)
  • 手指一碰到背景灰的就關閉modal(缺點:modal本體戳久了還是可以穿透碰到背景)

上述4個做法都會自帶其他bug,沒一個能夠真正解決到問題。但其實此問題可以透過巧妙的html排法+CSS解決

HTML排法

.content-area
 ul
  li 內容內容
  li 內容內容(以下放大概50個li,讓這頁長出拉霸)

然後,在<head>裡面塞入一個<meta>

<meta name="viewport" content="width=device-width">

CSS內容防滑實作

html
 margin: 0
 height: 100%
 
body
 margin: 0
 height: 100%
 
.content-area
 height: 100%
 overflow: hidden
 -webkit-overflow-scrolling: auto

htmlbodymargin: 0是為了解除原生賦予的外距(他會造成一點點的空間可以滑)。

精髓是從htmlbody,再到.content-area的父子三層都套上height: 100%
這樣設定後原本在手機端失效的overflow: hidden就變有效了(雖然還要搭配-webkit-overflow-scrolling: auto使用)

經過上述設定後,就成功防止內容被滑動了
但是,這樣的設定如果手機滑到一半才在中途加overflow: hidden那些
頁面會被強制跳到開頭,沒辦法固定在先前滑到一半的地方

固定在先前滑動到的地方

.content-area下面在新增.content-wrapper
並且把overflow-hidden那些掛在.content-wrapper

.content-area
 .content-wrapper
  ul
   li 內容內容
   li 內容內容(以下放大概50個li,讓這頁長出拉霸)

然後,讓原先長出拉霸的<body>取消拉霸(掛上overflow: hidden
將拉霸設定到.content-wrapper上面去

html
 margin: 0
 height: 100%
 
body
 margin: 0
 height: 100%
 overflow: hidden
 
.content-area
 height: 100%
 // overflow: hidden
 // -webkit-overflow-scrolling: auto

.content-wrapper
 height: 100%
 overflow: scroll
 
.no-scroll .content-wrapper
 overflow: hidden
 -webkit-overflow-scrolling: auto

Framework7 Core

Framework7是手機特化型的一套框架,他排除了一些在開發手機時常踩到的雷,比如popup開啟時背景防止滑動、輸入框點選時小鍵盤會蓋到輸入框自己、fixed元素過多時在輸入框操作會有視差位移的大bug

如何使用

Framework7 Core是以Vanilla JS為環境的套件,所以不需要使用React / Vue,本介紹連Webpack也不包,只描述簡單引入的方法

.
├── index.html (首頁)
├── pages
│   ├── news.html (最新消息)
│   └── about.html (關於我們)
├── app.js (Framework7設定檔)
├── router.js (路徑設定檔)
└── style.css (自訂的CSS)

範例網頁的架構有三頁

首頁 (index.html)
  ├── 最新消息 (news.html)
  └── 關於我們 (about.html)

Framework7最難的地方就是設換頁路徑(因為他是SPA)
這邊先留意起來

換頁設定

參考官網教學,建立index.html

<!DOCTYPE html>
<html>
  <head>
    <!-- Required meta tags-->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <!-- Color theme for statusbar (Android only) -->
    <meta name="theme-color" content="#2196f3">
    <!-- Your app title -->
    <title>My App</title>
    <!-- Path to Framework7 Library Bundle CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/framework7/5.7.5/css/framework7.bundle.css">
    <!-- Path to your custom app styles-->
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <!-- App root element -->
    <div id="app">

      <!-- Your main view, should have "view-main" class -->
      <div class="view view-main">
        <!-- Initial Page, "data-name" contains page name -->
        <div data-name="home" class="page">

          <!-- Top Navbar -->
          <div class="navbar">
            <div class="navbar-bg"></div>
            <div class="navbar-inner">
              <div class="title">我是首頁</div>
            </div>
          </div>

          <!-- Bottom Toolbar -->
          <div class="toolbar toolbar-bottom">
            <div class="toolbar-inner">
              <!-- Toolbar links -->
              <a href="/news/" class="link">最新消息</a>
              <a href="/about/" class="link">關於我們</a>
            </div>
          </div>

          <!-- Scrollable page content -->
          <div class="page-content">
            <p>你好我是首頁</p>
          </div>
        </div>
      </div>
    </div>
    <!-- Path to Framework7 Library Bundle JS-->
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/framework7/5.7.5/js/framework7.bundle.js"></script>
    <script type="text/javascript" src="router.js"></script>
    <!-- Path to your app js-->
    <script type="text/javascript" src="app.js"></script>
  </body>
</html>

在Framework7裡面,換頁的路徑都要設定在app.js(或在本範例中額外拆出來放在router.js

app.js

var app = new Framework7({
    // App root element
    root: '#app',
    // App Name
    name: 'My App',
    // App id
    id: 'com.myapp.test',
    // Enable swipe panel
    theme: 'ios',
    // Add default routes
    routes: routes,
});

var mainView = app.views.create('.view-main');

theme可以填入iosmdaurora三種參數,建議填入不然他樣式在手機/電腦會長不一樣

router.js

var routes=[
    {
        //最新消息
        path: '/news/',
        url: 'pages/news.html',
        options: {
            transition: 'f7-parallax',
        },
    },
    {
        //關於我們
        path: '/about/',
        url: 'pages/about.html',
        options: {
            transition: 'f7-parallax',
        },
    },
]

換頁的動態寫在options裡面的transition,可提供的參數可以參考官網範例的Page Transitions這頁

最新消息 news.html

除了index.html以外的頁面html,只要放<div class="page">的內容就好

<div data-name="news" class="page">

    <!-- Top Navbar -->
    <div class="navbar">
        <div class="navbar-bg"></div>
        <div class="navbar-inner">
            <div class="title">最新消息</div>
        </div>
    </div>

    <!-- Scrollable page content -->
    <div class="page-content">
        <p>Coming Soon...</p>
    </div>
</div>

關於我們 about.html

<div data-name="about" class="page">

    <!-- Top Navbar -->
    <div class="navbar">
        <div class="navbar-bg"></div>
        <div class="navbar-inner">
            <div class="title">關於我們</div>
        </div>
    </div>

    <!-- Scrollable page content -->
    <div class="page-content">
        <p>本公司創立於西元100年</p>
    </div>
</div>

換頁demo

php除錯模式display_errors

透過啟用display_errors,php會把錯誤噴在網頁上,在開發模式下可以更快速的找出問題

檢視目前的display_errors狀態

開一個php檔案,並寫下

echo phpinfo();

在網頁中尋找display_errors這一項,狀態為On表示已開啟噴錯模式,狀態為Off代表關閉噴錯模式

修改php.ini設定檔

開啟以下檔案(作業系統:Mac)

/private/etc/php.ini

找出display_errors =這行,並且依照狀況在等號後面填入On / Off

修改後存檔,重啟Apache

Mac安裝php

開啟Apache

Mac內建php與Apache,只需要透過一些設定就能建置php開發環境
本指南適用於php版本5.6.30

sudo apachectl start

輸入完指令後,打開瀏覽器http://localhost,如果看到「It works!」即代表成功開啟了Apache

開啟httpd.conf檢視權限

Mac的php文件都放置在/Library/WebServer/Documents/之下
首先進去該路徑,並且在~/Documents/底下新增一個資料夾test/
接者在底下新增一個檔案index.php,隨意echo一些內容

回到瀏覽器,輸入http://localhost/test,網頁沒有如預期的出現「hey」的內容,而是出現了「Forbidden」的畫面

接者用文字編輯器(Sublime),開啟/etc/apache2/httpd.conf這個檔案
搜尋以下字串

#LoadModule php5_module libexec/apache2/libphp5.so

將最前方的#移除,使其成為

LoadModule php5_module libexec/apache2/libphp5.so

回到http://localhost/test,網頁能正常顯示了
※備註:php 7的話就是找這段LoadModule php7_module libexec/apache2/libphp7.so

MySQL與phpMyAdmin

Mac沒有附帶MySQL與phpMyAdmin,所以兩者需要去官網額外下載安裝

phpMyAdmin連線錯誤的解法

Mac作業系統沒辦法用localhost當主機名,必須改成用127.0.0.1當主機名
由於phpMyAdmin的config檔案裡預設是寫成連到localhost
所以必須手動進去config檔案,修改成127.0.0.1

config檔案

用文字編輯器(Sublime, VSCode)開啟以下檔案

/Library/WebServer/Documents/phpMyAdmin/libraries/config.default.php

找到檔案中的這一行

$cfg['Servers'][$i]['host'] = 'localhost';

並且改成

$cfg['Servers'][$i]['host'] = '127.0.0.1';

固網D-Link開External IP的方法

條件限制

  • 只能透過固網設定,行動網路因為會透過一層路由,所以沒法直接對外開放
  • 只能開localhost:80的內容,沒法開像是localhost:8080localhost:8000這樣複雜的port
    所以如果網站的port不是80的話,要先改成80

外部IP的功能

外部IP可以把自己這台電腦當作主機發佈給網際網路
所以網際網路上的任何人都能讀取自己電腦的localhost內容

步驟⑴ 設定固定IP

當電腦存取網路時,網路機器(有線網路 or WiFi)會給予該電腦一個IP
如果有另一台電腦也連上同一個網路,網路機器會在給那台電腦另一個IP

在未特別設定的情況下,IP會隨著每次接到網路時,而有不同的值
這時可以透過固定IP設定,強制佔用某個IP,讓該電腦每次連網時都使用同一個IP

查看目前的IP

打開系統偏好設定>網路,查看目前的浮動IP

記下來目前的IP是192.168.1.109
接著,打開D-Link的設定面板,網址列輸入192.168.1.1

【小提示】DSL-6740C的預設帳號密碼為
帳號:cht
密碼:chtvdsl

點選DCHP Server
進去後,可以看到EXISTING DCHP CLIENT列著一大排裝置
(下圖有經過修改所以只能看到一個裝置)
使用快速鍵Ctrl+F搜尋192.168.1.109(剛剛記起來的IP)
找到我們現在連的這台裝置

然後把IP Address、MAC Address分別填入下方的ADD STATIC IP ADDRESS裡面
填好後按Apply

這樣就完成了固定IP設定了,以後192.168.1.109這組IP就永遠給該電腦用了

步驟⑶ 開啟Virtual Server

上方選單先選ADVANCED,再從側選單選Virtual Server

依照上圖設定輸入

Enable Virtual Server Rules打勾
Name隨意取名
InterfaceWAN1_2
Internal startport80
Internal endport80
External startport80
External endport80
Protocol TypeBoth

大功告成

經過以上設定後,就打開了External Server
來試試看用External Server開專案的方法吧

⑴ 開localhost,設定port80

如果是python的話,要使用以下指令開啟localhost

python -m SimpleHTTPServer 80

如果是webpack的話,要去package.json,在webpack-dev-server後面加入port的描述,改好後重啟他

"dev-server": "webpack-dev-server --port 80"

如果是php的話,因為預設就是port: 80了,所以什麼都不用設定

⑵ 查看電腦的對外IP

開啟IP位置查詢網站,會看到一組數字為36.225.60.77
(每台電腦,每個時間進去看到的IP都不同)

然後用另一台裝置,連不同網路,網址列輸入36.225.60.77:80
可以神奇地看到原本應該是localhost的東西變成可以對外了

node版本管理工具

介紹node版本管理工具n,可以輕鬆的安裝(或切換)node版本
在切換node版本前,可以先透過指令查詢現有的node版號

node -v

回傳值即為現有node版號

安裝n工具

npm install -g n

假設今天要新裝12.0.0的版本,則執行以下程式

sudo n 12.0.0

因為執行了sudo所以要輸入電腦的開機密碼
執行後,如果電腦裡找不到12.0.0這個版,他會幫你重新裝
但如果電腦之前已經安裝12.0.0的話,他會幫你切換到那個版過去

尋找已經裝過的版本

可以透過資料夾找出曾經裝過的node版本
如果覺得node太多版了,也可以直接在資料夾裡面刪除他

以mac為例,node版本會放置在

/usr/local/n/versions/

這個地方不好找,以下說明具體的尋找方式

先開一個Finder出來

然後,點選上方選單的「前往」,再選「前往檔案夾」

然後,在檔案夾路徑輸入/usr/local/n/versions/

按下「前往」,就能到達該資料夾了

Gulp自動編譯增加CSS前綴

目的

自動編譯增加CSS前綴
手動編譯工具請參考這裏

檔案架構

.
├── css
│   └── styles.css
└── index.html

安裝node.js

去node.js官網下載node.js

安裝gulp

終端機cd到目前編輯的資料夾,輸入以下指令

npm init

不管他回啥都按enter
接著再輸入以下指令

npm install gulp -g

新增gulpfile.js

.
├── css
│   └── styles.css
├── index.html
└── gulpfile.js

再回到terminal輸入指令

npm install gulp gulp-autoprefixer --save-dev

編輯gulpfile.js

在gulpfile.js新增以下程式碼

var gulp=require('gulp');
var autoprefixer=require('gulp-autoprefixer');

gulp.task('style', function(){
    gulp.src('css/style.css')
        .pipe(autoprefixer())
        .pipe(gulp.dest('build'))
})

再到terminal輸入指令

gulp styles

編譯完成後,會出現build資料夾,裡面有styles.css,內容是自動完成前綴的CSS檔案
往後只要把html的<link>換成dist的CSS就能引入有完整前綴的檔案

<link ref="stylesheet" href="dist/styles.css" />

watch 存檔同時編譯

在gulpfile.js下方新增以下程式碼

gulp.task('watch', function(){
    gulp.watch('css/styles.css', ['styles']);
})

再回到terminal

gulp watch

就能看到系統會隨時監控style.css,只要一偵測到存檔,就會即時做編譯

Line Awesome

用法

跟FontAwesome4完全一樣,只要引入CSS,用i tag加上class就能使用 <head>引入CSS資源
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/line-awesome@1.3.0/dist/line-awesome/css/line-awesome.css" />
使用<i class="la">呼叫line awesome
i.la.la-exclamation

Line Aewsome取代Font Awesome

當目前的專案已經使用Font Awesome開發一段時間時,
全數手動更換class成Line Awesome會很花成本,
這時可以使用互換性的Line Awesome CSS,
只要替換CSS,不用更換class,就能全數更新成Line Awesome的icon 引入轉換用的CSS資源
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/line-awesome@1.3.0/dist/font-awesome-line-awesome/css/all.css" />
繼續使用Font Awesome的<i class="fa">
i.fa.fa-exclamation

參考資料

  • https://icons8.ru/line-awesome/howto
  • https://icons8.com/line-awesome

flex#11 應用:RWD排版與磚牆效果

RWD排版

利用flex-wrap與flex-grow / flex-basis達成不依靠@media做到RWD

.container
 .box.box1 one 😎
 .box.box2 two 🍕
 .box.box3 three 🍟
 .box.box4 four 👍
 .box.box5 five 👀
 .box.box6 six 💩
.box1
 background: #1abc9c
.box2
 background: #3498db
.box3
 background: #9b59b6
.box4
 background: #34495e
.box5
 background: #f1c40f
.box6
 background: #e67e22
 
.container
 display: flex
 border: 10px solid mistyrose
 flex-wrap: wrap
 
.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
 flex-basis: 300px //效力等同於width: 300px
 flex-grow: 1

Display
▼1236px以上

▼900px~1235px

▼600px~899px

▼599px以下

磚牆效果

利用flex-direction、flex-wrap、flex-grow達成類似磚牆的排法

.container
 display: flex
 border: 10px solid mistyrose
 flex-direction: column
 flex-wrap: wrap
 height: 100vh
 
.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
 flex-basis: 250px
 flex-grow: 1
 
.box3
 flex-grow: 5
 
.box4
 flex-basis: 100px

Display

one 😎
two 🍕
three 🍟
four 👍
five 👀
six 💩

flex#10 flex-basis, flex-grow, flex-shrink

解説

flexg屬性可以在拆分成以下3項

  • flex-basis:當flex item沒變形時,他的初始預設寬度(或是高度,當在cross-axis時)
  • flex-grow:當容器有多餘空間時,分配給flex item的空間比例
  • flex-shrink:當容器空間不足時,flex item犧牲體積的比例
.container
 .box.box1 one 😎
 .box.box2 two 🍕
.box1
 background: #1abc9c
.box2
 background: #3498db

.container
 display: flex
 border: 10px solid mistyrose
 
.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
one 😎
two 🍕

flex-basis

當flex item沒變形時,他的初始預設寬度(或是高度,當在cross-axis時)

.box1
 background: #1abc9c
.box2
 background: #3498db

.container
 display: flex
 border: 10px solid mistyrose
 
.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
 flex-basis: 400px
one 😎
two 🍕

設定為flex-basis: 400px後,每個item的寬度都變成400px了

flex-grow

套用以下設定

  • box1→flex-grow: 1
  • box2→flex-grow: 2

解説:假定容器寬度為1200px
在flex-basis的設定之下,box1、box2各寬400px
1200-400*2=300px,餘下的寬度為300px

flex-grow總數為1+2=3
300px/3=100px
因此

box1的寬度變為400px+100px=500px
box2的寬度變為400px+200px=600px

【注意】由於flex屬性同時包含了flex-grow / shrink
所以如果今天設定是

  • box1→flex: 1
  • box2→flex: 2

box1寬度會等於1/31200px=400px
box2寬度會等於2/3
1200px=800px

.box1
 background: #1abc9c
.box2
 background: #3498db

.container
 display: flex
 border: 10px solid mistyrose
 
.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
 flex-basis: 400px

.box1
 flex-grow: 1
.box2
 flex-grow: 2
one 😎
two 🍕

基於上述算式,box2只比box1大一點點而已。
並且,由於沒有指定flex-shrink
當容器寬度小於2者的flex-basis加總時(例:容器為700px),
2個flex-item會變成一樣大。

flex-shrink

flex-shrink只會在容器空間不夠時有作用
容器空間過多時不會起作用
(flex-grow的相反)

計算方式也是grow的相反

  • box1→flex-shrink: 1
  • box2→flex-shrink: 2

解説:假定容器寬度為500px
在flex-basis的設定之下,box1、box2各寬400px
500-400*2=-400px,不足400px

flex-grow總數為1+2=3
300px/3=100px
因此

box1的寬度變為400px-100px=300px
box2的寬度變為400px-200px=200px

.box1
 background: #1abc9c
.box2
 background: #3498db

.container
 display: flex
 border: 10px solid mistyrose
 
.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
 flex-basis: 400px

.box1
 flex-shrink: 1
.box2
 flex-shrink: 2
one 😎
two 🍕

在寬度足夠時看會沒差,需要把螢幕條小在看。
條小後,會看到box2比box1大一點點

總結

flex屬性集合了flex-grow, flex-shrink, flex-basis三種屬性
當我們使用flex: 1時,其實代表的是flex: 1 1 auto

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: auto

flex#9 flex

flex屬性

flex屬性,指定在flex item上面。描述當flex container有剩餘空間(或不夠的空間)時要如何分配
flex屬性僅代表等分比例原則,不是px也不是%

  • flex: 1→1份
  • flex: 2→2份(flex1的二倍)
  • flex: 3→3份(flex1的三倍)

未指定flex屬性時

.container
 .box.box1 one 😎
 .box.box2 two 🍕
 .box.box3 three 🍟
 .box.box4 four 👍
 .box.box5 five 👀
 .box.box6 six 💩
.box1
 background: #1abc9c
.box2
 background: #3498db
.box3
 background: #9b59b6
.box4
 background: #34495e
.box5
 background: #f1c40f
.box6
 background: #e67e22
.box7
 background: #e74c3c
.box8
 background: #bdc3c7
.box9
 background: #2ecc71
.box10
 background: #16a085
 
.container
 display: flex
 border: 10px solid mistyrose

.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
one 😎
two 🍕
three 🍟
four 👍
five 👀
six 💩

flex容器有剩餘空間,flex item沒有填滿剩餘空間

flex: 1

若指定所有flex item為1,則會

  • flex item會填補容器的空白
  • 所有flex item寬度變一樣
.box1
 background: #1abc9c
.box2
 background: #3498db
.box3
 background: #9b59b6
.box4
 background: #34495e
.box5
 background: #f1c40f
.box6
 background: #e67e22
.box7
 background: #e74c3c
.box8
 background: #bdc3c7
.box9
 background: #2ecc71
.box10
 background: #16a085
 
.container
 display: flex
 border: 10px solid mistyrose

.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
 flex: 1
one 😎
two 🍕
three 🍟
four 👍
five 👀
six 💩

flex item填滿容器,並且均等長

flex進階指定

套用以下指定

  • box1→flex: 1
  • box2→flex: 2
  • box3→flex: 1
  • box4→flex: 1
  • box5→flex: 3
  • box5→flex: 3
.box1
 background: #1abc9c
.box2
 background: #3498db
.box3
 background: #9b59b6
.box4
 background: #34495e
.box5
 background: #f1c40f
.box6
 background: #e67e22
.box7
 background: #e74c3c
.box8
 background: #bdc3c7
.box9
 background: #2ecc71
.box10
 background: #16a085
 
.container
 display: flex
 border: 10px solid mistyrose

.box
 color: white
 text-align: center
 text-shadow: 4px 4px 0 rgba(0,0,0,0.1)
 padding: 10px
 flex: 1

.box2
 flex: 2

.box5
 flex: 3
one 😎
two 🍕
three 🍟
four 👍
five 👀
six 💩

box1為1等分 ∴寬度為1/9
box2為2等分 ∴寬度為2/9
box3為1等分 ∴寬度為1/9
box4為1等分 ∴寬度為1/9
box5為3等分 ∴寬度為3/9
box6為1等分 ∴寬度為1/9

總長為1+2+1+1+3+1=9

Catalina 在根目錄(Macintosh HD)編輯的方法

關閉SIP

輸入以下指定確認SIP是否開啟

csrutil status

//回傳enabled表示SIP為開啟的撞狀態,需要關閉SIP
//回傳disabled表示SIP為關閉狀態,不需關閉SIP(跳過關閉SIP的步驟)

如SIP為enabled狀態,執行以下步驟關閉SIP

  1. 電腦關機
  2. 關機後,按住開機鍵,同時按住Cmd+R(按住直到螢幕出現apple logo後才放開)
  3. 進入復原模式,選擇使用者後,按下一步
  4. 選擇上方的「工具程式」,選「終端機」
  5. 輸入以下指令
    csrutil disable
  6. 重新開機

解除根目錄唯讀

  1. 終端機輸入csrutil status,確認回傳為disabled
  2. 輸入sudo mount -uw /
  3. 輸入sudo killall Finder
  4. 輸入cd /System/Library/Extensions
  5. 輸入sudo mv AppleThunderboltNHI.kext AppleThunderboltNHI_kext.bak

flex#8 align-self

說明

  • align-items指定全體的flex item沿著cross-axis對齊情況
  • align-self指定個別的flex item沿著cross-axis對齊情況(可以覆蓋align-items的指定)

align-self: auto (default)

預設值:不指定(繼承align-items)

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  align-items: baseline //指定整體

.box2
  padding-bottom: 200px
.box6
  padding-bottom: 0
.box9
  padding-bottom: 50px
  align-self: auto
1
2
3
4
5
6
7
8
9
10

align-self: flex-end

對齊cross-axis的底部
現在cross是由上至下↓↓↓,所以是靠下對齊

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  align-items: baseline //指定整體

.box2
  padding-bottom: 200px
.box6
  padding-bottom: 0
.box9
  padding-bottom: 50px
  align-self: flex-end
1
2
3
4
5
6
7
8
9
10

align-self: flex-end

對齊cross-axis的中部
現在cross是由上至下↓↓↓,所以是靠中對齊

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  align-items: baseline //指定整體

.box2
  padding-bottom: 200px
.box6
  padding-bottom: 0
.box9
  padding-bottom: 50px
  align-self: flex-end
1
2
3
4
5
6
7
8
9
10

align-self: baseline

對齊cross-axis的上部
現在cross是由上至下↓↓↓,所以是靠上對齊

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  align-items: baseline //指定整體

.box2
  padding-bottom: 200px
.box6
  padding-bottom: 0
.box9
  padding-bottom: 50px
  align-self: flex-end
1
2
3
4
5
6
7
8
9
10

flex#7 align-content

注意事項

  • flex-wrap需要為wrap
  • 只考慮與cross-axis的關係

範例

align-content: stretch (default)

效果:填滿cross-axis

.container
 .box.box1 1
 .box.box2 
  |2
  br
  |:)
 .box.box3 3
 .box.box4 4
 .box.box5 5
 .box.box6 6
 .box.box7 7
 .box.box8 8
 .box.box9 9
 .box.box10 10
.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 30%

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  flex-wrap: wrap
  align-content: stretch
1
2
🙂
3
4
5
6
7
8
9
10

align-content: flex-start

集中於上方

.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 30%

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  flex-wrap: wrap
  align-content: flex-start
1
2
🙂
3
4
5
6
7
8
9
10

align-content: flex-end

集中於下方

.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 30%

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  flex-wrap: wrap
  align-content: flex-end
1
2
🙂
3
4
5
6
7
8
9
10

align-content: space-between

平均分散

.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 30%

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  flex-wrap: wrap
  align-content: space-between
1
2
🙂
3
4
5
6
7
8
9
10

align-content: center

集中於中間

.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 30%

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  flex-wrap: wrap
  align-content: center
1
2
🙂
3
4
5
6
7
8
9
10

補充:讓「10」也可以水平置中

使用justify-content: center

.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 30%

.container
  display: flex
  border: 10px solid mistyrose
  height: 100vh
  flex-wrap: wrap
  align-content: center
  justify-content: center
1
2
🙂
3
4
5
6
7
8
9
10

flex#6 align-items

定義
justify-content會沿著cross-axis對齊

快速複習

  • 【預設】flex-direction: row,cross-axis為由由上至下↓↓↓
  • flex-direction: column,cross-axis為左至右→→→

重點

  • 外層wrapper要有足夠的空間(否則align-items沒空間發揮)
  • 每個item嘗試使用不同的高度(以便看出效果差異)

cross-axis為由上至下時(預設)

align-items: stretch(預設)

效果:填滿

.container
  display: flex
  border: 10px solid mistyrose
  height: 400px
  align-items: stretch

.box1
  font-size: 30px
 
.box3
  font-size: 150px
1
2
3
4
5
6
7
8
9
10

align-items: center

集中在中間

.container
  display: flex
  border: 10px solid mistyrose
  height: 400px
  align-items: center
1
2
3
4
5
6
7
8
9
10

align-items: flex-end

集中於下方

.container
  display: flex
  border: 10px solid mistyrose
  height: 400px
  align-items: flex-end
1
2
3
4
5
6
7
8
9
10

align-items: flex-start

集中於上方(貼齊上緣)

.container
  display: flex
  border: 10px solid mistyrose
  height: 400px
  align-items: flex-start
1
2
3
4
5
6
7
8
9
10

align-items: baseline

集中於上方(items自己置中對齊)

.container
  display: flex
  border: 10px solid mistyrose
  height: 400px
  align-items: baseline
1
2
3
4
5
6
7
8
9
10

cross-axis為由左至右時(flex-direction: column)

align-items: stretch(預設)

填滿

.box
  color: white
  font-size: 20px
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  align-items: stretch

.box1
  font-size: 30px
 
.box3
  font-size: 40px
 
.box4
  font-size: 50px
1
2
3
4
5
6
7
8
9
10

align-items: center

集中於中間

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  align-items: center
1
2
3
4
5
6
7
8
9
10

align-items: flex-end

集中於右側(貼齊右側)

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  align-items: flex-end
1
2
3
4
5
6
7
8
9
10

align-items: flex-start

集中於左側(貼齊左側)

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  align-items: flex-start
1
2
3
4
5
6
7
8
9
10

flex#5 justify-content

定義

justify-content會沿著main-axis對齊

快速複習

  • 【預設】flex-direction: row,main-axis為由左至右→→→
  • flex-direction: column,main-axis為由上至下↓↓↓

main-axis為由左至右的情況下(Default)

justify-content: flex-start (default)

items會擠在左邊

.container
  display: flex
  border: 10px solid mistyrose
  justify-content: flex-start
1
2
3
4
5
6
7
8
9
10

justify-content: flex-end

items會擠在右邊

.container
  display: flex
  border: 10px solid mistyrose
  justify-content: flex-end
1
2
3
4
5
6
7
8
9
10

justify-content: center

items會擠中間

.container
  display: flex
  border: 10px solid mistyrose
  justify-content: center
1
2
3
4
5
6
7
8
9
10

justify-content: space-between

第1個item擠在左邊,最後1個item擠在右邊,中間的items均分

.container
  display: flex
  border: 10px solid mistyrose
  justify-content: space-between
1
2
3
4
5
6
7
8
9
10

justify-content: space-around

手法近似於每個item得到一樣的margin-left跟margin-right。
由於第2-9個item的兩邊會重疊鄰居的margin,所以空隙為2倍。
第1個與第10個item的margin沒有鄰居加持,所以與邊界的空隙只有1倍。

.container
  display: flex
  border: 10px solid mistyrose
  justify-content: space-around
1
2
3
4
5
6
7
8
9
10

main-axis為由上至下的情況下(flex-direction: column)

2個注意事項

  • 需要指定min-height
  • 每個item不能給太高(加總高須低於min-height)

justify-content: flex-start

items都擠在上方

.box
  color: white
  font-size: 20px
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  min-height: 700px
  justify-content: flex-start
1
2
3
4
5
6
7
8
9
10

justify-content: flex-end

items都擠在下方

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  min-height: 700px
  justify-content: flex-end
1
2
3
4
5
6
7
8
9
10

justify-content: center

items都擠在中間(狀似孤島)

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  min-height: 700px
  justify-content: center
1
2
3
4
5
6
7
8
9
10

justify-content: space-between

首尾貼緊上下的均分

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  min-height: 700px
  justify-content: space-between
1
2
3
4
5
6
7
8
9
10

justify-content: space-around

上下等距的均分

.container
  display: flex
  border: 10px solid mistyrose
  flex-direction: column
  min-height: 700px
  justify-content: space-around
1
2
3
4
5
6
7
8
9
10

【GCP】introduction to APIs

目的

利用GCP建立一個API

建立JSON檔案

開啟GCP平台,打開Cloud Shell

輸入以下指令,編輯value.json

nano values.json

進入編輯模式後,貼上以下內容
※<YOUR_BUCKET_NAME>請換成自訂的任意名字,命名須遵守這篇文章

{  "name": "<YOUR_BUCKET_NAME>",
   "location": "us",
   "storageClass": "multi_regional"
}

結束編輯模式:Ctrl+X
確定儲存:Y
結束:Enter

取得API認證

OAuth 2.0 playground
選擇「Cloud Storage JSON API V1」
並選擇之下的「https://www.googleapis.com/auth/devstorage.full_control」

選好後,按下藍色按鈕「Authorize APIs」

接者,完成Step2跟Step3,並複製Access token序號

建立Cloud Storage Bucket

回到GCP Cloud Shell,輸入以下指令

ls

會看到回傳2份檔案:README-cloudshell.txt values.json

繼續輸入以下指令
※<YOUR_TOKEN>請貼上剛剛複製的OAuth Access token序號

export OAUTH2_TOKEN=<YOUR_TOKEN>

輸入以下指令
※<YOUR_PROJECT_ID>請貼上專案ID

export PROJECT_ID=<YOUR_PROJECT_ID>

執行以下程式,建立bucket

curl -X POST --data-binary @values.json \
    -H "Authorization: Bearer $OAUTH2_TOKEN" \
    -H "Content-Type: application/json" \
    "https://www.googleapis.com/storage/v1/b?project=$PROJECT_ID"

檢視建立好的Cloud Storage Bucket

選單→Storage→Browser

並點選專案名,就可看到建立好的Cloud Storage Bucket

上傳媒體檔案至API

  1. 找一張圖片,儲存為demo-image.pn
  2. 點選Cloud Sheel右上角的「…」menu處,選擇「upload file」
  3. 執行以下程式,找尋媒體路徑,並且複製回傳的絕對路徑
    realpath demo-image.png
  4. 執行以下指令
    ※<DEMO_IMAGE_PATH>請代換成剛剛複製貼上的路徑

    export OBJECT=<DEMO_IMAGE_PATH>
  5. 執行以下指令
    ※<YOUR_BUCKET>代換成這隻Bucket的名稱(value.json裡面的name)

    export BUCKET_NAME=<YOUR_BUCKET>
  6. 執行以下指令,上傳媒體至Cloud Storage Bucket
    ※<YOUR_BUCKET>代換成這隻Bucket的名稱(value.json裡面的name)

    curl -X POST --data-binary @$OBJECT \
        -H "Authorization: Bearer $OAUTH2_TOKEN" \
        -H "Content-Type: image/png" \
        "https://www.googleapis.com/upload/storage/v1/b/$BUCKET_NAME/o?uploadType=media&name=demo-image"
  7. 接者,可以在預覽主畫面上,看到上傳成功的媒體

flex#4 flex-order

說明

  • 預設值:order: 0
  • order的數字只具相對意義,order5的會排在order10的前面。但是當頁面上只存在一個order時,給定order1的效力等同於order50
  • 指定order後,畫面上的順序雖然被改變了,但是滑鼠反藍選取的順序依舊會照html排列,所以會看到選取的地方被中斷

order為正數時:會排到右邊

.container
  display: flex

.box
  flex: 1 // 填滿一行

.box3
  order: 1

.box7
  order: 2
1
2
3
4
5
6
7
8
9
10

order為負數時:會排到左邊

.container
  display: flex

.box
  flex: 1 // 填滿一行

.box3
  order: -1

.box7
  order: -2
1
2
3
4
5
6
7
8
9
10

flex#3 flex-wrap

flex-wrap: nowrap (預設)

不拆行(所有flex item擠在一排裡面)
即便指定了寬度,flex container也會以擠在一起為原則,盡量達成指定的寬度

.container
  display: flex
  border: 10px solid goldenrod
  flex-wrap: nowrap
 
.box
  color: white
  font-size: 100px
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 300px
1
2
3
4
5
6
7
8
9
10

flex-wrap: wrap

拆行(flex item超過寬度的話會掉下來)
會遵守width

.container
  display: flex
  border: 10px solid goldenrod
  flex-wrap: wrap
 
.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 300px
1
2
3
4
5
6
7
8
9
10

flex-wrap: wrap-reverse

反轉cross-axis
本來是由上到下↓↓↓
現變成由下到上↑↑↑

.container
  display: flex
  border: 10px solid goldenrod
  flex-wrap: wrap-reverse
 
.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  width: 300px
1
2
3
4
5
6
7
8
9
10

讓flex-item可以無縫填滿外層

  • 寬度算好
  • 使用box-sizing: border-box
.container
  display: flex
  border: 10px solid goldenrod
  flex-wrap: wrap
 
.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  box-sizing: border-box
  width: 33.33%
1
2
3
4
5
6
7
8
9
10

flex-item之間留個空隙

  • 空隙用margin指定
  • 寬度用calc算好
.container
  display: flex
  border: 10px solid goldenrod
  flex-wrap: wrap
 
.box
  color: white
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px
  margin: 10px
  box-sizing: border-box
  width: calc(33.33% - 20px)
1
2
3
4
5
6
7
8
9
10

flex#2 flex-direction

flex-direction: row (預設)

main axis: 由左至右 →→→
cross axis: 由上至下 ↓↓↓

.container
  display: flex
  border: 10px solid goldenrod
 flex-direction: row
1
2
3
4
5
6
7
8
9
10

flex-direction: column

main axis: 由上至下 ↓↓↓
cross axis: 由左至右 →→→

.container
  display: flex
  border: 10px solid goldenrod
 flex-direction: column
1
2
3
4
5
6
7
8
9
10

flex-direction: row-reverse

main axis: 由右至左 ←←←

.container
  display: flex
  border: 10px solid goldenrod
 flex-direction: row-reverse
1
2
3
4
5
6
7
8
9
10

flex-direction: column-reverse

main axis: 由下至上 ↑↑↑

.container
  display: flex
  border: 10px solid goldenrod
 flex-direction: column-reverse
1
2
3
4
5
6
7
8
9
10

flex#1 CSS flexbox

無使用flex時

.container
 .box.box1 1
 .box.box2 2
 .box.box3 3
 .box.box4 4
 .box.box5 5
 .box.box6 6
 .box.box7 7
 .box.box8 8
 .box.box9 9
 .box.box10 10
.box
  color: white
  font-size: 100px
  text-align: center
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.1)
  padding: 10px

.box1
  background: #1abc9c
.box2
  background: #3498db
.box3
  background: #9b59b6
.box4
  background: #34495e
.box5
  background: #f1c40f
.box6
  background: #e67e22
.box7
  background: #e74c3c
.box8
  background: #bdc3c7
.box9
  background: #2ecc71
.box10
  background: #16a085

Display

1
2
3
4
5
6
7
8
9
10

使用display: flex

外層元素會拉成一列
內層元素會變成flex-item

.container
  display: flex
  border: 10px solid goldenrod
1
2
3
4
5
6
7
8
9
10

使用display: inline-flex

外層元素會拉成一列,但不會全部填滿一列
內層元素會變成flex-item

.container
  display: inline-flex
  border: 10px solid goldenrod
1
2
3
4
5
6
7
8
9
10

JavaScript easing

說明

  1. 設定好自訂動畫函數,input固定間隔值的數字,動畫函數的output會是一個動畫曲線
  2. 用setInterval寫動畫執行函數。參數意義如下
    ele:element
    b:before(初期值)
    c:change(終點值)
    t:timing(目前的時間戳)
    d:duration(這個動畫總共要有幾秒)
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
.box
button(onclick="move()") click me
.box
 background: teal
 width: 200px
 height: 100px
 margin: 40px
//easing函數
function easeOut(t, d){
  return 1 - Math.pow(1-(t/d), 5);
}

function smoothScroll(ele, b){
  var c=-ele.scrollTop;
  var start = Date.now();
  var total = 1400; //毫秒
  var d = 1;

  timer = window.setInterval(function() {
    var t = (Date.now() - start) / total;
    var result = easeOut(t, d);
    var y = b + result * c;
    ele.scrollTop = y;
    if (t >= d) { 
      clearInterval(timer);
    }
  }, 16);
}

function move(){
 if(document.body.scrollTop!==0){
  //Safari
  smoothScroll(document.body, pageYOffset)
 }else{
  //Chrome, FF, IE
  smoothScroll(document.documentElement, pageYOffset)
 }
}

easing 函數介紹

easing out類(開始快,結束慢)

easing out by pow (指數)

function easeOutPow(t, d){
  return 1 - Math.pow(1-(t/d), 5);
}

easing  out by quad (2倍)

function easeOutQuad(t) { 
  return t*(2-t)
}

easing  out by cubic (3倍)

function easeOutCubic(t) { 
  return (--t)*t*t+1 
}

easing  out by quart (4倍)

function easeOutQuart(t) {
  return t*t*t*t
}

easing  out by quint (5倍)

function easeOutQuint(t) {
 return 1+(--t)*t*t*t*t
}

easing in類(開始慢,結束快)

easing in by pow (指數)

function easeInPow(t, d){
  return Math.pow((t/d), 5);
}

easing in by quad (2倍)

function easeInQuad(t) { 
  return t*t 
}

easing in by cubic (3倍)

function easeInCubic(t){ 
  return t*t*t 
}

easing in by quart (4倍)

function easeInQuart(t) { 
  return t*t*t*t 
}

easing in by quint (5倍)

function easeInQuint(t) { 
  return t*t*t*t*t 
}

ease in and out類(開始慢,中間快,結束慢)

easing in by pow (指數)

function easeInOutPow(t, d){
  return t<.5 ? Math.pow((t/d)*1.8, 6.5) : 1 - Math.pow((1-(t/d))*1.8, 6.5);
}

easing in by quad (2倍)

function easeInOutQuad(t) { 
  return t<.5 ? 2*t*t : -1+(4-2*t)*t 
}

easing in by cubic (3倍)

function easeInOutCubic(t) { 
  return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1
}

easing in by quart (4倍)

function easeInOutQuart(t) { 
  return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t 
}

easing in by quint (5倍)

function easeInOutQuint(t) { 
  return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t
}

參考資料

https://gist.github.com/gre/1650294

SMIL 動態控制

SVG <animate>的動態控制屬性

結束後保持變化後的狀態 fill=”freeze”

效果類似animation-fill-mode: forwards;

svg(viewbox="-50 -50 100 100")
 circle(cx=0,cy=0,r=10)
  animate(attributeName="r" dur="5s" values="10;40" fill="freeze")
svg
 width: 240px
 border: solid 2px

JS控制動畫開始

  1. 用參數控制SVG動畫一開始先停止
  2. 用JS method喚醒動畫
svg(viewbox="-50 -50 100 100")
 circle(cx=0,cy=0,r=10)
  animate#myAni(attributeName="r" dur="2s" values="10;30;10" begin="infinite" repeatCount="indefinite")
button(onclick="myAni.beginElement()") click me
svg
 width: 240px
 border: solid 2px

Safari bug:transform-origin不會生效

若使用CSS Animation Keyframes設定動畫的話,則transform-origin可以在safari正常使用的,
但在SMIL設定<animate>,則safari的transform-origin屬性不知為何沒有作用,會造成動畫扭曲

IIFEs

function statements

function statements在執行前會先透過Hoisting儲存在記憶體,
所以可以提前執行不會有問題。

greeting(); //可以被提前執行

function greeting(){
  console.log('Good');
}

function expressions

function expressions執行前不會進記憶體,
所以提前執行會出問題。

greeting(); //→不會理你

var greeting=function(){
 console.log('GOOD');
}

IIFEs

透過function expressions建立函式,並且立刻執行它。

沒用IIFEs時

var greeting=function(){
 console.log('GOOD'); 
}

//Console Panel
//Nothing

使用IIFEs時

var greeting=function(){
console.log('GOOD'); 
}();
//會立刻執行

//Console Panel
//GOOD

Form相關事件

簡介

埋在form裡面的<input type=”submit” />以及<button>,只要按下就會觸發form執行

reportvalidity()

手動觸發「檢核」提示

form#myForm
 input(type="text" required)
 input(type="submit")
 
button(onclick="myForm.reportValidity()") click me

checkValidity()

檢查表單檢核有沒有過,有通過的話回傳true,沒通過的話回傳false

form#myForm
 input(type="text" required)
 input(type="submit")
//Console Panel
myForm.checkValidity()

reset()

清空表單的input內容

form#myForm
 input(type="text" required)
 input(type="submit")
//Console Panel
myForm.reset() //清空內容

PHP陣列取值

name如果命名成xxx[]的話,傳到PHP會直接變成陣列
適合用在很多checkbox時

<form action="ttt.php" method="post">
    <input type="checkbox" name="user[]" value="草莓" id="strawberry" />
    <label for="strawberry">草莓</label>
    <input type="checkbox" name="user[]" value="橘子" id="orange" />
    <label for="orange">橘子</label>
    <input type="checkbox" name="user[]" value="香蕉" id="banana" />
    <label for="banana">香蕉</label>
    <br>
    <br>
    <input type="submit" name="your_submit" />
</form>

<?php 

if(!empty($_POST['your_submit'])){
  for ($i=0; $i < count($_POST['user']); $i++) { 
    echo $_POST['user'][$i].'<br>';
  }
}

?>

Reference and Copy

By Value: Number, String, Boolean

Number

let age=100;
let age2=age;
console.log(age, age2); //100 100

age=200;
console.log(age, age2); //200 100

String

let name='wes';
let name2=name;
console.log(name, name2); //'wes' 'wes'

name='wesley';
console.log(name, name2); //'wesley' 'wes'

Boolean

let isGood=true;
let isGood2=isGood;
console.log(isGood, isGood2); //true true

isGood=false;
console.log(isGood, isGood2); //false true

By Reference: Object, Array, Function

const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team = players;
console.log(players, team);
// ["Wes", "Sarah", "Ryan", "Poppy"]
// ["Wes", "Sarah", "Ryan", "Poppy"]

team[3]='Luis';
console.log(players, team);
// ["Wes", "Sarah", "Ryan", "Luis"]
// ["Wes", "Sarah", "Ryan", "Luis"]

//teams[3]的「team」是Reference,所以初始一但被更新,複製的也會被更新

Reference中的例外:該如何讓被複製的array不受初始變更的影響

方法1:slice

const fruits=['apple', 'banana', 'orange', 'grape'];
const fruits2=fruits.slice();

fruits[3]='mango';
console.log(fruits, fruits2);
// ["apple", "banana", "orange", "mango"]
// ["apple", "banana", "orange", "grape"]

方法2:New Array+contact

const fruits=['apple', 'banana', 'orange', 'grape'];
const fruits3=[].concat(fruits);

fruits[3]='mango';
console.log(fruits, fruits3);
// ["apple", "banana", "orange", "mango"]
// ["apple", "banana", "orange", "grape"]

方法3:ES6 Spread

const fruits=['apple', 'banana', 'orange', 'grape'];
const fruits4=[...fruits];

fruits[3]='mango';
console.log(fruits, fruits4);
// ["apple", "banana", "orange", "mango"]
// ["apple", "banana", "orange", "grape"]

方法4:Array.from

const fruits=['apple', 'banana', 'orange', 'grape'];
const fruits5=Array.from(fruits);

fruits[3]='mango';
console.log(fruits, fruits5);
// ["apple", "banana", "orange", "mango"]
// ["apple", "banana", "orange", "grape"]

多層物件中的淺拷貝

當1個物件他有很多層時

透過assign改第1層:不會改到初始物件

const wes={
 name: 'wes',
 age: 100,
 social: {
  twitter: '@webos',
  facebook: 'wesbos.developer'
 }
}

const dev=Object.assign({}, wes);
dev.name='wesley';
console.log(wes, dev);
// Object {
//   name: 'wes',
//   age: 100,
//   social: {
//    twitter: '@webos',
//    facebook: 'wesbos.developer'
//  }
// }
// Object {
//   name: 'wesley',
//   age: 100,
//   social: {
//    twitter: '@webos',
//    facebook: 'wesbos.developer'
//  }
// }

透過assign改第2層:會一起改到初始物件

const wes={
 name: 'wes',
 age: 100,
 social: {
  twitter: '@webos',
  facebook: 'wesbos.developer'
 }
}

const dev2=Object.assign({}, wes);
console.log(wes, dev);
// Object {
//   name: 'wesley',
//   age: 100,
//   social: {
//    twitter: '@coolman',
//    facebook: 'wesbos.developer'
//  }
// }
// Object {
//   name: 'wesley',
//   age: 100,
//   social: {
//    twitter: '@coolman',
//    facebook: 'wesbos.developer'
//  }
// }

原因:assign指能對第1層做深拷貝,其他第2層以下還是淺拷貝

多層物件的多重深拷貝(clone deep)

使用這招前請三思是否有必要多重深拷貝

const wes={
 name: 'wes',
 age: 100,
 social: {
  twitter: '@webos',
  facebook: 'wesbos.developer'
 }
}

const dev3=JSON.parse(JSON.stringify(wes));
dev3.social.twitter='@coolman';
console.log(wes, dev3);
// Object {
//   name: 'wesley',
//   age: 100,
//   social: {
//    twitter: '@wesbos',
//    facebook: 'wesbos.developer'
//  }
// }
// Object {
//   name: 'wesley',
//   age: 100,
//   social: {
//    twitter: '@coolman',
//    facebook: 'wesbos.developer'
//  }
// }

debounce

當執行某些event時(比如說scroll event),瀏覽器會執行很多次,這時可以用debounce function,限制他只能每xx毫秒執行一次,以節省效能

未使用debounce時

function goScroll(){
  console.count('gogogo');
}

window.addEventListener('scroll', goScroll);

//此時滑動螢幕捲軸,會發現後台被執行了很多次

使用debounce function減輕效能負擔

function debounce(func, wait = 20, immediate = true) {
  var timeout;
  return function() {
    var context = this, args = arguments;
    var later = function() {
      timeout = null;
      if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
  };
}

function goScroll(){
  console.count('gogogo');
}

window.addEventListener('scroll', debounce(goScroll, 50));
//debounce的第2個參數可以定義多久毫秒執行1次,若沒填的話此範例預設是每20秒執行1次。

【CSS】卡片翻面效果

<div class="photo">
    <div class="front">第一張卡片</div>
    <div class="back">第二張卡片</div>
</div>
.front {
  width: 300px;
  height: 350px;
  backface-visibility: hidden;
  transition: 0.6s;
  transform-style: preserve-3d;
  position: absolute;
  top: 0;
  left: 0;
  background-color: red;
}
.back {
  width: 300px;
  height: 350px;
  backface-visibility: hidden;
  transition: 0.6s;
  transform-style: preserve-3d;
  position: absolute;
  top: 0;
  left: 0;
  transform: rotateY(-180deg);
  background-color: green;
}
.photo:hover .back {
  transform: rotateY(0deg);
}
.photo:hover .front {
  transform: rotateY(180deg);
}

Class add and remove

此寫法為達成以下目的(常應用於選單收合)

  • A開啟時,點了B,打開B時要順便收起A
  • A開啟時,點了A,要收起A
.box apple
.box orange
.box grape
.box blue
.box gray
.box
 width: 200px
 height: 100px
 background-color: #eee
 text-align: center
 line-height: 100px
 transition-duration: 0.5s
 
.box.teal
 background-color: teal
 color: #fff
const boxes=document.querySelectorAll(".box");

boxes.forEach(box => box.addEventListener("click", checkall)
)

function checkall(){
 const nowTeal=document.querySelector(".teal");
 
 if(nowTeal){
  nowTeal.classList.remove("teal");
 }
 
 if(nowTeal==this){
  this.classList.remove("teal");
 }else{
   this.classList.add("teal");
 }

}

highchart 5角圖

<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-more.js"></script>
<div id="container"></div>
#container
 min-width: 400px
 max-width: 600px
 height: 400px
 margin: 0 auto
const spiderData=[
    {
        tag: '住宅',
        value: 5
    },
    {
        tag: '醫療',
        value: 2
    },
    {
        tag: '食品安全',
        value: 5
    },
    {
        tag: '教育',
        value: 1
    },
    {
        tag: '\t',
        value: 0
    },
]

const maxValue=Math.max(...spiderData.map(el => el.value));

Highcharts.chart('container', {

    chart: {
        polar: true,
        type: 'line',
        margin: [40, 0, 0, 0],
    },

    credits: {
        enabled: false
    },

    title: {
        text: ''
    },

    pane: {
        size: '100%'
    },

    exporting: { 
        enabled: false 
    },

    xAxis: {
        categories: spiderData.map((el) => {
            if(el.value===maxValue){
                return `<div style="color: red">${el.tag}<div>`;
            }else{
                return el.tag;
            }
        }),
        tickmarkPlacement: 'on',
        lineWidth: 0,
    },

    yAxis: {
        gridLineInterpolation: 'polygon',
        lineWidth: 0,
        min: 0,
        tickPositions: [0, 1, 2, 3, 4, 5],
        endOnTick: true,
    },

    tooltip: {
        enabled: false,
    },

    series: [{
        showInLegend: false,
        data: spiderData.map(el => el.value),
        pointPlacement: 'on',

    }]

});

CODEPEN

【CSS】字體設定

日文楷體

  • Mac會顯示「游教科書體」
  • Windows會顯示「標楷體」
h1 游教科書あっ!レポートレポート
h1
 font-family: YuKyo-Medium, STKaiti, DFKai-sb
 font-weight: normal

Display

游教科書あっ!レポートレポート

中文襯線體

  • Mac會顯示「宋體-繁」
  • Windows 英數字顯示「News Time Roman」,中文顯示「新細明體」
h1 這是一段中文字查詢頁面
h1
 font-family: 'Songti TC', 'Times New Roman', 'PMingLiu'
 font-weight: normal

Display

這是一段中文字查詢頁面

highchart 儀表圖

<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-more.js"></script>
<script src="https://code.highcharts.com/modules/solid-gauge.js"></script>
<div id="container-speed" class="container"></div>
.container {
  min-width: 310px; 
  max-width: 400px; 
  height: 300px; 
  margin: 0 auto;
}

tspan {
  font-size: inherit;
  color: inherit;
}

.verybad {
  color: #C00000;
  border-color: #C00000;
}

.bad {
  color: #E16100;
  border-color: #E16100;
}

.neutral {
  color: #FFC000;
  border-color: #FFC000;
}

.good {
  color: #D8Bf1A;
  border-color: #D8Bf1A;
}

.verygood {
  color: #9BBB59;
  border-color: #9BBB59;
}

.badge {
  font-size: 12px;
  border: 1px solid;
  border-radius: 3px;
  padding: 1px 2px;
  vertical-align: middle;
}

.voice-num {
  vertical-align: middle;
}
const testData=[43];

var speedOptions={

    chart:{
        type: 'gauge',
        plotBackgroundColor: null,
        plotBackgroundImage: null,
        plotBorderWidth: 0,
        plotShadow: false,
    },
        
    title: {
        text: '網路聲量'
    },
  
    pane: {
        startAngle: -90,
        endAngle: 90,
        background: null
    },
  
    exporting: { 
        enabled: false 
    },
  
    credits: {
        enabled: false
    },
        
    plotOptions: {
        gauge: {
            dataLabels: {
                borderColor: "none",
                useHTML: true,
                style: {
                    fontSize: "20px",
                }
                
            },
            dial: {
                baseLength: '0%',
                baseWidth: 10,
                radius: '100%',
                rearLength: '0%',
                topWidth: 1
            }
        }
    },
           
    // the value axis
    yAxis: {
        labels: {
            y: 10
        },
        tickPositions: [-50, 0, 50],
        minorTickLength: 0,
        min: -50,
        max: 50,
        plotBands: [{
            from: -50,
            to: 50,
            // color: '#C00000', // red
            color: {
                linearGradient: { x1: 0, x2: 1, y1: 0, y2: 0 },
                stops: [
                    [0.2, '#C00000'],
                    // #E16100
                    [0.5, '#FFC000'],
                    //#D8Bf1A
                    [0.7, '#9BBB59'],
                ]
            },
            thickness: '50%'
        }]
    },
    
    series: [{
        name: 'Speed',
        data: testData,
        dataLabels: {
            formatter: function(){
                let badge='';
                switch(true) {
                    case (this.y<-34):
                        //-35 -36 -37
                        badge='非常負面';
                        break;
                    case (this.y<-4):
                        //-34 -33 -32 ... -5
                        badge='較負面';
                        break;
                    case (this.y<5):
                        //-4 -3 ... 3 4
                        badge='中立';
                        break;
                    case (this.y<35):
                        //5 6 ... 33 34
                        badge='較正面';
                        break;
                    case (this.y<50):
                        badge='非常正面';
                }
            
                return `${this.y}分 ${badge}`
            }
        }
    }],
 
    tooltip: {
        enabled: false,
    },
 
};
Highcharts.chart('container-speed', speedOptions);

// -5 ~ +5 中立
// 5 ~ 35  較正面
// 35 ~ 50 非常正面

CODEPEN

 

Git ssh設定

mac https://qiita.com/shizuma/items/2b2f873a0034839e47ce

Mac

移動至ssh的資料夾
cd ~/.ssh
如果是首次使用ssh的話,.ssh folder裡面會沒有東西,要生成金鑰 (第二次以後使用的話,就不用再產生金鑰了)
$ssh-keygen -t rsa
下了這行↑指令後,Terminal會問3個問題,都打Enter就好 之後,copy電腦裡面的金鑰匙,並貼上Github 複製金鑰的指令:
pbcopy < ~/.ssh/id_rsa.pub

Win10

控制台 > 系統及安全性 > 系統 > 點選「進階系統設定」
進階 > 環境變數
選「Path」 > 編輯
新增 > 複製git/urs/bin的路徑貼上去
切換到終端機,找到.ssh資料夾
cd .ssh
產生ssh key
ssh-keygen
接下來不管Terminal回啥,按Enter,一共按3次Enter 之後,.ssh資料夾會產生「id_rsa」、「id_rsa.pub」2份檔案 複製id_rsa.pub的內容,輸入以下指令
cat id_rsa.pub
然後複製Terminal回傳的東西,並且貼上github

【JavaScript】appendChild

append虛擬元素時

#newBlock
 width: 100px
 height: 100px
 background: teal
const newBlock=document.createElement('div');
newBlock.id='newBlock';
document.appendChild(newBlock.cloneNode(true));
cloneNode代表無限複製,如果沒有用這個引數,他只會做出「搬移」(僅複製一次的效果)

append現有元素時

#nowBlock
hr
#newland
#nowBlock
 width: 100px
 height: 100px
 background: teal
newLand.appendChild(nowBlock.cloneNode(true))

localStorage

可以在瀏覽器存資料的方式,類似cookies。

  • 字串:可以存字串,讀取出來也是字串
  • 數字:可以存數字,但讀出來會變成字串
  • JSON:不能直接存JSON,要轉成string再存,讀出來也是字串化的JSON
const testJSON={
  color: 'red',
  count: 3,
  food: 'apple'
}

//寫localStorage一定要這樣寫,不能寫成localStorage.fruit
//要先轉成string後再存
localStorage['fruit']=JSON.stringify(testJSON);

//讀取
console.log(localStorage['fruit']) 
//"{"color":"red","count":3,"food":"apple"}"

//再轉成JSON繼續使用
console.log(JSON.parse(localStorage['fruit']))
//{color: 'red', count: 3, food: 'apple'}

closest

往parent找最近的元素。
children, sibling, parent’s sibling 都不會找到。

用法

<h1>我是標題</h1>
<table>
  <tbody>
    <tr id="myTr">
      <td>測試資料</td>
    </tr>
    <tr>
      <td>測試資料</td>
    </tr>
  </tbody>
</table>
myTr.closest('table') //回傳table這個元素
myTr.closest('td') //null //無法尋找子層
myTr.closest('tr') //null //無法尋找兄弟層
myTr.closest('h1') //null //無法尋找父層的兄弟

列印CSS

@media print指定於列印時觸發的CSS。

※chrome debug方法:
More tools→Rendering→Emulate CSS media→print

指定頁碼

@page {
  @bottom-center {
    content: "第" counter(page) "頁";
  }
}

列印邊界

使用@page可以指定預設列印邊界。但使用者仍可透過自訂列印邊界改掉它

@page {
  margin-bottom: 10mm;
}

Boostrap

Bootstrap內建有列印後隱藏的class

<div class="hidden-print">這段文字會被隱藏</div>

print()事件

print()事件可以呼叫列印行為

window.print()

元素位置與座標

clientHeight / clientWidth

※inline元素會回傳0
回傳的寬 / 高,不包含border, margin, scrollbar的width

offsetHeight / offsetWidth

※inline元素會回傳0
回傳的寬 / 高,不包含margin,會包含border, scrollbar的width

scrollHeight / scrollWidth

有scrollbar時:回傳被隱藏的內容的高 / 寬,不包含border, margin
無scrollbar時:scrollHeight == clientHeight

clientTop

元素內層與外層之間的距離 (就是border的寬度)

offsetTop

元素到網頁最上方的距離(越過瀏覽器視窗,到body最上面)

scrollTop

回傳元素捲了多少px
元素捲到最底部時:scrollTop + clientHeight = scrollHeight

getBoundingClientRect()

回傳値包含小數點,client三兄弟只能回傳整數
回傳目標元素到可視範圍之間的距離
※iOS Safari等手機瀏覽器,網址bar會時而隱藏時而浮出,會導致數值變來變去

用法:#element.getBoundingClientRect().top

※注意
在mobile裝置上使用getBoundingClientRect()時
需與scrollX / scrollY併用
因為,手機瀏覽器的網址列,有時會浮出,有時會消失。
getBoundingClientRect()不會計算到這個情況

參考文章:http://uhyo.hatenablog.com/entry/2017/03/15/130825

function getAbsolutePosition(elm){
  const {left, top} = elm.getBoundingClientRect();
  return {
    left: left + window.scrollX,
    top: top + window.scrollY,
  };
}

pointer-events: none

目的

讓元素可以被穿透

.box
 .back
 .front 
  |hover
  br
  |me
.box
 width: 300px
 height: 300px
 position: relative
 
.back
 background-color: teal
 width: 100px
 height: 100px
 &:hover
  background-color: darkgreen
 
 
.front
 background-color: skyblue
 width: 100px
 height: 100px
 position: absolute
 top: 0
 left: 50px
 opacity: 0.5
 pointer-events: none

Display

.
hover
me

【JavaScript】Item Picker

結構

  • 用select標籤做items,size的值給大一點讓item-box可以變長
  • 已選取item的背景色要用線性漸層
  • #mySelect.options[i] 可以回傳#mySelect的第i個選項
  • #mySelect.selectedIndex可以回傳目前被選取的序列
table.item-picker
 tr
  th 未加入功能清單
  th
  th 已加入功能清單
 tr
  td
   select(size="6" name="source1").pick-list
    option(value="0") 平台資訊審核
    option(value="1") 手續費審核
    option(value="2") 使用者管理
    option(value="3") 角色管理
  td
   button.add →
   br
   button.delete ←
  td
   select(size="6" name="target1").pick-list
    option(value="4") 平台資訊設定
    option(value="5") 手續費設定
    option(value="6") 顧客約定管理
$dark: #439E96
$turquoise: #50BBB6
$light: #E7F2F1
$black: #596D6C
$gray: #CCE0DE

*
 // border: 1px solid #000

// .iten-picker
//  overflow: auto
 
.add,.deduct
 display: block


 
table
 margin-top: 100px
 margin-right: auto
 margin-left: auto
 text-align: center
 
th
 color: $turquoise
 font-size: 16px
td
 select
  width: 150px
  font-size: 14px
  color: $black
  border-radius: 3px
  border-style: dashed
  &:focus
   outline: none
   box-shadow: 0px 1px 10px 0px #cce0de
  option
   padding: 2px 10px
   transition: color 0.5s
   &:checked
    background: $gray linear-gradient(0deg, $gray 0%, $gray 100%)
   &:hover
    color: $turquoise
  &:focus
   option
    &:checked
     background: $turquoise linear-gradient($turquoise 0%, $turquoise 100%)
     color: #fff
    
button
 transition-duration: 0.5s
 height: 30px
 width: 30px
 border-radius: 100%
 text-align: center
 border-color: $gray
 color: $turquoise
 outline: none
 
 &:hover
  background-color: $turquoise
  color: #fff
  border-color: $turquoise
const sourceItem=document.querySelector("select[name='source1']");
const targetItem=document.querySelector("select[name='target1']");
const addbtn=document.querySelector(".add");
const deletebtn=document.querySelector(".delete");

addbtn.addEventListener("click", function(){
 const theItem=sourceItem.options[sourceItem.selectedIndex]
 if(theItem){
  sourceItem.removeChild(theItem);
  targetItem.appendChild(theItem);
 }
 
});

deletebtn.addEventListener("click", function(){
 // targetItem.focus();
 const theItem=targetItem.options[targetItem.selectedIndex]
 if(theItem){
   targetItem.removeChild(theItem);
   sourceItem.appendChild(theItem);
 }

 
});

Display

CODEPEN

【CSS】px to rem

px rem
10px 0.625rem
11px 0.6875rem
12px 0.75rem
13px 0.8125rem
14px 0.875rem
15px 0.9375rem
16px 1rem
17px 1.0625rem
18px 1.125rem
19px 1.1875rem
20px 1.25rem
21px 1.3125rem
22px 1.375rem
23px 1.4375rem
24px 1.5rem
25px 1.5625rem
26px 1.625rem
27px 1.6875rem
28px 1.75rem
29px 1.8125rem
30px 1.875rem
31px 1.9375rem
32px 2rem
33px 2.0625rem
34px 2.125rem
35px 2.1875rem
36px 2.25rem
37px 2.3125rem
38px 2.375rem
39px 2.4375rem
40px 2.5rem
41px 2.5625rem
42px 2.625rem
43px 2.6875rem
44px 2.75rem
45px 2.8125rem
46px 2.875rem
47px 2.9375rem
48px 3rem
49px 3.0625rem
50px 3.125rem
51px 3.1875rem
52px 3.25rem
53px 3.3125rem
54px 3.375rem
55px 3.4375rem
56px 3.5rem
57px 3.5625rem
58px 3.625rem
59px 3.6875rem
60px 3.75rem
61px 3.8125rem
62px 3.875rem
63px 3.9375rem
64px 4rem

【Angular】service引入外部資料

外部資料檔案與接口

  • 建立mock-members.ts
import { Member } from './member';

//外部からアクセルできるようにexport
export const MEMBERS: Member[] = [
  {id: 11, name: '武山学'},
  {id: 12, name: '初音ミク'},
  {id: 13, name: '桐山零'},
  {id: 14, name: '三角竜之'},
  {id: 15, name: '羽生結弦'},
  {id: 16, name: '佐々木希'},
  {id: 17, name: '松崎はとり'},
  {id: 18, name: '半沢直樹'},
  {id: 19, name: '伊藤幸'},
  {id: 20, name: '桜田深雪'}
];
  • 建立member.service.ts
import { Injectable } from '@angular/core';
import { Member } from './member';
import { MEMBERS } from './mock-members';

//Componentから読み込むことをできるため、@Injectableを使う
@Injectable()
export class MemberService {
  //プロパティーやメソッドを定義する
  getMembers(): Member[]{
    return MEMBERS;
  }
}
//serviceで共通化の処理をできる。各ComponentからこのServiceを読み込むことで共通化を実現する

修改app.component.ts

import { Component } from '@angular/core';
import { Member } from './member';
import { MemberService } from './member.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ],
  providers: [ MemberService ]
  //app.componentでMemberServiceを使うという宣言
})

// プロパティーを定義する
export class AppComponent  {
  title='自社社員名簿';
  members: Member[]; 
  // Member型の配列にする
  selectedMember: Member;

  //AppComponentでMemberServiceを利用することができます
  constructor(private memberService: MemberService){
    // this.memberService = memberService;
    // 暗黙的に代入されます
  }
  //上記引用方法はDependency Injection(DI)です
  
  onSelected(member: Member): void {
    this.selectedMember=member;
  }

 //追加したMemberServiceを実行するメソッドを定義
  getMembers(): void {
    this.members=this.memberService.getMembers();
  }
}

Life Cycle

Life Cycle: 當元件被建構出來時、元件資料被變更時、元件被移除時會執行的callback

利用Life Cycle,當元件資料變更時呼叫service的getMembers()

引入interface、並執行在class上

import { Component, OnInit } from '@angular/core';

//...

export class AppComponent implements OnInit {

//...
//constructor( ...

ngOnInit():void {
    //constructorが実行された後、初期化時に呼ばれるcallback
    this.getMembers();
  }

【Angular】分割元件

定義元件

  • 在app資料夾建立新檔案,檔名為「member-detail.component.ts」
  • 在member-detail.component.ts新增以下內容
import {Component} from '@angular/core';
//angular coreからComponentというモジュールをインポートします

//Componentのメタデータを定義する
//<member-detail></member-detail>として使います
@Component({
  selector: 'member-detail',
  templateUrl: './member-detail.component.html' 
})
// ※@Component裡面不能放註解否則會壞掉
//HTMLテンプレートを定義する


export class MemberDetailComponent {

}

建立共用Class(將Class獨立出來)

  • 新增member.ts檔案
  • 在member.ts新增以下內容
export class Member {
  id: number;
  name: string;
}
//クラスをpublicにする
  • 在member-detail.component.ts的第二行新增以下內容,引入member.ts
import {Member} from './member';

引入外部Property

使用input。修改member-detail.component.ts為以下內容

import { Component, Input } from '@angular/core'; //データを渡す
//Input は外部のデータを参照するためのモジュールです
import { Member } from './member';

//<member-detail [member]="selectedHero"></member-detail>として使います
@Component({
  selector: 'member-detail',
  templateUrl: './member-detail.component.html'
})

export class MemberDetailComponent {
  //memberというプロパティーを定義、型をMemberに指定
  @Input() member: Member;
}

app.module.ts 新增以下內容

import { MemberDetailComponent } from './member-detail.component';
//引入MemberDetailComponent
declarations: [ ..., ..., MemberDetailComponent ],
//declarations也新增MemberDetailComponent

app.component.html最後一行新增以下內容

<member-detail [member]="selectedMember"></member-detail>

【Angular】事件綁定與ngIf

基本用法

<li (click)="onSelected(member)">
  • onclik → onclick事件
  • onSelected(member) → 綁定的handler函數,定義會寫在app.component.ts

Typescript定義

export class Member {
  id: number;
  name: string;
}

//Member クラスのようにプロパティーを強制する
const MEMBERS: Member[] = [
  {id: 11, name: '武山学'},
  {id: 12, name: '初音ミク'},
  {id: 13, name: '桐山零'},
  {id: 14, name: '三角竜之'},
  {id: 15, name: '羽生結弦'},
  {id: 16, name: '佐々木希'},
  {id: 17, name: '松崎はとり'},
  {id: 18, name: '半沢直樹'},
  {id: 19, name: '伊藤幸'},
  {id: 20, name: '桜田深雪'}
];

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})


export class AppComponent  {
  title='自社社員名簿';
  members = MEMBERS; 
  // 選択している社員を保存するため、保存用のプロパティーを定義する
  selectedMember: Member;
  // selectedMember只有在onselected觸發後,才會有值。一開始的時候沒有值
  onSelected(member: Member): void {
    this.selectedMember=member;
  }
}

HTML模板與Ngif

<h1>{{title}}</h1>
<h2>社員一覧</h2>

<ul class="members">
<!-- クリックイベントを設定する -->
  <li *ngFor="let member of members" (click)="onSelected(member)">
    <spanclass="badge">{{member.id}}</span>
    {{member.name}}
  </li>
</ul>
<!-- selectedMemberがまだ選択されてない時はエラーになる -->
<!-- selectedMemberがまだ選択された時のみ、以下のHTMLを表示する -->
<div *ngIf="selectedMember">
<!-- selectedMemberの値がある場合、コンテンツを表示する -->
  <h2>{{selectedMember.name}}</h2>
  <div>
    <label>id: </label>{{selectedMember.id}}
  </div>
  <div>
    <label>name: </label>
    <input type="text" [(ngModel)]="selectedMember.name"placeholder="名前">
  </div>
</div>

屬性綁定

當onclick觸發事件後,綁定class在元素上


<h1>{{title}}</h1>
<h2>社員一覧</h2>

<ul cl
ass="members">
<!-- クリックイベントを設定する -->
<li *ngFor="let member of members" (click)="onSelected(member)" [class.selected]="member === selectedMember">
<!-- []に書かれたものはHTMLの属性値の指定 -->
<!-- class名を指定しています。class="selected"を追加するための書き方 -->
<!-- ngForで回しているmember変数を、選択済みのselectedMemberになる時、class="selected"を追加する -->
<spanclass="badge">{{member.id}}</span>
{{member.name}}
</li>
</ul>
<div *ngIf="selectedMember">
<h2>{{selectedMember.name}}</h2>
<div>
<label>id: </label>{{selectedMember.id}}
</div>
<div>
<label>name: </label>
<inputtype="text" [(ngModel)]="selectedMember.name"placeholder="名前">
</div>
</div>

【Angular】Ngfor 迴圈

用途

可以迴圈生成HTML片段

物件設定

export class Member {
  id: number;
  name: string;
}

//Member クラスのようにプロパティーを強制する
const MEMBERS: Member[] = [
  {id: 11, name: '武山学'},
  {id: 12, name: '初音ミク'},
  {id: 13, name: '桐山零'},
  {id: 14, name: '三角竜之'},
  {id: 15, name: '羽生結弦'},
  {id: 16, name: '佐々木希'},
  {id: 17, name: '松崎はとり'},
  {id: 18, name: '半沢直樹'},
  {id: 19, name: '伊藤幸'},
  {id: 20, name: '桜田深雪'}
];

//資料的設定一定要在@Component上方,否則會噴錯
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})


// プロパティーを定義する
export class AppComponent  {
  title='自社社員名簿';
  members = MEMBERS; 
  
}

HTML模板

ngFor語法:let <自定義的名稱(可自訂)> of <變數名(固定)>

<h1>{{title}}</h1>
<h2>社員一覧</h2>
<ul>
  <li *ngFor="let member of members">
    <spanclass="badge">{{member.id}}</span>
    {{member.name}}
  </li>
</ul>

【Angular】雙向綁定

意涵

input元素⇄JS物件

  • input元素的值會由JS物件決定
  • 當input發生輸入,新的值會重設JS物件的值

定義Property

export class Member {
  id: number;
  name: string;
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})

// プロパティーを定義する
export class AppComponent  {
  title='自社社員名簿';
  // クラスを型として指定。IDとnameのプロパティーをオブジェクトに強制したい
  member: Member={
    id: 1,
    name: '山田太郎'
  }
}

設定HTML模板

<h1>{{title}}</h1>
<h2>{{member.name}}</h2>
<div>
  <label>id: </label>
  <span>{{member.id}}</span>
</div>
<div>
  <label>name: </label>
  <!-- <span>{{member.name}}</span> -->
  <!-- 雙向綁定 -->
  <input type="text" [(ngModel)]="member.name"placeholder="名前" />
</div>

Ng Model設定

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
//moduleを追加
import {FormsModule} from '@angular/forms';

import { AppComponent }  from './app.component';

@NgModule({
    imports:      [ 
        //FormsModuleを配列に追加
        BrowserModule, 
        FormsModule 
    ],
    declarations: [ 
        AppComponent 
    ],
    bootstrap:    [ 
        AppComponent 
    ]
  })
export class AppModule { }

【TypeScript】interface

用法

interface(接口),可以強制定義class的屬性名稱,數量,型別

定義class

//インターフェイスは定義だけで実装がない、クラスのようなものですものです
//クラスに特定な機能を実装を強制する目的で利用されます

interface Animal {
    name: string;//「name」というプロパティーをクラスに強制する
    legs: number;
    isCry: boolean; 
    cry(): void;
}


class Dog implements Animal {
    name: string='Maru';
    legs: number=4;
    isCry: boolean=true;
    cry(): void{
        if (this.isCry) {
            console.log('bow, wow!');
        }
    }
}

定義物件

interface Animal {
    name: string;
    legs: number;
    isCry: boolean; 
    cry(): void;
}

let Dog: Animal {
    name: 'Maru',
    legs: 4,
    isCry: true,
    cry() {
        console.log('bow, wow!');
    }
}

類似的方法

當覺得不需要使用interface小題大作,卻又想強制定義屬性時,可以使用以下的方法

let Dog: { name: string, legs: number } = {
 name: 'Maru',
 legs: 4,
}

【TypeScript】Generics 泛型

用法

透過參數客製化class,是TypeScript獨有的機能

傳統的寫法

class NumberStore{
    data: number;
}

class StringStore{
    data: string;
}

//型チェックができません
class AnyStore{
    data: any
}

使用泛型的寫法

//Tという文字は自由に名付けられる
class Store<T>{
    data: T;
    getStore(): T{
        return this.data;
    }
}

let stringStore=new Store();
stringStore.data = 'X';
console.log(stringStore.getStore()); //'X'

let numberStore = new Store();
numberStore.data = 100000;
console.log(numberStore.getStore()); //100000

函數的泛型

function hello<T>(keyword: T) {
 console.log(`Log: ${keyword}`);
}

hello<string>('Hello Ian!'); //'Log: Hello Ian!'
hello<number>(1000000000);//'Log: Hello 1000000000'

指定2個以上的泛型參數

class Component<T, U>{
    name: T;
    created: U;
}

let component = new Component<string, number>();
component.name = 'app';
component.created = Date.now();

【JavaScript】getter / setter

getter跟setter都是寫在物件底下的function方法

getter

給物件綁定訪問時(Access)可取得的函數

var obj = {
 log: ['example','test'],
 get latest() {
 if (this.log.length == 0) {
   return undefined;
  }else{
   return this.log[this.log.length - 1];
  }
 }
}

console.log(obj.log); //['example', 'test']
console.log(obj.latest); // "test"

setter

setter可以設定參數,然後讓物件回傳不同的值

//language 是一個物件
var language = {
  set current(name) {
    this.log.push(name);
  },
  log: []
}

language.current = 'EN'; //透過setter設定參數
console.log(language.log); // ['EN']

language.current = 'FA'; //透過setter設定參數
console.log(language.log); // ['EN', 'FA']

【WordPress】常用函數

bloginfo:網站的各項重要資料

 變數  意義
 bloginfo(‘name’)  網站名
 bloginfo(‘url’)  網址
 bloginfo(‘charset’)  網址
 bloginfo(‘description’)  網站描述
 bloginfo(‘stylesheet_url’)  style.css 檔案所在的路徑

wp_title() 網站當前頁面名稱

其他

 變數  意義
 language_attributes()  網站語系
 is_home()

post相關函數

※的函數需要在the_post()的底下才能作用

 變數  意義
 have_post()  回傳目前有沒有文章 回傳值:true / false
 the_post()  處理一件文章
 ※the_title()  文章標題
 ※the_permalink()  文章網址
 ※the_category(“, “)  顯示分類,各分類之間用「, 」分隔
 ※the_author()  顯示作者
 ※comments_popup_link (“無留言時”,  “一則留言時”, “多留言時”) 當跳出留言的功能啟動的話,導入留言視窗 若無啟動留言功能,則不顯示
 ※edit_post_link(“編輯連結的標題”, ” 標題前的字符”, ” 標題後的字符”) 以管理員或作者身分登入時才顯示。顯示一個可以用來編輯當前文章的編輯連結
 ※the_ID() 導入每篇文章的 ID

_e(“字串”)

讓中間的字串支援多國語系翻譯

posts_nav_link()

若超過10篇文章,則顯示分頁連結
posts_nav_link(“中間的字符”, “往前的文字”, “往後的文字”)

wp_list_cats()

文章分類顯示函數,裡面需填入字串。參數之間用【&】連結

  • sort_column=name→分類按字符順序排列
  • optioncount=1→顯示每個分類含有的文章數
  • hierarchial=0→不按照層式結構顯示子分類

 

 

 

 

【Angular】TypeScript class

用法

  • 列別名稱通常用「大駝峰式命名」
  • 函數與變數通常用「小駝峰式命名」
class 類別名 {
    訪問修飾符 屬性名: 型別=値;

    constructor(參數: 型別) {
        this.屬性名2='屬性名2'; //這行可以透過private 建構子參數省略不寫
    }

    函數(參數: 型別): 回傳値型別{
        console.log(this.屬性名='屬性名');
    }
}

訪問修飾符的種類

  1. public
    外部可存取,無指定訪問修飾符時為default設定
  2. protected
    原生類別或派生類別可存取
  3. private
    僅限原生類別可存取

※TypeScript最終還是JavaScript,指定訪問修飾符可以讓開發時檢查程式碼,如果硬要無視他,仍然可以執行。

範例

//定義類別
class Animal { 

    // 屬性名
    age: number = 10;

    // 建構子
    constructor(private isCry: boolean, private legs: number) {
        //這邊參數用private代表默認代入↓
        //this.isCry=isCry;
    }

    // 函數
    cry(): void {
        if (this.isCry) { 
            console.log(`legs: ${this.legs}`)
        }
    }

}

//使用類別
let dog = new Animal(true, 4);
//因為有定義函數
dog.cry(); //'legs: 4';

getter / setter

class Animal {
    private _age: number = 10;

    //取得上方private _age的值
    get age(): number {
        //除了取得值以外也可以進行處理
        // console.log(this._age);

        return this._age;
    }

    //要用跟getter相同的method名
    set age(value: number) {
        //除了取得值以外也可以進行處理
        // console.log(this._age);

        if (value < 0) { throw new Error('age cannot > 10')
        }
        this._age = value; //設定_age的值
    }
}

let dog = new Animal();
console.log(dog.age); //dog.age=10;
dog.age = 11; //set dog.age=11;

繼承

以舊有class為基礎,定義新的class

建構新的Method

class Animal{
    age: number = 10;

    constructor(private isCry: boolean, private legs: number) {
        
    }

    cry(): void {
        if(this.isCry) {
            alert('legs: ' + this.legs);
        }
    }
}

// あるクラスを基に別のクラスを定義することは継承と言います
// Animalクラスを継承してしてDogクラスを定義する
class Dog extends Animal {
    cry(): void {
        if (this.isCry) {
            alert('bow, wow!');
        }
    }
}

let dog = new Dog(true, 4);
dog.cry();

沿用舊有Method

class Animal{
    age: number = 10;

    constructor(private isCry: boolean, private legs: number) {
        
    }

    cry(): void {
        if(this.isCry) {
            alert('legs: ' + this.legs);
        }
    }
}

class Dog extends Animal {

    // constructor() {
           //也可以用super定義constructor的繼承
    //     super();
    // }

    cry(): void {

        // 親のクラスにアクセスためのOBJです
        super.cry();
        //処理を追加することも可能です
        console.log('hello!');
    }
}

let dog = new Dog(true, 4);
dog.cry();

 

 

【CSS】Color Name

REF: W3Schools

Color Name HEX Display
White #FFFFFF
Ivory #FFFFF0
LightYellow #FFFFE0
Yellow #FFFF00
Snow #FFFAFA
FloralWhite #FFFAF0
LemonChiffon #FFFACD
Cornsilk #FFF8DC
SeaShell #FFF5EE
LavenderBlush #FFF0F5
PapayaWhip #FFEFD5
BlanchedAlmond #FFEBCD
MistyRose #FFE4E1
Bisque #FFE4C4
Moccasin #FFE4B5
NavajoWhite #FFDEAD
PeachPuff #FFDAB9
Gold #FFD700
Pink #FFC0CB
LightPink #FFB6C1
Orange #FFA500
LightSalmon #FFA07A
DarkOrange #FF8C00
Coral #FF7F50
HotPink #FF69B4
Tomato #FF6347
OrangeRed #FF4500
DeepPink #FF1493
Fuchsia #FF00FF
Magenta #FF00FF
Red #FF0000
OldLace #FDF5E6
LightGoldenRodYellow #FAFAD2
Linen #FAF0E6
AntiqueWhite #FAEBD7
Salmon #FA8072
GhostWhite #F8F8FF
MintCream #F5FFFA
WhiteSmoke #F5F5F5
Beige #F5F5DC
Wheat #F5DEB3
SandyBrown #F4A460
Azure #F0FFFF
HoneyDew #F0FFF0
AliceBlue #F0F8FF
Khaki #F0E68C
LightCoral #F08080
PaleGoldenRod #EEE8AA
Violet #EE82EE
DarkSalmon #E9967A
Lavender #E6E6FA
LightCyan #E0FFFF
BurlyWood #DEB887
Plum #DDA0DD
Gainsboro #DCDCDC
Crimson #DC143C
PaleVioletRed #DB7093
GoldenRod #DAA520
Orchid #DA70D6
Thistle #D8BFD8
LightGray #D3D3D3
LightGrey #D3D3D3
Tan #D2B48C
Chocolate #D2691E
Peru #CD853F
IndianRed #CD5C5C
MediumVioletRed #C71585
Silver #C0C0C0
DarkKhaki #BDB76B
RosyBrown #BC8F8F
MediumOrchid #BA55D3
DarkGoldenRod #B8860B
FireBrick #B22222
PowderBlue #B0E0E6
LightSteelBlue #B0C4DE
PaleTurquoise #AFEEEE
GreenYellow #ADFF2F
LightBlue #ADD8E6
DarkGray #A9A9A9
DarkGrey #A9A9A9
Brown #A52A2A
Sienna #A0522D
YellowGreen #9ACD32
DarkOrchid #9932CC
PaleGreen #98FB98
DarkViolet #9400D3
MediumPurple #9370DB
LightGreen #90EE90
DarkSeaGreen #8FBC8F
SaddleBrown #8B4513
DarkMagenta #8B008B
DarkRed #8B0000
BlueViolet #8A2BE2
LightSkyBlue #87CEFA
SkyBlue #87CEEB
Gray #808080
Grey #808080
Olive #808000
Purple #800080
Maroon #800000
Aquamarine #7FFFD4
Chartreuse #7FFF00
LawnGreen #7CFC00
MediumSlateBlue #7B68EE
LightSlateGray #778899
LightSlateGrey #778899
SlateGray #708090
SlateGrey #708090
OliveDrab #6B8E23
SlateBlue #6A5ACD
DimGray #696969
DimGrey #696969
MediumAquaMarine #66CDAA
RebeccaPurple #663399
CornflowerBlue #6495ED
CadetBlue #5F9EA0
DarkOliveGreen #556B2F
Indigo #4B0082
MediumTurquoise #48D1CC
DarkSlateBlue #483D8B
SteelBlue #4682B4
RoyalBlue #4169E1
Turquoise #40E0D0
MediumSeaGreen #3CB371
LimeGreen #32CD32
DarkSlateGray #2F4F4F
DarkSlateGrey #2F4F4F
SeaGreen #2E8B57
ForestGreen #228B22
LightSeaGreen #20B2AA
DodgerBlue #1E90FF
MidnightBlue #191970
Aqua #00FFFF
Cyan #00FFFF
SpringGreen #00FF7F
Lime #00FF00
MediumSpringGreen #00FA9A
DarkTurquoise #00CED1
DeepSkyBlue #00BFFF
DarkCyan #008B8B
Teal #008080
Green #008000
DarkGreen #006400
Blue #0000FF
MediumBlue #0000CD
DarkBlue #00008B
Navy #000080
Black #000000

【CSS】outline

説明

outline位在border外面

.box
.box
 width: 300px
 height: 200px
 background-color: mistyrose
 border: 2px solid hotpink
 outline: 1px solid red

Display

.

FireFox的outline bug

如果裡面的東西超出box的話,outline也會被撐開
※其他瀏覽器不會有這個問題

.box
 .inner
.box
 width: 300px
 height: 200px
 background-color: mistyrose
 border: 2px solid hotpink
 outline: 1px solid red

.inner
 background-color: moccasin
 height: 300px
 width: 100px 

Display

解決方法

改用box-shadow做出outline的效果

.box
 .inner
.box
 width: 300px
 height: 200px
 background-color: mistyrose
 border: 2px solid hotpink
 // outline: 1px solid red
 box-shadow: 0 0 0 1px red
 
.inner
 background-color: moccasin
 height: 300px
 width: 100px

Display

.

【JavaScript】select元素相關操作

#mySelect.options

回傳選項清單

select#mySelect
 option apple
 option banana
 option grape
console.log(mySelect.options)
//回傳所有options的集合
//型態為HTMLOptionsCollection

#mySelect.selectedIndex

回傳目前被選取到的index

select#mySelect
 option apple
 option banana
 option grape
console.log(mySelect.selectedIndex)
//0

【Angular】TypeScript 函式與參數

參數與回傳値的型別定義

TypeScript的函式寫法有以下需求

  1. 必須指定參數型別
  2. 必須指定回傳値型別
function hello(name: string, number: number): string{ //這邊指定回傳值型別
 return `hello ${name} and ${number}`;
}

console.log(hello("blank", 100)); //hello blank and 100

如果函式沒有回傳値的話,回傳値的型別為「void」

function hello(name: string): void{
 return `hello ${name}`;
}

console.log(hello("blank")); //hello blank

省略指定參數

參數的冒號「:」前面加問號「?」表示這個參數可以省略

function hello(name?: string): string{
    return `hello ${name}`;
}

console.log(hello()); //hello undefine
//如果沒有設問號的話,使用hello()時,括號裡面一定要放參數。直接使用hello()的話會造成錯誤。

函式內定義參數

可以在定義函式時就指定好參數內容

function hello(name: string = 'Taro'): string{
    return `hello ${name}`;
}

console.log(hello()); //hello Taro

指定複數參數

參數前面多加「…」就可以指定數個同類型的參數

function sum(...values: number[]) { 
    let total = 0;
    for (let i: number = 0; i < values.length; i++) { 
        total += values[i];
    }

    return total;
}

console.log(sum(10, 100, 10000, 100000)); //110110

 

【Angular】TypeScript型別宣告

説明

宣告字串

var title: string = 'Angular';
//title = 10000 ←不可以把變數的型態改成數值

title = 'Angular4'; //如果用var宣告的話就可以改變文字內容

宣告數值

var num: number = 9;

宣告布林值

var isOpen: boolean = true;

宣告陣列

文字陣列

var array: string[] = ['name', 'email', 'body'];

數字陣列

var numbers: number[] = [1, 5, 7];

混合陣列

var data: [number, string, number] = [0, 'apple', 99]

宣告物件

var options: { [key: string]: string; } = {name: 'gggg'} 
//key可以自己換別的字

宣告任意型別

任意型別類似JavaScript的一般變數,宣告後可以改變他的型別。

var something: any = 'go'; //最初宣告為任意型別,給定型別為字串

something = 2000; //改變型別為數值
something = false; //改變型別為布林值

用了any的話,型別就不會被檢查。所以一般推薦少用為妙。

不給定型別也可以

不給型別的話,TypeScript會自己辨認型別。

var aaa = "string";

//aaa = 1000 //還是不能更動型別,所以這樣會error

宣告複數型別

var sample: string | number;

sample = "aaaaa";
sample = 100000; //這個變數可以代入字串或數值

型別轉換

function log(message: string) { 
 return `text content is ${message}`
}

log('ggg') //text content is ggg;

//轉換參數型別為任意型別
log(<any>1000); //text content is 1000

【Angular v4】環境架設

下載檔案

  1. Angular quickstart
  2. node.js

環境建置

  1. 安裝node.js
  2. 解壓縮quickstart
  3. 依序輸入這3個指令
    cd 指向quickstart路徑
    npm install
    npm start

檔案編輯

  • app/component.ts→定義元件
  • index.html→定義首頁
  • main.ts→啟動Web APP
  • style.css→定義全體Web APP的樣式

編輯元件

打開app/component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
   <h1>Hello {{name}}</h1> //變數用2個大括弧包起來
   <h2>text</h2>
  `,
  styles: [` //撰寫style
   h1 {
    color: red;
  }
  h2 {
    color: bluec;
   }
 `]
})

export class AppComponent{ 
 name = 'Fist Angular Application'; //定義變數內容 
}

【JavaScript】假的scroll-bar

步驟

  1. 隱藏原生的scroll-bar
  2. 用CSS做出一個假的bar
  3. 算出scroll-bar的長度
  4. 當發生滑動時,改變bar的top

隱藏原生的scroll-bar

  • 外層定義一個box,裡面要有假的bar與內容wrapper
  • 外層box設overflow: hidden
  • 內層wrapper設padding-left,這樣真正的scroll-bar就會超出外層box而被卡掉
.box#box
 .scroll-bar#bar
 .wrapper#wrapper
 img.items#items(src="https://ianchen.thisistap.com/wp-content/uploads/OOD.jpg" width="300px")
.box
 border: 1px solid #000
 width: 300px
 height: 300px
 overflow: hidden
  
.wrapper
 width: 100%
 height: 100%
 overflow-y: scroll
 padding-right: 28px

Display

用CSS做出一個假的bar

利用絕對定位將假bar定位在box的右上方

.box#box
 .scroll-bar#bar
 .wrapper#wrapper
.box
 border: 1px solid #000
 width: 300px
 height: 300px
 overflow: hidden
 position: relative
  
.wrapper
 width: 100%
 height: 100%
 overflow-y: scroll
 padding-right: 28px

.scroll-bar
 width: 8px
 height: 84px
 border-radius: 30px
 background-color: #7F7F7F
 position: absolute
 top: 0px
 right: 2.5px

Display

.

算出scroll-bar的長度

scroll-bar的兩種可能性

  1. 內容items的高度 小於 外層box的高度→不會出現scroll-bar
  2. 內容items的高度 大於 外層box的高度→出現scroll-bar
    scroll-bar的長度公式:外層box高度×(外層box高度÷內容items高度)
window.addEventListener("load", function(){
 
 if(items.clientHeight>=box.clientHeight){
  bar.style.display="none"; //如果內容items高度小於外層box,隱藏scroll-bar
 }else{
  bar.style.height=box.clientHeight * (box.clientHeight / items.clientHeight) + "px";
  //外層box高度×(外層box高度÷內容items高度)
 }
 
})

當發生滑動時,改變bar的top

  1. 當內容wrapper發生滑動→scroll事件
  2. 改變bar的top值→但是top值的MAX不是100
    計算top值的公式:目前items可視範圍離外層wrapper有多遠÷(items高度−wrapper高度)×(wrapper高度−bar高度)

 

 

 

customize scroll-bar

scrollbar height =
box.clientHeight * (box.clientHeight / content.offsetHeight)

【JavaScript】requestAnimationFrame

功能

楨率高的動畫函數。
setInterval最小時間單位是1毫秒,如果需要短時間大幅度的動態效果,則setInterval會顯得吃力。
這時就可以使用requestAnimationFrame

解説

requestAnimationFrame的callback函數的【第一個參數】會變成時間戳(=記錄當下的時間)

requestAnimationFrame(step);

function step(ggg){
 console.log(ggg); //會回傳當下的時間,但只會回傳一次
}

如果想要讓時間戳一直回傳過來,就要在callback函式裡面呼叫requestAnimationFrame

requestAnimationFrame(step);

function step(ggg){
 console.log(ggg);
 
 requestAnimationFrame(step); //會一直回傳時間戳過來
}

動畫的概念:

  1. 定義初始時間値,與變動時間値
  2. 紀錄毎次變動時間 減去 初始時間的相差値
  3. 相差値會越來越大
    requestAnimationFrame(step);
    
    //定義初始時間値
    let start=null;
    
    function step(ggg){
     if(start==null){
      start=ggg; //在一開始的時候帶入時間初始値
     }
     let passed=ggg-start; //算出現在時間與初始時間的相差値
     console.log(start); //會回傳初始時間値
     console.log(passed); //會回傳經過的時間
     requestAnimationFrame(step);
    }
  4. 用這個相差値帶入style的參數,就可以做出動態效果
    button(onclick="requestAnimationFrame(step)") click me
    #animatebox
    #animatebox
     width: 300px
     height: 200px
     background-color: teal
     opacity: 0
    let start=null;
    
    function step(ggg){
     if(start==null){
      start=ggg;
     }
     let passed=(ggg-start)/1000; //透明的値是0~1,用除數把數値縮小
     console.log(passed);
     
     animatebox.style.opacity=passed;
     
     if(passed<1){
      requestAnimationFrame(step); //如果透明値大於1停止動畫
     }
     
    }

範例

【JavaScript】event.target

説明

配合click / mouserover事件使用,指出目前的target元素

.outer(onclick="action()")
 .inner
 span click me
.outer
 background-color: #eee
 width: 200px
 height: 40px
 text-align: center
 .inner
 line-height: 40px
 display: inline-block
function action(){
 console.log(event.target);
}

應用例:收合選單

如果選單的標題與內容不小心寫在同一層級,
事件又想要綁在最外層的元素,
可以配合contains鎖定事件觸發的目標。

ul
 li(onclick="show(this)")
 .title 第一層選單 click me
 .content 這是第一層選單的內容
 br
 |內容...
 br
 |內容...
 br
 |內容...
 li(onclick="show(this)")
 .title 第二層選單 click me
 .content 這是第二層選單的內容
 br
 |內容...
 br
 |內容...
 br
 |內容...
ul
 display: inline-block
li
 list-style: none
 background-color: LightCyan 
 color: teal
 .title
 padding: 5px 10px
 .content
 background-color: Teal
 color: #fff
 padding: 5px 10px
 display: none
 .content.show
 display: block
function show(th){
 if(event.target.classList.contains("title")){
 th.children[1].classList.toggle("show");
 }
}

Display

  • 第一層選單 click me
    這是第一層選單的內容
    內容…
    內容…
    內容…
  • 第二層選單 click me
    這是第二層選單的內容
    內容…
    內容…
    內容…

【JavaScript】事件屬性的參數

説明

【事件屬性】
設定事件時,不使用事件綁定(addEventListener)
而將事件直接寫在tag的on事件屬性裡面

this的用法

  1. ATTR的括弧裡面要寫this(要寫完整的this,不能任意命名)
  2. function內要放第一個變數,變數名稱自定義,不能用this
.element(onclick="getAction(this)") click me
function getAction(th){
 console.log(th);
}

//"<div class='element' onclick='getAction(this)'>click me</div>"

※注意

如果在function裡面直接用this,會變成叫出window (global)

.element(onclick="getAction(this)") click me
function getAction(th){
 console.log(this);
}

//Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}

event的用法

  1. function內直接用event就好
.element(onclick="getAction()") click me
function getAction(m){
 console.log(event);
}

//MouseEvent {isTrusted: true, screenX: 47, screenY: 162, clientX: 47, clientY: 65, …}

【JavaScript】customize select-picker

説明

利用JS與CSS,自定義select-picker的style

  1. 用<select>跟<option>做出真選單
  2. 隱藏掉<select>(隱藏真選單)
    〔JS部分〕
  3. 讀取真選單<select>的內容,抓到後會是NodeList形式,給它轉換成陣列
  4. 換成陣列後,用mapping method,填充成<li>option內容</li>的形式
  5. 做出一個假的option wrapper / open-bar,然後把<li>們放進去

範例

引用函式庫:FontAwesome

.selector
 select
 option 台灣
 option 香港
 option 柬埔寨
 option 越南
 option 緬甸
 option 澳洲
 option 美國
 option 中國
 option 澳洲
 option 法國
 option 印尼
 option 英國
br
br
.selector
 select
 option good
 option apple
 option orange
 option red
 option beauty
 option view
 option nonono
 option yes
 option nice
.selector
 select
  display: none
 
.select-picker
 .open-bar
  width: 190px
  height: 30px
  line-height: 30px
  padding-left: 10px
  border-radius: 3px
  border: 1px solid gray
  user-select: none
  &:after
   content: "\f0d7"
   font-family: "FontAwesome"
   float: right
   margin-right: 10px
 
 .items
  max-width: 650px
  display: none
  border: 1px solid #000
  padding: 15px
  margin: 0
  position: absolute
  z-index: 999
  background-color: #fff  

 .item
  width: 120px
  display: inline-block
  height: 25px
  list-style: none
  padding: 5px 15px
 
 .items.items-show
  display: block
  display: inline-block
  
 .item.item-active
  background-color: #eee 
const selector=document.querySelectorAll(".selector");

selector.forEach(wrapper => {
 
 const RawData=wrapper.querySelector("select");
 const options=Array.from(RawData.children).map((item, n) => `<li class="item" onclick="select(${n}, this)">${item.textContent}</li>`).join("");
 
 //Create Content
 wrapper.innerHTML+=
  `<div class="select-picker">
    <div class="open-bar" onclick="showBlock(this)">${RawData.options[RawData.selectedIndex].text}</div>
     <ul class="items">
     ${options}
     </ul>
   </div>
  </div>`;

 
});


function showBlock(e){
 e.parentNode.children[1].classList.toggle("items-show");
}


function select(index, ele){
 
 const RawData=ele.closest(".selector").children[0];
 const itemActive=ele.closest(".selector").querySelectorAll(".item-active");
 
 
 if(itemActive.length>0) {
  itemActive[0].classList.remove("item-active");
 }
 RawData.selectedIndex=index;
 ele.parentNode.parentNode.children[0].textContent=RawData.options[RawData.selectedIndex].innerHTML;
 ele.parentNode.classList.remove("items-show");
 ele.classList.add("item-active");
}

Display

【HTML】disabled

説明

disabled是input相關屬性,可以讓input的內容無法編輯、修改。

  • 若text input裡面有文字(value內容),則使用者無法修改
  • 若radio button / checkbox預設為勾選,則使用者無法取消勾選
  • 若radio button / checkbox預設為不勾選,則使用者無法勾選他

範例

text

<input type="text" value="fixed text" disabled />

radio button

<input name="rd-dis" type="radio"checked disabled/>
<label>radio one</label>
<input name="rd-dis" type="radio" disabled/>
<label>radio two</label>




checkbox

<input type="checkbox" checked disabled/>
<label>checkbox one</label>
<input type="checkbox" disabled/>
<label>checkbox two</label>




select

<select disabled>
 <option>apple</option>
 <option>orange</option>
 <option>banana</option>
</select>

 

【JavaScript】JS的位置

説明

在HTML檔案中,JS不論是寫在頁面上,或是用檔案引入,都建議放在body後面。

理由:HTML文件的讀取方式是由上而下,如果寫在body前面的話,網頁還沒讀完JS就執行了,所以DOM會抓不到東西。

但若一定要寫在body前面的話,可以在JS內容外面加上一層load事件,讓網頁全部讀完後,再執行JS。

window.addEventListener("load", function(){

 //這邊放入JS內容。

});

【Bootstrap】selector

説明

用以Bootstrap為基底的函式庫做出美觀的selector picker

【必裝的函式庫】

jQuery.js(jQuery要放在最上面)
bootstrap.js
bootstrap.css
bootstrap-select.js
bootstrap-select.css

範例

用法:使用 <select> ,並且帶上 selectpicker 的 class

select.selectpicker
 option one
 option two
 option three

Display

CODEPEN

【CSS】漸層色塊

漸層原理説明

撰寫漸層時,要指定多組參數。每一組參數要有〖顏色〗與〖%數〗

.box
.box
 width: 500px
 height: 200px
 //left: 指定〖從左〗開始漸層 
 background: linear-gradient(left, DarkGreen 20%, MediumSeaGreen 70%)

▼示意圖

  • 第1組參數:〖起始色〗深綠色、〖%數〗20%
    0%~20% →深綠色純色區
  • 第2組參數:〖終色〗淺綠色、〖%數〗70%
    20%~70%→漸層區域
    70%~100%→淺綠色純色區

色塊

漸層區的定義是「第1個%」與「第2個%」之間的區塊,
所以如果「第2個%」指定跟「第1個%」一樣的數字,就無法產生漸層區,會形成色塊。

線性漸層色塊

.lg-box
.lg-box
 width: 300px
 height: 100px
 border: 1px solid #000
 background: linear-gradient(Left, Teal 50%, transparent 50%)

Display

圓形漸層色塊

圓形漸層如果參數指定的剛剛好一樣,會有顏色轉換處會有鋸齒產生,所以需要預留數%的漸層空間

.rd-box
.rd-box
 width: 200px
 height: 200px
 border-radius: 100%
 border: 1px solid #000
 background: radial-gradient(Teal 49.5%, transparent 50%)//預留0.5%的漸層空間

Display

【CSS】radio style

説明

自由撰寫radio input的樣式,但是保有 checked 的功能

【架構】

  • <label>裡面要有 2 個東西:<input type=”radio”>、<div class=”radio-widget”>
    ※這樣點到label裡面任何一處,<input type=”radio”>都會被觸發
  • radio-widget是假的radio,在〔.radio-widget〕做出勾選前的樣式
  • 在〔input[type=”radio”]:checked+.radio-widget〕撰寫勾選後的樣式

參考:input label 應用漸層色塊
衍生:checkbox style

範例

label.radio-label
 input(type="radio" name="rd" id="rd1" value="1" checked)
 .radio-widget
 | option 1

label.radio-label
 input(type="radio" name="rd" id="rd2" value="2" checked)
 .radio-widget
 | option 2

label.radio-label
 input(type="radio" name="rd" id="rd3" value="3" checked)
 .radio-widget
 | option 3
.radio-label
 margin: 0px 5px

 //原本的radio設不顯示
 input[type="radio"]
  display: none
 
 //這邊寫選取後的效果
 input[type="radio"]:checked+.radio-widget
  border-color: #50BBB6
  background: radial-gradient(#50BBB6 44%, transparent 50%)
  
.radio-widget
 width: 20px
 height: 20px
 border: 1px solid #eee
 border-radius: 100%
 position: relative
 margin-right: 5px
 background: radial-gradient(#eee 20%, #fff 25%)
 

.radio-widget, input
 display: inline-block
 vertical-align: top

【CSS】checkbox style

説明

自由撰寫checkbox的樣式,但是保有 checked 的功能

【架構】

  • <label>裡面要有 2 個東西:<input type=”checkbox”>、<div class=”checkbox-widget”>
    ※這樣點到label裡面任何一處,<input type=”checkbox”>都會被觸發
  • checkbox-widget是假的checkbox,在〔.checkbox-widget〕做出勾選前的樣式
  • 在〔input[type=”checkbox”]:checked+.checkbox-widget〕撰寫勾選後的樣式

參考:input label 應用
衍生:radio style

範例

label.checkbox-label
 input(type="checkbox" value="1")
 .checkbox-widget
 | checkbox 1

label.checkbox-label
 input(type="checkbox" value="1")
 .checkbox-widget
 | checkbox 2

label.checkbox-label
 input(type="checkbox" value="1")
 .checkbox-widget
 | checkbox 3
.checkbox-label
 .checkbox-widget, input
  display: inline-block
  vertical-align: top
 
 
 //在這裡刻出自訂樣式的checkbox
 .checkbox-widget 
  width: 20px
  height: 20px
  margin-right: 5px
  background-color: #fff
  border: 1px solid #eee
  border-radius: 4px
  text-align: center
  &:after
   content: "✔︎"
   color: #fff
 
 //原本的checkbox設不顯示
 input[type="checkbox"]
  display: none

 //選取後的樣式
 input[type="checkbox"]:checked+.checkbox-widget          
  background-color: #50BBB6 
  border-color: #50BBB6

【CSS】smooth

説明

滑順滾動的屬性,讓網頁內引導<href=”#id”>的動作可以滑順地跳下來

瀏覽器支援度→Can I Use …

範例

方法一:指定CSS

a(href="#title")
 button click me

//這邊填充很多個br
h1#title title
*
 scroll-behavior: smooth

Display

方法二:指定JavaScript

button(onclick="scrollIt()") click me
function scrollIt(){
 
 window.scroll({
  top: 0,
  left: 0,
  behavior: "smooth"
 });

 
}

Display

【CSS】border-collapse 表格消除空隙

情況

table 預設的 style 方格與方格之間會有空隙
(在table, td, th都有設定border的情況下會很明顯)

解決:border-collapse

table
 tr
  th Items
  th Price
 tr
  td Apple
  td $10
 tr
  td Orange
  td $15
table, td, th 
 border: 1px solid black
 
table
 border-collapse: collapse

Display

沒有加border-collapse的情況

Items Price
Apple $10
Orange $15

有使用border-collapse的情況

Items Price
Apple $10
Orange $15

【JavaScript】smalot-bootstrap-datetimepicker

説明

這是一套基於BS的函式庫,可以快速製作時間選擇器的小元件。

【必裝的函式庫】

屬性介紹

format【字串形式】

意義
p am / pm
P AM / PM
s 秒(沒有0開頭)
ss 秒(有0開頭)
i 分(沒有0開頭)
ii 分(有0開頭)
h 時(沒有0開頭,24小時制)
hh 時(有0開頭,24小時制)
H 時(沒有0開頭,12小時制)
HH 時(沒有0開頭,12小時制)
d 日期(沒有0開頭)
dd 日期(有0開頭)
m 數字月份(沒有0開頭)
mm 數字月份(有0開頭)
M 英文月份簡寫
MM 英文月份全名
yy 西元年(末二位數)
yyyy 西元年(四位數)

StartView / MinView 【數字形式】

在「只選擇月份」或是「只選擇日期」的情況下,預設的介面卻可以指定到某天的分鐘,會不太適合。
可以透過調整這2個參數達成更精準的控制

  • StartView:一開始選擇時間的介面〔預設:2〕
  • MinView:最後選擇時間的介面〔預設:0〕
意義
0 【選分鐘】hour view
1 【選小時】day view
2 【選日期】month view
3 【選月份】year view
4 【選年份】10-year view

範例:選擇日期

.input-append.date.form_datetime(data-view='hour')
 input(size='16', type='text', value='', readonly='', placeholder='2017-11-15 16:35')
 span.add-on
 i.fa.fa-calendar
$(".form_datetime[data-view='hour']").datetimepicker({
 format: "yyyy-mm-dd", //這邊可以自訂時間表示的格式
 autoclose: true,
 minView: 3
 });

範例:選擇時與分

.input-append.date.form_datetime(data-view='hour')
 input(size='16', type='text', value='', readonly='', placeholder='2017-11-15 16:35')
 span.add-on
 i.fa.fa-calendar
$(".form_datetime[data-view='hour']").datetimepicker({
 format: "yyyy-mm-dd hh:ii",
 autoclose: true
 });

範例:選擇月份

.input-append.date.form_datetime(data-view='year')
 input(size='16', type='text', value='', readonly='', placeholder='2017-11')
 span.add-on
 i.fa.fa-calendar
$(".form_datetime[data-view='year']").datetimepicker({
 format: "yyyy-mm",
 autoclose: true,
 startView: 4,
 minView: 3
 });

Display

CODEPEN

 

 

【CSS】height transition

説明

CSS展開動畫:點選收起來的選單→觸發動作開啓選單

開啓狀態下的選單區塊,高度寫inherit的話,無法完成動態效果,所以要用max-height的方式完成

.outer 收合的選單 (hover me)
 .inner
  p 選單内部
  p 選單内部
  p 選單内部
.outer
  background-color: #E7F2F1
  display: inline-block
  padding: 10px
  &:hover
   .inner
   max-height: 150px
   //不用精準設定到元素高度,元素只會展開到他自己的高度。數字可以抓大一點,數字越大展開的速率越快。
 
.inner
 background-color: #439E96
 padding: 0px 10px
 margin: 5px
 max-height: 0
 overflow: hidden
 transition-duration: 0.5s

Display

收合的選單 (hover me)

選單内部

選單内部

選單内部

【JavaScript】load HTML 外部載入HTML

用途

在一個HTML檔案的部分區域引入其他外部HTML

▼示意圖

上圖使用了2個外部HTML,檔案結構如下

  • 主頁面〔index.html〕
  • 主選單(橫條選單)〔main-nav.html〕
  • 側選單(直條選單)〔verti-nav.html〕

在主頁面〔index.html〕頁面中,引入2個選單HTML〔main-nav.html〕&〔verti-nav.html〕

native JS 方法

const xhr=new XMLHttpRequest();
xhr.open("GET", "../verti-navs.html", true);
xhr.send();

xhr.onreadystatechange=function(){
 if(xhr.readyState==4 && xhr.status==200){

    document.querySelectorAll(".verti-nav")[0].innerHTML = xhr.responseText;

    }
};

jQuery 方法:load

$(".verti-nav").load("verti-navs.html");

觀看環境

外部載入HTML、ajax等做法會涉及到檔案之間的溝通,所以無法普通地在本機電腦打開觀看。需要透過以下的環境打開檔案。

觀看環境一:localhost

將電腦設定成虛擬主機(localhost),有以下幾種方法
  • 安裝XAMPP
  • 使用Webpack+Node.js
  • 利用終端機內建的python設定localhost環境

以下介紹用終端機設定localhost環境的方法

cd /Users/Desktop/web 
#指定要打開的資料夾

python -m SimpleHTTPServer 9099

輸入完畢後開啟瀏覽器,打開網址:http://localhost:9099
就會跳出index.html的頁面

觀看環境二:FireFox

FireFox也支援檔案之間的溝通。
但是,必須要先從index.html打開,再用index.html連到其他的頁面。否則FF會抓不到根目錄。

JS的撰寫方式

針對load進來的選單HTML撰寫JS時,有時會發生選單相關JS沒有被執行到的狀況。(特別在localhost環境下觀看時)

原因:JS的語言特性會偷跑,如果所有HTML的JS都要統一管理成同一份檔案,必須考慮到瀏覽器載入HTML的順序,再安排JS的撰寫位置。

一般來說,瀏覽器載入檔案的順序會是這樣:

  1. 主HTML檔案
  2. JS檔案
  3. load進來的選單HTML
    (因為選單load進來的動作是由JS控制,所以JS一定會比選單HTML早一步執行)

※因為JS檔案(內含選單相關JS)在選單HTML存在前就被載入了,所以當JS執行的時候並不存在選單HTML,導致JS的程式無效。

解決:

  1. 將選單相關JS寫在XMLHttpRequest的動作裡面。
  2. 並且增加一條判斷式:如果response存在,才執行選單JS
const xhr=new XMLHttpRequest();
xhr.open("GET", "../verti-navs.html", true);
xhr.send();

xhr.onreadystatechange=function(){
 if(xhr.readyState==4 && xhr.status==200){

    document.querySelectorAll(".verti-nav")[0].innerHTML = xhr.responseText;
    if(xhr.responseText){
       //這邊撰寫選單相關JS

     }
  }
};

【JavaScript】change event

説明

<input>狀態改變時→觸發事件
【例】下拉式選單:變更選項→觸發事件
【例】核取方塊:勾選 / 取消勾選→觸發事件

label Choose a color
 select#sel(name="colors" onchange="selShow()")
 option(value="Nothing") Select One...
 option(value="red") RED
 option(value="blue") BLUE
 option(value="green") GREEN 
#changeStatus
function selShow(){
 changeStatus.textContent=`You selected ${sel.value}`
}

Display

【JavaScript】transitionend event

説明

transitionend event:漸變結束後→觸發事件

#box(ontransitionend="transShow()") hover me
p#transStatus
#box
 border: 1px solid #000
 display: inline-block
 transition-duration: 0.5s
 &:hover
  font-size: 30px
function transShow(){
 transStatus.textContent="Transition is Done"
}

Display

hover me

【JavaScript】keydown and keyup event

説明

  • keydown event:鍵盤按下時→觸發動作
  • keyup event:鍵盤按下後鬆開→觸發動作

 

p Press any key
p#pressStatus
window.addEventListener("keydown", function(e){
 pressStatus.textContent=`Key code of ${e.keyCode} is pressed`;
})

window.addEventListener("keyup", function(){
 pressStatus.textContent=`Now you left the key`
})

Display

Press any key

【JavaScript】focus and blur event

説明

  1. focus event:聚焦(點選到)該元素→觸發事件
    ※可以用滑鼠點選觸發focus,某些情況下也可以用鍵盤選取(Tab鍵),觸發focus
  2. blur event:focus的相反,離開該元素後→觸發事件
input#getText(type="text")
button(onclick="getFocus()") Get focus
button(onclick="loseFocus()") Lose focus
function getFocus(){
 getText.focus();
}

function loseFocus(){
 getText.blur();
}

Display

【JavaScript】mouse event series

説明

  • mousedown:滑鼠按下去的瞬間→觸發事件
  • mouseout:滑鼠離開→觸發事件
  • mouseup:滑鼠按下、離開的瞬間→觸發事件

 

p Try to draw it ↓↓
canvas#canvas(width="300", height="300")
#canvas
 width: 300px
 height: 300px
 border: 1px solid #000
let isDrawing=false;
let lastX=0;
let lastY=0;
const ctx=canvas.getContext("2d");

function draw(e){
 if(!isDrawing){
  return;
 }

 ctx.beginPath();
 ctx.moveTo(lastX, lastY);
 ctx.lineTo(e.offsetX, e.offsetY);
 ctx.strokeStyle="#000000";
 
 [lastX, lastY]=[e.offsetX, e.offsetY];
 
 ctx.stroke();
}

//滑鼠按下→開始畫圖,計算起始座標
canvas.addEventListener("mousedown", (e) => {
 isDrawing = true;
 [lastX, lastY]=[e.offsetX, e.offsetY];
});

//滑鼠移動→繼續畫圖
canvas.addEventListener("mousemove", draw);
//滑鼠放掉→停止畫圖
canvas.addEventListener("mouseup", () => isDrawing = false);
//滑鼠離開→停止畫圖
canvas.addEventListener("mouseout", () => isDrawing = false);

Display
Try to draw it ↓↓

【JavaScript】mousemove event

説明

滑鼠移動→觸發事件

#screenBox Hover me
#locate
#screenBox
 width: 300px
 height: 200px
 background-color: #eee
 text-align: center
 line-height: 200px
 color: rgba(0,0,0,0.5)
screenBox.addEventListener("mousemove", function(e){
 locate.textContent=`Mouse locate is (${e.offsetX}, ${e.offsetY})`
})

Display

Hover me

【JavaScript】Parent, Children, Silbing

選取母元素

#parentBox
 .child A Box
 .child B Box
#parentBox
 width: 500px
 height: 500px
 background-color: #eee
 text-align: center
 line-height: 500px

 
.child
 width: 100px
 height: 100px
 display: inline-block
 line-height: 100px
 background-color: #beddcd
const childs=document.querySelectorAll(".child");
childs[0].parentNode; //會選到parentBox

選取子元素

※HTML, CSS 沿用上例

children Method 是 NodeList 形式(即便只有一個子元素),所以要向使用 querySelectorAll 的時候一樣,指定 index

parentBox.children[0]; //會選到A Box

選取相鄰元素

※HTML, CSS 沿用上例

Native JavaScript 沒有辦法直接指定相鄰元素(jQuery 的話可以使用sibling()),所以只能先回到母元素→再指定子元素

const childs=document.querySelectorAll(".child");

childs[0].parentNode.children[1]; //會選到B Box

【Git】忽略檔案

目的

更新版本的時候,讓某些檔案不要被更新上去(忽略更新)

方法

在本地數據庫新增【.gitignore】檔案。
之後用文字編輯器(Sublime / notepad)開啟【.gitignore】,
並且輸入指令。

※輸入gitignore忽略指令的方法:
①用文字編輯器打開【.gitignore】
②輸入指令
③存檔

$ touch .gitignore

//在選定的數據庫資料夾裡面,新增忽略指定檔案

【.gitignore指令】忽略檔案

直接輸入要忽略的檔案
[例]要忽略index.html這個檔案

index.html

【.gitignore指令】忽略特定類型的所有檔案

米字號【*】+副檔名
[例]忽略所有html檔案

*.html

【.gitignore指令】忽略資料夾

正斜線【/】+資料夾名稱
[例]忽略css這個資料夾

/css

 

 

【Git】數據庫

流程示意圖

建立本地數據庫:git init

//在選定的資料夾上
$ git init

設好本地數據庫後,該資料夾內的資料的變更・新增・刪除,都會被記錄下來。

加入索引:git add

檔案都建立好・改好後,要提交紀錄。
提交前,先加入索引。

$ git add .
//目前資料夾的所有內容都會被加入索引
//【.】是全部的意思


$ git add index.html
//加入單獨檔案

検査狀態:git status

$ git status

//資料有加入索引的話,會顯示綠字
//資料沒有加入索引的話,會顯示紅字

提交版本更新:git commit -m ‘修改紀錄’

確認索引沒問題後,提交版本更新

$ git commit -m "update-1"

//提交完成後,用git status查詢,會出現nothing,表示這個資料夾裡面已經沒有東西需要被更新了

査詢紀錄:git log

查詢版本更新的次數、狀況等

$ git log

//會回傳這個資料夾的版本次數、作者、時間、版本名

【Git】Command Line 命令提示字元

【Windows】打開Git Bash,輸入命令提示字元
※cd 是 Command的意思

移動路徑:cd 路徑

移動到C槽

cd c:
//user@DESKTOP-L659N78 MINGW64 /c

移動到桌面的Git資料夾:C:\Users\user\Desktop\Git
↑這個是直接複製資料夾的路徑,

cd c/Users/user/Desktop/Git

//Git選取子資料夾的符號是【正斜線:/】,但是Windows顯示的子資料夾卻是【反斜線:\】

另一種方法:把資料夾直接拖拉到Git Bash視窗上面。
Git Bash會直接讀到這個資料夾的路徑。

展開列表(看資料夾裡面的檔案):ls

※ls→list

cd ls
//會回傳這個資料夾裡面的檔案

回母資料夾:cd ..

桌面上的Git資料夾→桌面

user@DESKTOP-L659N78 MINGW64 /c/users/user/desktop/git (master)

cd ..
user@DESKTOP-L659N78 MINGW64 /c/users/user/desktop/(master)

建立新資料夾:mkdir 資料夾名稱

※mkdir→make directory

mkdir hi
//電腦就會跑出一個新的資料夾

建立新檔案:touch 檔名

touch hello.txt
//該資料夾底下就會出現hello.txt這個檔案

建立開發者資訊

建立開發者資訊,
日後建立 / 修改檔案到Git時,會沿用建立者 / 修改者的姓名及基本資訊。

//設定email
git config --global user.email "這邊輸入email"

//設定姓名
git config --global user.name "這邊輸入姓名"

//檢查有沒有輸入成功
git config --list
//會回傳一堆資料,檢查user.email / user.name這2個欄位

變更遠端倉庫網址

git remote set-url origin https://xxxxxx.git

 

 

【JavaScript】滑鼠相關屬性

滑鼠相關屬性

clientX & clientY

定位基準:視窗長&寬
視窗長&寬→window.innerHeight / Width
※視窗的範圍:瀏覽器的畫面,扣掉上方網址列的部分。
如果把瀏覽器畫面,視窗也會跟著變小

▼示意圖▼

intro.addEventListener("mousemove", function(e){
   console.log(e.clientX, e.clientY);
 }
)

offsetX & offset Y

定位基準:目標元素的長&寬
目標元素的長&寬:id.clientHeight / Width

▼示意圖▼

intro.addEventListener("mousemove", function(e){
   console.log(e.offsetX, e.offsetY);
 }
)

pageX & pageY

定位基準:整個頁面的長&寬
整個頁面的長&寬:document.body.clientHeight / Width
※【沒設定頁面寬的情況】瀏覽器的寬度拉小,整個網頁頁面的寬度也會跟著變小(被壓縮了)
※【有設定頁面寬的情況】瀏覽器的寬度拉小,整個網頁頁面的寬度不會跟著變小(會出現橫向的卷軸)

▼示意圖▼

intro.addEventListener("mousemove", function(e){
   console.log(e.pageX, e.pageY);
 }
)

screenX & screenY

定位基準:電腦畫面的長&寬
電腦的長&寬:screen.height / width
※電腦整個畫面:上方有包含網址列,下方有包含工具列
※跟clientX / Y 的差別:定義的外圍是電腦畫面,所以即便拉小瀏覽器視窗,也不會有任何影響

▼示意圖▼

intro.addEventListener("mousemove", function(e){
   console.log(e.screenX, e.screenY);
 }
)

【JavaScript】load事件 網頁打開後馬上觸發

説明

網頁一載入→觸發事件
【例】表單填入網頁:頁面一打開,焦點就對準輸入格。

範例

寫法一

監聽load事件

input#input(type="text" placeholder="input something...")
window.addEventListener("load", function(){
 input.focus();
})

寫法二

使用window.load

window.onload=input.focus();

Display

【JavaScript】Event Listener 監聽事件一覽

用法

①透過特定event觸發動作

用法基礎篇

目標物.addEventListener("特定的event", 
 function(){
  //這邊寫要觸發的動作
 }
)

②操控目標元素完成某動作

用#button按鈕操控,讓超連結#theURL被點擊

a#theURL(href="ianchen.thisistap.com") 超連結
br
button(onclick="openURL()") click it and open the URL
//onclick的用法參見下方
function openURL(){
 theURL.click();
}

Display

超連結

③ON+EVENT名稱:用HTML直接呼叫 function

基礎範例

HTML用法:on+event名=”函式名()”
※如果要呼叫event的話,需要使用這種形式:on+event名=”函式名(event)”

【例如】click event & makeItBigger function
→onclick="makeItBigger()" 
//這個東西放在HTML tag裡面
//記得要另外寫makeItBigger這個函式

【上述用法等同】
id.eventListener("click", makeitBigger());

滑鼠相關監聽事件

  1. click event:滑鼠按下→觸發事件
  2. mousemove event:滑鼠移動→觸發事件
  3. mousedown event:滑鼠按下去的瞬間→觸發事件
  4. mouseout event:滑鼠離開→觸發事件
  5. mouseup event:滑鼠按下、離開的瞬間→觸發事件

打字相關監聽事件

  1. focus event:聚焦(點選到)該元素→觸發事件
    ※可以用滑鼠點選觸發focus,某些情況下也可以用鍵盤選取(Tab鍵),觸發focus
  2. blur event:focus的相反,離開該元素後→觸發事件
  3. keydown event:鍵盤按下時→觸發動作
  4. keyup event:鍵盤按下後鬆開→觸發動作
  5. input event:輸入文字→觸發動作

其他監聽事件

  1. transitionend event:漸變結束後→觸發事件
  2. change event:<input>狀態改變時→觸發事件
    【例】下拉式選單:變更選項→觸發事件
    【例】核取方塊:勾選 / 取消勾選→觸發事件

 

【JavaScript】input相關Method

value

偵測文字輸入框的內容

input#input(type="text")
br
#show
window.addEventListener("keyup", function(){
 show.textContent=`What you input is "${input.value}"`;
})

Display

What you input is

checked


監測核取方塊被點選的狀態。並且傳回布林值

input#checkbox(type="checkbox")
br
p#ckeckStatus
btn.addEventListener("click", function(){
 ckeckStatus.innerHTML=`ckecked status: ${checkbox.checked}`;
})

Display


【EXCEL】PivotTable 樞紐分析表

説明

將清單形式的資料整理成表格,
可以自訂縱軸與橫軸的欄位。

範例:研討會人數統計

原始資料名單

場次 負責業務 參加者姓名 更新日期 性別 素食 / 葷食
台北 A組 Mary 9月23日 素食
台中 B組 Moko 9月23日 素食
高雄 B組 Cherry 9月23日 素食
台北 A組 Apple 9月24日 素食
台北 A組 Banana 9月25日 素食
台中 B組 Grape 9月25日 素食
高雄 C組 Vegetable 9月25日 葷食
高雄 A組 Blue 9月25日 葷食
台中 A組 Sky 9月26日 葷食
台中 C組 Ocean 9月26日 葷食
台北 C組 Autumn 9月26日 葷食

建立樞紐分析表步驟:

    1. 選取資料範圍
    2. 標籤塊【插入】→樞紐分析表→確定
    3. 本次分析:場次與負責業務的人數相對關係
    4. 【場次】拉向列方格
    5. 【欄位】拉向欄方格
    6. 【參加者姓名】拉向値方格

※名單更新→同步更新樞紐分析表的資料

EXCEL的樞紐分析表無法即時同步更新(Google Sheet的就可以)
但是,名單更新後,可以手動點選以下路徑,更新樞紐分析表資料

樞紐分析表工具→標籤塊【分析】→重新整理

範例:業績報告(使用加總功能)

原始資料名單

日期 訂單號 客戶 産品 數量 金額
9月23日 A0001 Bank Banana 1  $        1,000
9月23日 A0002 Bank Apple 10  $        5,000
9月23日 A0003 Bank Banana 3  $        3,000
9月24日 A0004 Bank Banana 5  $        5,000
9月24日 A0005 Bakery Apple 3  $        1,500
9月26日 A0006 Bakery Banana 1  $        1,000
9月26日 A0007 Bakery Apple 1  $            500
9月26日 A0008 School Apple 1  $            500
9月26日 A0009 School Apple 2  $        1,000
9月26日 A0010 School Orange 1  $        3,000
9月26日 A0011 School Orange 3  $        9,000
9月29日 A0012 Council Orange 5  $      15,000
9月29日 A0013 Council Mango 1  $      10,000
9月29日 A0014 Council Mango 5  $      50,000

建立樞紐分析表:分析客戶、産品的銷售金額關係。

  • 【産品】拉向列方格
  • 【客戶】拉向欄方格
  • 【金額】拉向値方格
  • ※點選樞紐分析表儲存格→右鍵→摘要値方式→加總

 

【JavaScript】Contains Method

説明

  1. 判斷節點(Node)裡面有沒有含有檢索的內容。
  2. 若有,回傳true
  3. 若無,回傳false

範例

#p.hi 123
console.log(p.classList.contains("hi"));
//true

進階:防止選取子元素

母選單設定點選→觸發子選單展開・收合的function,但因為子選單包在母選單底下,所以在子選單打開的狀況下,點選子選單內容,會觸發function,造成子選單收合

解決方法:在function裡面增加contains條件(選取子選單的class),防止子選單觸發

.nav
 ul
 li.first
 |list 01 click
 ul.second
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.first
 |list 01 click
 ul.second
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.first
 |list 01 click
 ul.second
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.first
 |list 01 click
 ul.second
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.first
 |list 01 click
 ul.second
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
 li.not-choose list 02
.second
 max-height: 0px
 overflow: hidden
 transition-duration: 0.5s
 
.open
 max-height: 100px
// if(e.target.classList.contains("list02")==true) return;
// // 讓他不要選到children
const firsts=document.querySelectorAll(".first");

firsts.forEach(first => first.addEventListener("click", function(e){
 //讓他不要選到children
 if(e.target.classList.contains("not-choose")==true) return;
 this.children[0].classList.toggle("open")
 
 
}))

Display

Facebook Pixel

官方文件

  1. 基本用法【PageView】
  2. 轉換率追蹤【Purchasing Event】
  3. 事件與參數用法【Event & Parameter】

範例

基本用法:導入PageView

<script> 
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? 
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; 
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; 
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, 
document,'script','//connect.facebook.net/en_US/fbevents.js'); 
fbq('init', '這邊放入FB頁面的ID'); 
fbq('track', 'PageView'); 
</script> 

<noscript> 
 <img height="1" width="1" style="display:none" 
src="https://www.facebook.com/tr?id=這邊放入FB頁面的ID&amp;ev=PageView&amp;noscript=1" /> 
</noscript>

進階用法:轉換率追蹤

<script> 
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? 
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; 
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; 
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, 
document,'script','//connect.facebook.net/en_US/fbevents.js'); 
fbq('init', '這邊放入FB頁面的ID'); 
fbq('track', 'PageView');  
fbq('track', 'Purchase', {currency: '這邊放幣值', value: 這邊放金額}); 
//物件參數還可以設其他的,但是上面2個是必須指定。
//詳細參考事件與參數用法 
</script> 


<noscript> 
<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=這邊放入FB頁面的ID&amp;ev=PageView&amp;noscript=1" />  //↓如果有2個事件的話(例如本例),就要指定2組img tag
<img height="1" width="1" border="0" alt="" style="display:none" 
src="https://www.facebook.com/tr?id=107593379985738&amp;ev=Purchase&amp;cd[value]=這邊放金額&amp;cd[currency]=這邊放幣值" />  
//【&amp;】是&的轉換字元,用來連結Purchase事件的物件資料。
//因為本例只有2組物件參數,所以用了一個&amp;連結彼此
//若使用更多物件參數,就需要追加&amp;連接
//用法請參考轉換率追蹤 </noscript>

除錯方法

  1. 準備好FB Pixel程式碼
  2. 下載Chrome外掛程式:Facebook Pixel Helper
  3. 用CODEPEN打開一個PEN,把程式碼貼在HTML的地方
  4. 用Facebook Pixel Helper檢查程式碼有沒有成功執行(成功會有綠色的勾勾)

 

【HTML】ruby tag 注音表記

説明

顯示國字的注音(或是日文的假名)

範例

基本用法

  • 文字主體ーrb tag (ruby body)
  • 注音標示ーrt tag (ruby text)
ruby
 rb 木霊 //文字主體
 rt こだま //注音標示

Display

木霊こだま

跨瀏覽器用法

有瀏覽器無法讀取ruby tag時,可以使用以下方法解決

  • rp tag (ruby parentheses)包覆rt tag
ruby
 rb 木霊
 rp (
 rt こだま
 rp )

Display

木霊(こだま)

【CSS】z-index

條件

  1. 內元素絕對定位(所有)
  2. 外元素相對定位
  3. 目標元素指定index

※z-index的數值代表優先度。
數字大,表示優先顯示的強度越強(排在前面)
數字小,表示優先顯示的強度越低(隱藏在後面)←反向操作:設負數讓元素被蓋住

範例

沒有設index的情況下,HTML下面的元素會蓋掉上面的元素

.box1
.box2
body
 position: relative //外元素相對定位

.box1,.box2
 width: 200px
 height: 200px
 border: 1px solid #eee
 
.box1
 background-color: #73c6a8
 position: absolute //內元素絕對定位
 
.box2
 background-color: #73c0c6
 position: absolute //內元素絕對定位

 margin-top: 50px
 margin-left: 50px

Display

.
.

 

 

 

 

 

 

綠色方塊的HTML在上面,藍色方塊的HTML在下面。
所以藍色方塊優先顯示,蓋掉綠色方塊

設定z-index,優先顯示目標元素

.box1
.box2
body
 position: relative //外元素相對定位

.box1,.box2
 width: 200px
 height: 200px
 border: 1px solid #eee
 
.box1
 background-color: #73c6a8
 position: absolute //內元素絕對定位
 z-index: 1
 
.box2
 background-color: #73c0c6
 position: absolute //內元素絕對定位

 margin-top: 50px
 margin-left: 50px

Display

.
.

 

 

 

 

 

 
設定index為1,優先顯示綠色方塊

【Vue.js】ready指令 API串接

説明

用ready串接後台API

範例

API載入純文字

#app
 p {{text}}
const vm=new Vue({
 el: "#app",
 data: {
  text: ""
  },
 ready: function(){
  $.ajax({
   url: "https://awiclass.monoame.com/api/command.php?type=get&name=notifydata",
   success: function(res){
    vm.text=res;
    }
   })
  }
 
})

Display

哈囉!! 這邊是你用AJAX載入的純文字公告!!

API載入JSON

要先轉換成JSON格式,否則會變成純字串

#app
 ul
  li(v-for="item in items") 【{{item.name}}】${{item.price}}
const vm=new Vue({
 el: "#app",
 data: {
  items: []
  },
 ready: function(){
  $.ajax({
   url: "https://awiclass.monoame.com/api/command.php?type=get&name=itemdata",
   success: function(res){
    vm.items=JSON.parse(res); //轉換成JSON格式
    }
   })
 }
 
})

Display

  • 【吹風機】$300
  • 【麥克筆】$9000
  • 【筆記型電腦】$54555
  • 【Iphone 9】$32000
  • 【神奇海螺】$5000
  • 【PSP1007】$2000
  • 【鍵盤】$3500

【JavaScript】Ajax

説明

jQuery版本
使用原生的JavaScript串接Ajax

  1. 建立HTTPRequest請求  new XMLHttpRequest()
  2. 建立回應函式  onreadystatechange
  3. 確認readyState、status(HTTP狀態碼)OK
  4. 執行回應動作,回應的值為responseText
  5. 定義後端api網址與method  open()
  6. 前端傳送資料  send()

readyState

 值  狀態  說明
 0  UNSET  尚未讀取
 1  OPENED  讀取中
 2  HEADERS_RECEIVED  已下載完畢
 3  LOADING  資訊交換中
 4  DONE  成功

status

status為200,表示成功

HTTP狀態碼一覽

範例

JavaScript版

p#p
const xmlhttp=new XMLHttpRequest();

xmlhttp.onreadystatechange=function(){
 if(this.readyState==4 && this.status==200){
  p.textContent=this.responseText; //API連成功的動作
 }
};

xmlhttp.open("GET", "https://awiclass.monoame.com/api/command.php?type=get&name=notifydata");//API網址
xmlhttp.send();

Display

哈囉!! 這邊是你用AJAX載入的純文字公告!!

jQuery版

p#p
$.ajax({
 url: "https://awiclass.monoame.com/api/command.php?type=get&name=notifydata",
 success: function(res){
  p.textContent=res;
 }
})

Display

哈囉!! 這邊是你用AJAX載入的純文字公告!!

用ajax取得header, menu等共用HTML

#header
var headerXhr=new XMLHttpRequest();

headerXhr.open('GET', 'header.html', true);
headerXhr.send();

headerXhr.onreadystatechange=function(){
    if(headerXhr.readyState==4 && headerXhr.status==200){
        header.innerHTML = headerXhr.responseText;
    }
};

用ajax傳送JSON

TIPS: 先把JSON轉成string格式,再傳送

var xmlhttp=new XMLHttpRequest();

xmlhttp.open('POST', 'http://9.102.60.125:5000/api/bbinqcan', true);
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.onreadystatechange=function(){

    if(xmlhttp.readyState==4 && xmlhttp.status==200){
        res.textContent=xmlhttp.responseText;
    }
};

xmlhttp.send(
        //checkedJSON是一個JSON格式的變數
    JSON.stringify(checkedJSON);
)

【Vue.js】v-on

説明

methods寫在JS裡面,v-on寫在HTML裡面。
功能類似原生JS的addEventListener。

範例

#app
 button(v-on:click="show") click me
 p#vueText some text
#vueText
 color: #fff
var vm=new Vue({
 el: "#app",
 methods: {
 show: function(){
  vueText.style.color="#000"
  }
 }
})

Display

some text

【JavaScript】子物件指定

説明

JS中有2種方式可以指定子物件。

方法一:dot連接

常用方法,利用dot連接母物件與子物件

const obj={
 akasha: 222,
 typhoon: 15151515,
 tks: "33333333"
}

console.log(obj.akasha); //222

方法二:中括號

母物件後面用中括號,中括號裡面可以用字串指定,或是用變數代換

const obj={
 akasha: 222,
 typhoon: 15151515,
 tks: "33333333"
}

//字串指定法
console.log(obj["akasha"]); //222

/*****************************/

//變數代換法
const typ = "typhoon";
console.log(obj[typ]); //15151515

 

【Vue.js】v-if

使用方法

①data物件裡面建立會用到布林値的變數
②用v-model操控使用者變更布林値
③v-if=”變數名稱”,當布林値為真,顯示某些元素

範例

當核取方塊被點選,就會顯示下方文字

#app
 label Show text
 input(type="checkbox" v-model="show")
 p the value of show is {{show}}
 hr
 p(v-if="show") Hey the box is clicked !!
var vm=new Vue({
 el: "#app",
 data: {
 show: false
 }
});

Display

the value of show is false


【Vue.js】v-model 雙向綁定

說明

v-model的功能:
使用者輸入資料→JS物件同步更新→及時渲染

用法:v-model=”變數名稱”

範例

同步更新文字

#app
 label 姓名:
 input(v-model="name")

/********************/

h4 您好,{{name}}
var vm=new Vue({
 el: "#app",
 data: {
  name: "陳怡安"
 }
});

同步更新style

#app
 label 顏色:
 input(v-model="color")
/********************/

p 下面這個方塊的顏色是【{{color}}】
.block(style="background-color: {{color}}")
.block
 border: 1px solid #000
 width: 40px
 height: 40px
var vm=new Vue({
 el: "#app",
 data: {
  color: "#C0C999"
 }
});

陣列用法

#app
 p 喜歡的書:
 //渲染3次
 div(v-for="(id, book) in books")
  label 【{{id+1}}】
 //印出書名
  input(v-model="book")
 
/******************/
p 喜歡的書
 ul 
 li(v-for="book in books") {{book}}
var vm=new Vue({
 el: "#app",
 data: {
  books: ["雨天的艾莉絲", "鋼鍊", "天地明察"]
 }
});

Display

CODEPEN

 

【Vue.js】v-for

基本用法

v-for=”項目 in 陣列”

#app
 ul
  li(v-for="item in shoplist") {{item}}
var vm=new Vue({
 el: "#app",
 data: shoplist: ["apple", "banana", "papaya"]
});

Display

  • apple
  • banana
  • papaya

物件形式與存取編號

編號用id表示

#app
 ul
  li(v-for="(id, item) in shoplist2") {{id+1}}. {{item.name}}
  br
  |${{item.price}}

//陣列的編號從0開始計算,但是一般商品的編號要從1開始,所以id+1
var vm=new Vue({
 el: "#app",
 data: shoplist2: {
 name: "apple",
 price: 1000,
 origin: ["美國", "日本"]
 },
 {
 name: "banana",
 price: 500,
 origin: ["台灣", "韓國", "菲律賓"]
 },
 {
 name: "papaya", 
 price: 300,
 origin: ["亞特蘭提斯", "印度"]
 }
});

Display

  • 1. apple
    $1000
  • 2. banana
    $500
  • 3. papaya
    $300

使用第二層v-for

#app
 ul
  li(v-for="(id, item) in shoplist2") 【編號】{{id+1}} 
  br
  |【品名】{{item.name}}
  br
  |【價錢】${{item.price}}
  br
  |【生產地】
  span(v-for="place in item.origin") </br>{{place}}
var vm=new Vue({
 el: "#app",
 data: shoplist2: {
 name: "apple",
 price: 1000,
 origin: ["美國", "日本"]
 },
 {
 name: "banana",
 price: 500,
 origin: ["台灣", "韓國", "菲律賓"]
 },
 {
 name: "papaya", 
 price: 300,
 origin: ["亞特蘭提斯", "印度"]
 }
});

Display

  • 【編號】1
    【品名】apple
    【價錢】$1000
    【生產地】
    美國
    日本
  • 【編號】2
    【品名】banana
    【價錢】$500
    【生產地】
    台灣
    韓國
    菲律賓
  • 【編號】3
    【品名】papaya
    【價錢】$300
    【生產地】
    亞特蘭提斯
    印度

 

 

【CSS】Width fix the list text

問題背景

<li>在不指定寬度的情況下,會自動填滿整行。
如果把border叫出來,就會很明顯。
例如:

  • 填滿整行的li
  • 填滿整行的li

可以佐以排版技巧來解決這個問題。

橫向排列:inblock

橫向排列只要指定display就可以解決問題

ul
 li 橫向排列不填滿
 li 橫向排列不填滿
 li 橫向排列不填滿
li
 border: 1px solid #000
 display: inline

Display

  • 橫向排列不填滿
  • 橫向排列不填滿
  • 橫向排列不填滿

縱向排列:float

使用float完成縱向排列

ul
 li 縱向排列不填滿
 li 縱向排列不填滿
 li 縱向排列不填滿
ul
 float: left

li
 border: 1px solid #000

Display

  • 縱向排列不填滿
  • 縱向排列不填滿
  • 縱向排列不填滿

 

注意:周邊元素必須指定clear

如果不指定clear的話,會變成這樣↓↓

  • 縱向排列不填滿
  • 縱向排列不填滿
  • 縱向排列不填滿

這段文字想要放在清單下方,但是跑到左邊來了QQ

 

 


解決法:周邊的元素指定clear。

ul 
 li 縱向排列不填滿 
 li 縱向排列不填滿 
 li 縱向排列不填滿 

p 這段文字想要放在清單下方,成功。
ul
 float: left
li
 border: 1px solid #000

p 
 clear: both 
 //該元素的左邊(left)、右邊(right)、兩邊(both)都不可以有任何東西,若有的話,該元素會自動往下移。

Display

  • 縱向排列不填滿
  • 縱向排列不填滿
  • 縱向排列不填滿
這段文字想要放在清單下方,成功

【Vue.js】變數代換

説明

Vue.js是前端框架,用JS解決HTML資料處理。
→技術文件

HTML:寫模板
JS:代入資料
→解決①HTML重複撰寫太多②資料即時更新問題

應用

引入Vue.js

<head>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.js"></script>
</head>

建立模板與代入資料

#app
//指定Vue作用區域
 h1 Vue.js 使用大括號與變數代換
 hr
 p 我的名字叫做{{name}},來自{{city}}
 //用兩個大括號{{}}建立模板
 br
 |最近正在學習如何使用{{lang}},
 br
 |他其實就像是一個{{text}}
 p Vue.js的代換可以用物件來表示,甚至陣列來判斷。
//建立一個Vue, 即時監測資料更新狀況
var vm=new Vue({
 el: "#app", //指定作用區域
 data: { //用物件形式存放資料
 name: "陳怡安",
 city: "基隆市",
 lang: "Vue.js",
 text: "資料置換法",
 notation: "兩個大括號{{變數名稱}}",
 }
})

Display

我的名字叫做陳怡安,來自基隆市
最近正在學習如何使用Vue.js,
他其實就像是一個資料置換法

Vue.js的代換可以用物件來表示,甚至陣列來判斷。
只要在裡面用兩個大括號{{變數名稱}}包起來就可以了。

陣列與v-for列表渲染

#app
 p 我的技能有:
 /**************
 非v-for法
 ul 
  li {{skills[0]}}
  li {{skills[1]}}
  li {{skills[2]}}
  li {{skills[3]}}
 ****************/
 //v-for法
 ul
  li(v-for="skill in skills") {{skill}}
  //skill 可以自己命名
  //skills要根據vue撰寫的内容指定
var vm=new Vue({
 el: "#app",
 data: {
 skills: ["程式開發", "網頁設計", "翻譯", "口譯"],
});

Display

我的技能有:

  • 程式開發
  • 網頁設計
  • 翻譯
  • 口譯

 使用物件資料

#app
 p 我踏入網頁設計已經有{{infos.year}}年了,希望可以持續進步。
var vm=new Vue({
 el: "#app",
 data: {
 infos:{
 year: 1
  },
 }
});

Display
我踏入網頁設計已經有1年了,希望可以持續進步。

代入HTML程式碼

使用3組大括號代入HTML程式碼

#app
 p Vue.js除了預設帶入是一般的純文字以外,三組大括號可以代入HTML原始碼
 div {{{html}}}
.block
 width: 100px
 padding: 10px
 border: 1px solid #000
var vm=new Vue({
 el: "#app",
 data: {
  html: "<div class='block'>代入HTML</div>"
  //雙引號内的HTML字串裡面要改單引號
 }
});

Display

代入HTML

globalCompositeOperation 合成效果

説明

對canvas產生圖層混合的效果。
類似CSS的mix-blend-mode

範例

canvas#canvas(width="800", height="800")
html, body
 margin: 0
 padding: 0
 overflow: hidden
const ctx=canvas.getContext("2d");


ctx.fillStyle="#FFA500";
ctx.fillRect(400, 400, 100, 100);

//合成效果一覽
ctx.globalCompositeOperation="multiply";

ctx.fillStyle="#f7f4c8";
ctx.fillRect(450, 450, 100, 100);

Display
Display

destructuring assignment 分離指派式

説明

可以將陣列或物件的資料取出成獨立變數。
※通常用來大量指定變數,可以一行寫完。

範例

/********
以前的寫法
const a=1;
const b=2;
*********/

/***ES6新寫法***/
[a, b]=[1, 2]
//可以簡潔地指定一坨變數。

console.log(a, b)//1 2

canvas stroke 描繪線條

步驟

  1. 建立路徑
    beginPath()
  2. 設定線條寬度(可省)
    lineWidth=”5″
  3. 設定顏色(可省)
    strokeStyle=”green”
  4. 開始
    moveTo(0, 100)
  5. 結束
    lineTo(300, 100)
  6. 畫出線來
    stoke()

範例

canvas#canvas(width="300", height="150")
const ctx=canvas.getContext("2d");
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;

//draw a line
ctx.beginPath();//start to draw a line
ctx.lineWidth="5";//set line width
ctx.strokeStyle="green";//set color
ctx.moveTo(0, 100);//start point
ctx.lineTo(300, 100);//end point
ctx.stroke();//draw it

Display

const

const 的特性

  1. 不可以被修改(再指定)
  2. 如果有block,在block裡面可以被改,但是回到外面會變回初始值

cant’t be updated & defined

const width=100;

width=200;
console.log(width);//100
//不可被修改

block scope

let points=50;
const winner=false;


if(points>40) {
 const winner=true;
 console.log(winner);//true
 //只有在block內部,才能夠被更改
}

console.log(winner);//false
//在外部則不會被更改,呈現初始值

By Value or By Reference

當資料類型為By Value時〔Number, String, Boolean〕
以const宣告的變數是不能被再指定的(即等於不能被改)

//錯誤示例
const name='Ian';
name='Chen'; //←ERROR!!

但若資料類型是By Reference時〔Object, Array〕,
以const宣告後可以用push, object.item=xxx等方式修改其值。
但一樣不能進行再指定的動作

//正確示例
const fruits=['apple', 'banana', 'orange'];
console.log(fruits); //['apple', 'banana', 'orange']

fruits.push('grape');
console.log(fruits); //['apple', 'banana', 'orange', 'grape']
//錯誤示例
const colors=['red', 'blue', 'green'];
colors=['pink', 'black', 'white'] //←ERROR!!

let

block

block是指大括弧 {} 裡面。
像是if述句,for述句,function裡面都是block

  • global scope→任何地方都能被改
  • block socpe→在if述句,for述句,function裡面可以被改(大括號包起來的地方),在外面還是回到初始值
  • function scope→只有在function裡面才可以被改

let 的特性

  1. 可以被修改
  2. 如果有block,在block裡面可以被改,但是回到外面會變回初始值

updated & defined

let width=100;

width=200;
console.log(width);//200
//可被修改

block scope

let points=50;
let winner=false;


if(points>40) {
 let winner=true;
 console.log(winner);//true
 //只有在block內部,才能夠被更改
}

console.log(winner);//false
//在外部則不會被更改,呈現初始值

var

var的特性

  1. 可以被重新定義(redefined)
  2. 可以被更新(updated)
  3. 若存在於函式內(function),就是函式變數(function scope),只能在函式內部被存取、修改。
  4. 若沒再函式內,會變成全域變數(global scope),不論在函式內或是外面都可以被存取,修改。

redefined and updated

var width=100;
console.log(width);//100

width=200;
console.log(width);//200
//可被修改

function scope

function setWidth() {
 var width = 100;
 console.log(width);//100
}

setWidth();
console.log(width);//nothing
//width只能在setWidth裡面作用

global scope

var width;
function setWidth() {
 width = 100;
 console.log(width);//100
}

setWidth();
console.log(width);//100
//width 在任何地方都能被修改或存取
let points = 50;
var winner = false;

if(points > 40) {
 var winner = true
 console.log(winner);//true
}

console.log(winner);//true
//因為if不是function,所以winner變成全域變數
//能在任何地方被修改與存取

建立canvas

説明

canvas 是一種強大的繪圖功能。
透過 HTML 與 JS 的使用 (主要是 JS )
可以在瀏覽器上繪製任何向量圖型。
跟 SVG 有點像,但用途更多元。

步驟解説

設定 HTML 與 CSS

canvas#canvas(width="800", height="800")
//呼叫canvas,然後給一個id,設定畫布長寬
//這邊設定的長800寬800會成為指定向量元素的絶對定位參考

html, body
 margin: 0
 overflow: hidden
//讓邊界不會多出來

定義 context 與長寬

context 是作畫的畫布。
圖形都要畫在 context 上面。

//定義context
const ctx=canvas.getContext("2d");//平面圖型用2d

//定義長寬
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;//設定畫布=全螢幕

開始作畫(添加圖形元素)

  1. 先設定顏色
  2. 再設定圖形
//填色
ctx.fillStyle="#FFA500";


//畫正方形
ctx.fillRect(400, 400, 100, 100);
//※顏色要在最上面,否則正方形會變成黑的

游標

説明

  1. 用 CSS 刻出游標樣式
  2. 抓取滑鼠位置
  3. 游標的位置隨著滑鼠移動而改變

範例

#cross
#cross
 position: absolute
 &:before,&:after
 content: ""
 display: block
 width: 60px
 height: 15px
 background-color: #fff
 position: absolute
 left: 50%
 top: 50%
 transform: translate(-50%,-50%) rotate(45deg)
 &:after
 transform: translate(-50%,-50%) rotate(-45deg)
window.addEventListener("mousemove", function(evt){
 
 var x=evt.pageX;
 var y=evt.pageY;
 
 cross.style.left=x+"px";
 cross.style.top=y+"px";
})

Display

CODEPEN

splice() 陣列項目刪除

説明

刪除或增添陣列項目

範例

刪除項

陣列.splice(編號,刪除幾項)

【例】arr.splice(1, 1)
→從 arr[1] 開始刪除1項
→刪掉 arr[1]

const fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];
fruits.splice(1, 1)//從fruits[1]開始,刪掉1項。

console.log(fruits);//["Banana", "Lemon", "Apple", "Mango"]

增添項

const fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];
fruits.splice(1, 1, "Melon")//從fruits[1]開始,刪掉1項,插入“Moko”

console.log(fruits);//["Banana", "Melon", "Lemon", "Apple", "Mango"]

 

slice() 保留部分陣列項目

定義

指定陣列編號範圍,返回一組新的陣列

【必須値】開始,結束
※返回的値不會包含結束的編號,但會包含開始的那個編號

範例

const fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];

const sl=fruits.slice(0, 2);
//return//fruits[0], fruits[1];
console.log(sl);//["Banana", "Orange"]

【省略結束値】回報至最末項

const fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];

const sl=fruits.slice(2);
//等於fruits.slice(2, fruits.length);
//return fruits[2], fruits[3], fruits[4]
console.log(sl);//["Lemon", "Apple", "Mango"]

擷取多範圍

fruits共有5項資料。
保留【第1項】【第3~5項】。

多條件要用中括號[]包起來。
條件之間用逗號, 連結。
並且加上spread operator轉換成array形式。

const fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];

const sl=[
 ...fruits.slice(0, 1),
 ...fruits.slice(2)
];

console.log(sl);
//["Banana", "Lemon", "Apple", "Mango"]

陣列相關方法2

Ref: JavaScript30

some()  確認有沒有人符合條件

給予條件→找出是否有一個人(或以上)符合條件。

//共用陣列資料
const people = [
 { name: 'Wes', year: 1988 },
 { name: 'Kait', year: 1986 },
 { name: 'Irv', year: 1970 },
 { name: 'Lux', year: 2015 }
];

確認是否有人已滿19歳

/*****【方法一】function*************/
const isAdult1=people.some(

function(person){
 //getFullYear?
 const currentYear=new Date().getFullYear();
 if(currentYear-person.year>=19){
 return true;
 }
}
 
);


/*****【方法二】arrow function*******/
const isAdult2=people.some(person => new Date().getFullYear()-person.year>=19);
console.log(isAdult2);//true

every()  確認是否全員符合條件

給予條件→找出是否所有人都符合條件。

確認是否全員都滿19歳

const everyAdult=people.every(person => new Date().getFullYear()-person.year>=19);

console.log(everyAdult);//false

find()  找出特定的項目

//共用陣列資料
const comments = [
 { text: 'Love this!', id: 523423 },
 { text: 'Super good', id: 823423 },
 { text: 'You are the best', id: 2039842 },
 { text: 'Ramen is my fav food ever', id: 123523 },
 { text: 'Nice Nice Nice!', id: 542328 }
];

找出id為【823423】的留言

const comment=comments.find(comment => comment.id===823423);

console.log(comment);
/**
Object {
  id: 823423,
  text: "Super good"
}
**/

findIndex 找出特定項目的陣列編號

【※注意】陣列編號由 0 開始算。編號 0 為第1項、編號 1 為第 2 項。

找出id為【823423】的陣列編號

const index=comments.findIndex(comment => comment.id===823423);

console.log(index);//1 ←comments[1];

splice 刪除特定編號的陣列

參考文章
【※注意】只能指定編號刪除。

comments.splice(1, 1);

//然後comments就會少一項了。但不保留沒刪除前的内容。

slice 摘取陣列

參考文章

也可以達到刪除特定項目陣列的效果。
但方法是擷取保留項目,像是指定列印頁數。

共5頁的資料→只列印【第1頁】、【第3-5頁】→最後印出來的資料就會少了第2頁

用slice可以同時保留刪除前的陣列與刪除後的陣列。

const newComments=[
 ...comments.slice(0, 1),//only return comments[0]
 ...comments.slice(2)//omit end value
 //保留slice[0], slice[2-4]→等於去掉slice[1]
]; 

console.log(newComments);
/**
const newC=[
 ...comments.slice(0, 1),//only return comments[0]
 ...comments.slice(2)//omit end value
];
**/

 

搜尋器

Reference: JavaScript 30

先備知識

範例

form.search-form
 input.search(type='text', placeholder='City or State')
 ul.suggestions
 li Filter for a city
 li or a state
html
 box-sizing: border-box
 background: #ffc600
 font-family: 'helvetica neue'
 font-size: 20px
 font-weight: 200

*
 box-sizing: inherit
 &:before, &:after
 box-sizing: inherit

input
 width: 100%
 padding: 20px

.search-form
 max-width: 400px
 margin: 50px auto

input.search
 margin: 0
 text-align: center
 outline: 0
 border: 10px solid #F7F7F7
 width: 120%
 left: -10%
 position: relative
 top: 10px
 z-index: 2
 border-radius: 5px
 font-size: 40px
 box-shadow: 0 0 5px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.19)

.suggestions
 margin: 0
 padding: 0
 position: relative
 /*perspective:20px;
 li
 background: white
 list-style: none
 border-bottom: 1px solid #D8D8D8
 box-shadow: 0 0 10px rgba(0, 0, 0, 0.14)
 margin: 0
 padding: 20px
 transition: background 0.2s
 display: flex
 justify-content: space-between
 text-transform: capitalize
 &:nth-child(even)
 transform: perspective(100px) rotateX(3deg) translateY(2px) scale(1.001)
 background: linear-gradient(to bottom, #ffffff 0%, #EFEFEF 100%)
 &:nth-child(odd)
 transform: perspective(100px) rotateX(-3deg) translateY(3px)
 background: linear-gradient(to top, #ffffff 0%, #EFEFEF 100%)

span.population
 font-size: 15px

.details
 text-align: center
 font-size: 15px

.hl
 background: #ffc600

.love
 text-align: center

a
 color: black
 background: rgba(0, 0, 0, 0.1)
 text-decoration: none
const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';

const cities=[];

//blob用來指稱原始資料
//轉換成JSON資料
fetch(endpoint)
 .then(blob => blob.json())
 .then(data => cities.push(...data))

function find(word, cities){
 
 return cities.filter(place => {
 const regex=new RegExp(word, "gi");
 return place.city.match(regex) || place.state.match(regex)
 })

}

function commas(x){
 return x.toString().replace(/B(?=(d{3})+(?!d))/g, ",");
}


function display(){
 const match=find(this.value, cities);
 const html=match.map(place => {
 const regex=new RegExp(this.value, "gi");
 const cityName=place.city.replace(regex, `<span class="hl">${this.value}</span>`);
 const stateName=place.state.replace(regex, `<span class="hl">${this.value}</span>`);
 
 return `
 <li>
 <span class="name">${cityName}, ${stateName}</span>
 <span class="population">${commas(place.population)}</span>
 </li>
 `
 }).join("");
 
 
 suggestions.innerHTML=html;
}

const search=document.querySelector(".search");
const suggestions=document.querySelector(".suggestions");


search.addEventListener("change", display);
search.addEventListener("keyup", display);

Display

CODEPEN

regex【i】不區別大小寫

功能

讓字串不區分大小寫,也能通過正規表達式檢査。
應用實例:搜尋不用區分大小寫,也能找到同樣的内容。

類似的東西:regex【g】找出所有

範例

方法一【/i】

i代表 insensitive ,對大小寫不敏感。

const regex=/apple/i;

const str1="APPLE";
const str2="Apple";
const str3="apple";

str1.match(regex);//["APPLE"]
str2.match(regex);//["Apple"]
str3.match(regex);//["apple"]
//不論大小寫(或混用)都能通過驗證

方法二【new RegExp】

使用時機:需要檢査的字串不是固定値,而是變數時(像是使用者鍵入的搜尋字詞)

寫法跟【/i】不同,但是效果相同。

const regex=new RegExp("apple", "i");

const str1="APPLE";
const str2="Apple";
const str3="apple";

str1.match(regex);//["APPLE"]
str2.match(regex);//["Apple"]
str3.match(regex);//["apple"]

fetch

介紹

fetch,是ajax之外取得外部資料的一種方式
使用promise物件架構

補充:ajax

範例

取得圖片

img#img
const moko="https://scontent-tpe1-1.xx.fbcdn.net/v/t1.0-9/15355572_1410266889006372_4764767806658769471_n.jpg?oh=39e4da3e9a9956f349b6c701556544ff&oe=593B04D9"


fetch(moko)
 .then(blob => blob.blob())
 .then(myb => img.src=URL.createObjectURL(myb))

//fetch發送請求
//使用blob獲得圖片的內容
//再從blob獲得URL,放到img的src之中

Display

取得JSON資料(有function的情況下)

button#btn click me
ul#ul
const endpoints="https://awiclass.monoame.com/api/command.php?type=get&name=tododata";

const tododata=[];

fetch(endpoints)
 .then(blob => blob.json())
 .then(data => tododata.push(...data))//要用spread把nodeList轉成array

function show (){
 ul.innerHTML=tododata.map(place => `<li>${place.name}</li>`).join("");//join("")把陣列轉換成字串,並且將分隔取代為無
}

btn.addEventListener("click", show)

Display

CODEPEN

取得JSON資料(無function的情況下)

沒有function的情況下,JS無法用內接陣列的方式,讀取外部URL的JSON。
因此,方法改寫如下,

ul#ul
const endpoints="https://awiclass.monoame.com/api/command.php?type=get&name=tododata";

// const tododata=[];

fetch(endpoints)
 .then(blob => blob.json())
 .then(data => {
 ul.innerHTML=data.map(place => `<li>${place.name}</li>`).join("");
})

Display

CODPEN

onclick

功能

onclick 是 HTML 的屬性,等於 JavaScript 的 click 事件

範例

button#btn(onclick="scale()") click me
/******
onclick="scale()"等於這個
btn.addEventListener("click", scale);
******/

function scale(){
 btn.style.transform="scale(2)";
 btn.style.transformOrigin="left top";
}

Display

jQuery AJAX

功能

不刷新頁面也能完成前後端的溝通。

  • HTML + PHP + MySQL :必須刷新頁面
  • HTMP + Ajax + PHP + MySQL:不必刷新頁面

以 Facebook 的按讚功能為例
使用者按讚→資料庫更新讚數→網頁顯示讚數+1

透過 Ajax 的輔助,這一連串的動作都可以不刷新網頁而達成。
使用者不必中斷滑到一半的閱讀體驗。

結構

  • form tag,使用 HTTP Method
  • 引入 jQuery
  • 準備處理資料庫溝通的 php 檔案,跟 HTML 放在同一個資料夾底下
  • 準備好 MySQL 資料表

範例

前端

//HTTP Method
form(method="POST")
 input(type="text" placeholder="email")
 input(type="password" placeholder="password")
 input(type="submit")
 p
const email=document.querySelectorAll("input")[0];
const password=document.querySelectorAll("input")[1];
const submit=document.querySelectorAll("input")[2];
const alert=document.querySelector("p");


submit.addEventListener("click", function(e){
 //clear submit default
 e.preventDefault();
 $.ajax({
 type: "POST",//對應 form 的 method
 url: "insert.php",//指定 php 檔案
 data: {
 //要傳送的值。php超全域變數: js變數
 email: email.value, 
 password: password.value
 },
 //若傳送成功
 success: function(re){
 //若php回應值=="success"
 if(re=="success"){
 alert.textContent="register successful"
 }else{
 alert.textContent="register fail"
 }
 }
 });
})

後端

+----------+------+-----+---------------+
| Field    | Type | Key | Extra         |
+----------+------+-----+---------------+
| ID       | int  | PRI | auto_increment|
| email    | text |     |               |
| password | text |     |               |
+----------+------+-----+---------------+
<?php

mysql_connect("localhost", "root", "");//MySQL連線
mysql_select_db("register");//選擇資料庫

//$_POST['email']與$_POST['password']是ajax定義好的超全域變數
//防止隱碼注入攻擊
$_POST['email']=mysql_escape_string($_POST['email']);
$_POST['password']=mysql_escape_string($_POST['password']);

//帳號與密碼寫入資料庫
$save=mysql_query("INSERT INTO member (email, password) VALUES('$_POST[email]','$_POST[password]')");


if(!$save){
 
 echo "fail";//若寫入失敗回傳fail

}else{
 
 echo "success";//成功則回傳success
}


?>

 

程式碼上色函式庫 highlight.js

功能

程式碼上色,可以用 syntax 找相關的英文資源 ,這個函式庫可以將部落格的程式碼文字依照關鍵字、註解等上不同顏色。

使用方法

  1. 引入 css 與 js 函式庫
  2. 依照規範撰寫pre tag 與 code tag
  3. 寫進指定 js 碼

※css有各種style。

一般網頁

外引CDN函式庫的方法

引入 js 與 css 函式庫

<head>
 
 //引入css,這裡選擇tomorrow style
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/tomorrow.min.css" />

 //引入js
 <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script>
</head>

依照規範撰寫 pre 與 code

  1. pre tag 裡面包一層 code tag
  2. <code class=”程式語言名”>
//以寫css碼為例
<pre>
 <code class="css">
  pre {
  border: 1px solid #eee;
  width: 400px;
 }
 </code>
</pre>

寫進指定 js

function custom(){
 hljs.initHighlightingOnLoad();
}
custom();

Display

CODEPEN

WordPress

header.php→引入 css 與 js 函式庫、寫入指定 js

<head>

//在head tag之間加上下面這3行
<!--highlight.js-->
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/tomorrow.min.css">
 <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script>

</head>

<body <?php body_class(); ?>>

//在<body>下面加上這段script
<script>
hljs.initHighlightingOnLoad();
</script>

文章編輯:text editor→依照規範撰寫 pre 與 code

//以寫css碼為例
<pre>
 <code class="css">
  pre {
  border: 1px solid #eee;
  width: 400px;
 }
 </code>
</pre>

Display

CODEPEN

 

 

match() 與 test()

功能

兩者都是搭配正規表達式的Method

match()

字串.match(正規表達式);
//回傳比對出的部分字串結果
//若比對不到東西,則回傳null

const str="apple";
const check=/[a-c]/;

str.match(check);//["a"]

test()

正規表達式.test(字串);
//有比對到回傳true,沒有則回傳false

const str="apple";
const check=/[a-c]/;

check.test(str);//true

setInterval

功能

設定每隔一段時間執行一次動作,可以模擬動畫效果
缺點:只能處理能夠數值化的CSS屬性(width, opacity, scale, rotate等等),不能完成顏色漸變等效果

button(onclick="show()") click me
br
br
#box show up
#box
 width: 200px
 height: 100px
 background-color: #aaffe2
 text-align: center
 line-height: 100px
function show(){
 //pos是變動的透明度數值
 var pos=0;
 //setInterval(動作, 毫秒)→每隔幾毫秒,執行一次動作
 const id=window.setInterval(function(){
 
 if(pos>=1){
 //如果pos夠大了,清掉Interval,阻止繼續增加下去
 clearInterval(id);
 pos=0;
 }else{
 pos+=0.1;
 box.style.opacity=pos;
 }
 
 }, 50)
}

show up

正規表達式

説明

正規表達式,是專門用來處理字串邏輯的條件語法。
常常用在 input 表單上,檢査使用者輸入的内容有沒有符合特定格式。
諸如信用卡號格式驗證,密碼強度驗證等等。

相關方法:match(),test()

語法

宣告正規表達式

正規表達式的前後要各用1個斜線/包起來(有點像字串需要用”包起來)。

const str="abcde";
const check=/abc/;

str.match(cri);//["abc"]
//回傳 被找到的字串
if(str.match(cri)){
 console.log(true);
 //↑通常驗證表單會寫成這個形式,裡面放通過驗證後的動作
}else{
 console.log(false)
}//true

尋找某區間内文字

設定檢索區間: [a-e]
a為開始値,e為終止値。

const str="apple";
const check=/[a-c]/;//尋找有沒有 a,b,c 這3個字母
//※寫成/[a-c]/g會比較好(下面會説明)
//※也可以比對多重區間(下面説明)

str.match(check)//["a"]

if(str.match(cri)){
 console.log(true);
}else{
 console.log(false);
}//true ←但只有 a 有被找到而已,pple並沒有被找到

/*****注意*****
※這個例子match是寬鬆尋找方法,只要有1個字元被找到,就會跑出true
*************/

尋找全部【/g】

參考這篇文章

一般情況下,match只會1次比對一個字串字元而已,所以回傳値只會吐出第1項符合的字元。
如果要叫他吐出所有,就要使用/g←放在正規表達式的最後面

※但如果是用 if(str.match(check)) 的話不使用 /g 也沒差

const str="apple";
const check=/[a-e]/;
const check2=/[a-e]/g;//尋找全部寫法


str.match(check);//["a"]
str.match(check2);//["a", "e"]←這樣寫就可以一條一條找出所有符合的字元了

/****比對多重區間***/
const check3=/[a-eo-p]/g//←直接接下去加入就好

str.match(check3);//["a", "p", "p", "e"]

尋找數字與數字字符【\d】

有2種方法可以找到數字

  • 設定 0-9 的區間
  • 正規字符【\d】
const str="apple123";
const check=/[0-9]/g
const check2=/\d/g; //\d效果同等於[0-9]


str.match(check);//["1", "2", "3"]
str.match(check2);//["1", "2", "3"]

檢査數字長度

使用大括號{}指定該字元必須出現幾次

const str="123456";
const check=/\d{4}/;//{4}表示數字(\d)要連續出現4次←4位數字

str.match(check);//["1234"]

if(str.match(check)){
 console.log(true);
}else{
 console.log(false);
}//ture
/*****
str="123456"→true
str="1234"→true
str="12"→false

只有當str不足4位數字時才會回報false(match找不到匹配値,回傳null)
str滿足或超過4位數都會回傳true
※那要怎麼寫手機號碼驗證!←下面告訴你
*****/

設定嚴格的條件:使用開始【^】與結束【$】字符

在上例中可以發現,只有當字串不滿足條件時才會報 false ,剛好符合或超過條件的清況都會回報 true 。

但某些情況下,我們需要非常精確的格式,比如

  • 手機號碼必須是 10 位數字
  • 信用卡號必為 4位數-4位數-4位數-4位數
  • 身份證字號為 1大寫英文 + 9 為數字

所以我們必須更進階地規定條件,不足或超出條件者都必須是 false。

以下為嚴格化寫法

  • 在開頭加入開始字符^
  • 結尾加入結束字符$
//驗證手機號碼
const str1="0912345678";
const str2="0912";
const str3="09123456789999"
const check=/^\d{10}$/;

str1.match(check); //["0912345678"]
str2.match(check); //null
str3.match(check); //null

//進階:規定前2碼為09

const check2=/^09\d{8}$/; //指定09跟後面的8碼條件不用任何東西連接,直接加上就行

str1.match(check2); //["0912345678"]

英數字集符號【\w】

如同 \d 為 [0-9] 的簡寫一般,
\w 則為 [a-zA-Z0-9_] 的簡寫。

可以檢查所有英文大小寫字母,底線,以及數字

const str="ianchen_0419";

const check=/\w/g;
const check2=/[a-zA-Z0-9_]/g;
//這兩個效果一樣

str.match(check);//["i", "a", "n", "c", "h", "e", "n", "_", "0", "4", "1", "9"]
str.match(check2);//["i", "a", "n", "c", "h", "e", "n", "_", "0", "4", "1", "9"]

 

可有可無的條件【?】

『使用者名稱可以包含減號-』

這樣的情況下,不論字串有無減號-,皆可滿足條件。
因此減號-是一個可有可無的條件。

在減號-後面加上問號 ? ,便可以構成可有可無的驗證式

const str1="ianchen";
const str2="ian-chen";
const str3="ian--chen";
const str4="-ianchen";
const str5="ianchen-";

const check=/\w-?\w/;
const check2=/^\w-?\w$/;
//用check2的話5個str都會是null,因為check2必須是【1字母 + 有無底線皆可 + 1字母】的組合才行。


str1.match(check);//["ia"]
str2.match(check);//["ia"]
str3.match(check);//["ia"]
str4.match(check);//["-i"]
str5.match(check);//["ia"]
//結論:5種情況都可以true

比對特殊字元:前置反斜線\

Regex特殊字元【.^$?】

這些字元都是Regex特殊字符,帶有特殊的意義。
若要單純比對這些符號,必須前置反斜線\以做區別。

const str1="$";
const str2="^";
const str3=".";
const str4="?";

const check1=/\$/;
const check2=/\^/;
const check3=/\./;
const check4=/\?/;

str1.match(check1);//["$"]
str2.match(check2);//["^"]
str3.match(check3);//["."]
str4.match(check4);//["?"]

比對很多次【+】

重複比對【+】前面的東西很多次

const str="ianchen_0419";
const check=/\w+/;
//重複比對英文字元(←【+】前面的)很多次,直到比對不了為止

str.match(check);//["ianchen_0419"]

有後面再比對前面【?=】

a(?=b)→如果有找到b的話,再檢查前面有沒有a,並且比對出a
【?=b】要使用括號包起來

const str1="ianchen0419";
const str2="ianchen"
const str3="0419ianchen";
const check=/[a-z]+(?=\d)/;
//後面有數字的話,再比對前面有沒有英文
//然後把所有的英文都找出來【+】

str1.match(check);//["ianchen"]
str2.match(check);//null ←這個因為後面找不到數字所以沒辦法比對英文
str3.match(check);//null ←這個因為數字前面沒有英文所以一樣無法

沒有後面再比對前面【?!】

a(?!b)→如果沒有找到b的話,再檢查前面有沒有a,並且比對出a
【?!b】要使用括號包起來

使用情境:給數字加上分隔逗號,每3碼加一個逗號,但是要從最末位往前數(所以要確保後面沒有落單的數字)

【例】1,234,567,890

const str1="ianchen0419";
const str2="ianchen"
const str3="0419ianchen";
const check=/[a-z]+(?!\d)/;
//後面沒有數字的話,再比對前面有沒有英文
//然後把所有的英文都找出來【+】

str1.match(check);//null ←這個因為後面有數字了所以不能比對前面的英文
str2.match(check);//["ianchen"]
str3.match(check);//["ianchen"]

找出英文邊界【\b】

※前提,英文句子要用空格隔開單字

【\b\w】→找到每個單字字首
【\w\b】→找到每個單字字尾

const str="this is a apple";//必須要有空格
const check1=/\b\w/g;//找字首
const check2=/\w\b/g;//找字尾

str.match(check1);//["t", "i", "a", "a"]
//"this is a apple"

str.match(check2);//["s", "s", "a", "e"]
//"this is a apple"

找出非英文邊界【\B】

用途:數字,符號等等

const str="1234567890";
const check1=/\B\d{3}/g;//每3個字斷開(從後面開始數)
const check2=/\d{3}\B/g;//每3個字斷開(從前面開始數)


str.match(check1);//["234", "567", "890"]
str.match(check2);//["123", "456", "789"]

英文數字逗點

【例】1234567890→1,234,567,890

function commas(x){
 return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
 //①從最末位往前數3碼(後面沒有數字的話,比對出3個數字)
 //②一直數一直數
 //③符合①就給他分隔符號
 //④\B比對非英文字的邊界,如果沒有\B的話會變成",123,456,789"
 //取代所有分隔符號為","

}

線上練習網站:RegexOne

CSS not selector

指定不要選到某class的元素

語法::not(.your-class)
h1.h1 h111
h1.h2 h222
h1.h3 h333
h1:not(.h1)
 color: red

指定不要選到某attribute的元素

抓取attribute名稱的範例
h1.h1(data-h1) h111
h1.h2(data-h2) h222
h1.h3(data-h3) h333
h1:not([data-h1])
 color: red
抓取attribute名稱+內容的範例
h1.h1(data-heading="h1") h111
h1.h2(data-heading="h2") h222
h1.h3(data-heading="h3") h333
h1:not([data-heading="h1"])
 color: red

多重條件

h1.h1 h111
h1.h2 h222
h1.h3 h333
h1
 color: red
 
h1:not(.h2):not(.h3)
 color: black

JavaScript Array methods 陣列相關方法1

本篇介紹的method

filter()[👧, 👦, 👩, 👨, 👵, 🧓] => [👦, 👨, 🧓]原本的陣列有 6 個,後來變成 3 個
map()[👧, 👦, 👩, 👨, 👵, 🧓] => [👧🏿, 👦🏿, 👩🏿, 👨🏿, 👵🏿, 🧓🏿]陣列內容都變成不一樣的顏色了(但數量一樣是 6 個)
sort()[👧, 👦, 👩, 👨, 👵, 🧓] => [👧, 👩, 👵 👦, 👨, 🧓]陣列的順序改變了
reduce()[👧, 👦, 👩, 👨, 👵, 🧓] => 👨‍👩‍👧陣列被綜合成一個東西了

本篇使用到的共用物件

const inventors = [
 { first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
 { first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
 { first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
 { first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
 { first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
 { first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
 { first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
 { first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
 { first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
 { first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
 { first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
 { first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
 ];

filter() 把陣列的數量變少,但是內容不變

filter() 裡面放一個函數,filter() 可以物件→物件,也能夠陣列→陣列

應用:篩選出出生於 16 世紀的投資客(出生年介於 1500 到 1600 之間)

const arr1=inventors.filter(function(item){
 if(item.year>=1500 && item.year<1600){
  return true;
 }
})

console.log(arr1); //[{ first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 }, { first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 }]

ES6 應用:箭頭函數(省略 return

const arr1=inventors.filter(item => item.year>=1500 && item.year<1600);

console.log(arr1); //[{ first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 }, { first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 }]

map() 陣列數量固定,但裡面內容置換

map() 可以物件→陣列,也可以陣列→陣列

應用:列出所有投資客的全名(first + last

const arr1=inventors.map(item => `${item.first} ${item.last}`);

console.log(arr1); //["Albert Einstein", "Isaac Newton", "Galileo Galilei", "Marie Curie", "Johannes Kepler", "Nicolaus Copernicus", "Max Planck", "Katherine Blodgett", "Ada Lovelace", "Sarah E. Goode", "Lise Meitner", "Hanna Hammarström"]

sort() 更動陣列順序

sort() 可以物件→物件,也能夠陣列→陣列
  • return 1:往後移動
  • return -1:往前移動

應用①:將投資客由老排到年輕(出生年數字越大代表越年輕,所以要越往後移動)

sort() 裡面的函數可以塞兩個參數,第一個值代表比較的第一個人、第二個值代表比較的第二個人,sort() 會幫忙把所有排列組合都列出來
const arr1=inventors.sort(function(a, b){
 if(a.year>b.year){
  return 1; //如果a的年紀比b小,a往後擺
 }else{
  return -1;
 }
});

console.table(arr1); //結果會出來一個由老排到小的物件

ES6 應用:箭頭函數 + if 簡寫

const arr1=inventors.sort((a, b) => a.year>b.year ? 1 : -1)

console.table(arr1);

應用②:將投資客的名字 (firstname) 開頭字母由A排到Z

const arr1=inventors.sort((a, b) => a.first>b.first ? 1 : -1)

console.table(arr1);

reduce() 將陣列內容輸出為單一值

reduce() 可以物件→單一值,也可以陣列→單一值

應用①:計算所有投資客總共活了多久

  • reduce() 裡面的函數的第一個參數為「累加器 accumulator」,第二個參數為「迭代中的元素 currentValue
  • reduce() 裡面的第二個值為一個數字,表示「累加器初始值 initialValue」(通常填 0
const value=inventors.reduce(function(total, item){
 return total += (item.passed-item.year);
}, 0)

console.log(value); //861

ES6 應用:箭頭函數

const value=inventors.reduce((total, item) => total += (item.passed-item.year), 0)

console.log(value); //861

應用②:統計各個交通工具出現的次數

//原始資料
const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car', 'truck' ];
var value=data.reduce((obj, item)=>{
 obj[item]++;
 return obj;
},{
 car: 0,
 walk: 0,
 truck: 0,
 bike: 0,
 van: 0
})

console.log(value); // {bike: 2, car: 5, truck: 3, van: 2, walk: 2}

符號分割字串 split()

字串的分割

split() 裡面填入一個字串形式的值,這個值用來切割其他要拿來切割的字串
var str='2020/01/01';
console.log(str.split('/')); //["2020", "01", "01"]

陣列=>字串的分割

陣列可以搭配 map() 轉換成字串在分割,但分割後的格式會變成陣列中的陣列
var arr=['2020/01/01 15:00', '2020/04/04 13:00', '2020/03/03 09:00'];
var arr2=arr.map(item => item.split(' '));
console.log(arr2); //[["2020/01/01", "15:00"], ["2020/04/04", "13:00"], ["2020/03/03", "09:00"]]
或乾脆只抓取分割後的某部分,組成新的陣列
var arr=['2020/01/01 15:00', '2020/04/04 13:00', '2020/03/03 09:00'];
var arr2=arr.map(item => {
 var [date, time]=item.split(' ');
 return date;
})
console.log(arr2); //["2020/01/01", "2020/04/04", "2020/03/03"]

shift() 切出第一個字串

var str="2020/01/01"
console.log(str.split('/').shift()); //"2020"

pop() 切出最後一個字串

var str="2020/01/01"
console.log(str.split('/').pop()); //"01"

檢查陣列是否包含特定值 includes()

includes() 可以用來檢查陣列是否包含某個值,回傳值為布林值
var arr=['2020/01/01', '2020/01/02', '2020/01/03'];

console.log(arr.includes('2020/01/01')) //true

結合 filter() 篩出特定值組合成一個新陣列

var arr=['2020/01/01', '2020/01/02', '2020/01/03', '2020/02/01', '2020/02/02', '2020/02/03'];

var arr2=arr.filter(item => item.includes('2020/01'));
console.log(arr2); //["2020/01/01", "2020/01/02", "2020/01/03"]

nodeList轉array

querySelectorAll()抓取下來的物件,雖然看起來跟array很像,但他其實是nodeList,所以不能直接適用於array可以用的一大堆好用method,例如map()reduce()等等
ul
 li apple
 li banana
 li grape

ES6法 展開運算符 Spread syntax

Spread syntax是ES6提供的方法,只要用[...]就能夠轉換了
var mylist=document.querySelectorAll('li');
var newlist=[...mylist];

傳統法 Array.from()

如果瀏覽器較舊不支援ES6 [...]大法的話,也能改用傳統的Array.from()
雖然不比ES6法簡潔,但也算方便
var mylist=document.querySelectorAll('li');
var newlist=Array.from(mylist);

console 相關method介紹

console系列除了開發抓蟲最常用的console.log()以外,還存在著許多便利的method,幫助在開發中更有智慧的抓蟲

字詞置換 %s

%ssstring的意思,可以做到在噴出除錯訊息時做到字詞置換的效果

var a='apple';
console.log('hello, I want a %s', a);

樣式變更 %c

在多人開發的環境中,console log時常會被上百個訊息給淹沒,這時可以透過%c自訂顯目的樣式讓他在很多訊息之中脫穎而出

var style='font-size: 60px; background: red; color: white';
console.log('%cImportant!', style);

%c如果放在訊息開頭的地方表示他從最頭開始就要套上醒目樣式,如果%c宣告在訊息中間的話則代表從指定的位置開始再做醒目提示

警告/錯誤/提示訊息

這三種在一般開發上都蠻少見,但在別人做好的套件上會常常看到

警告訊息 console.warn()

console.warn()用來提示警告性的訊息,在Chrome上的樣式是驚嘆號三角形+黃色底的造型

console.warn('我是警告訊息');

錯誤訊息 console.error()

console.error()用來提示錯誤性的訊息,在Chrome上的樣式是圓形叉叉+紅色底的造型

console.error('我是錯誤訊息');

提示訊息 console.info()

console.info()居然被新版的Chrome移除了,他現在用起來跟console.log()無異

驗證 console.assert()

console.assert(1===2);

第二個值可填可不填,填了如果出錯時他會幫你噴出自訂的錯誤訊息
(填了第二個值的範例:console.assert(1===2, '錯了');

清除 console.clear()

用了就會幫你把上面的歷史紀錄都清掉

檢視屬性 console.dir()

他可以一口氣噴出指定元素的所有屬性,像是offsetTopinnerHTMLparentNode、以及綁在他身上的各種事件

console.dir(document.body);

群組化呈現 console.group()

這也是給多人開發時使用的便利工具,尤其是在同一頁面同時噴出好幾百個log令人無法招架時,可以用console.group()將訊息歸類分群組

console.group('===登入頁測試區===');
console.log('假log1');
console.log('假log2');
console.log('假log3');
console.groupEnd();

console.group('===首頁測試區===');
console.log('假log1');
console.log('假log2');
console.log('假log3');
console.groupEnd();

上面的console.group()預設是將所有訊息展開,如果怕他太亂也可以試試看console.groupCollapsed(),是預設收合的樣式

console.groupCollapsed('===登入頁測試區 測試已完成===');
console.log('假log1');
console.log('假log2');
console.log('假log3');
console.groupEnd();

console.group('===首頁測試區===');
console.log('假log1');
console.log('假log2');
console.log('假log3');
console.groupEnd();

計算次數 console.count()

console.count()原意是計算次數,但可以巧妙的把它塞進某些function之中,觀察該函式被使用的次數(看執行效能?)

const arr=[1,3,5,7,9];
arr.reduce((total, number)=>{
 console.count();
 return total+=number;
},0);

reduce()看起來只是一個簡單的method而已,寫起來也不過幾行,沒想到一放入console.count()就發現他默默地被執行了5

計算時間 console.time()

console.time()可以計算一個動作執行時所花費的時間,藉以觀察效能
由於一般程式執行速度都很快,所以這邊用一個跟外部檔案抓資料的fetch()範例來看看花費時間

console.time("抓下資料");
fetch("https://gist.githubusercontent.com/ianchen0419/d499e572044655814f38aaf56c779823/raw/9876407c12b1e6bc5c31fe6e9f875ccfb82af43b/schedule.json")
 .then(data => data.json())
 .then(data => {
  console.timeEnd("抓下資料");
  console.log(data);
});

陣列+物件資料用表格呈現 console.table()

console.table()可以把一坨難看懂的陣列物件複合資料用表格呈現,可以一目瞭然增加易讀性

fetch("https://gist.githubusercontent.com/ianchen0419/d499e572044655814f38aaf56c779823/raw/9876407c12b1e6bc5c31fe6e9f875ccfb82af43b/schedule.json")
 .then(data => data.json())
 .then(data => {
  console.table(data);
});

圖層混合模式 mix-blend-mode

mix-blend-mode是一個CSS屬性,可以對圖層混合做出一些調整(類似Photoshop),IE不能用
本篇介紹2個簡單的去背效果

本次使用的範例圖

這張圖沒有去背,如果放到有色背景上會變這樣

色彩增值模式 multiply

背景色與目標圖片相乘後,成為新的顏色代換掉圖片自己的顏色

  • 背景色×目標圖片的黑色部分黑色(原地不動的意思)
  • 背景色×目標圖片的白色部分背景色
.image-outer
 img(src="https://raw.githubusercontent.com/ianchen0419/notes/master/img/圖層混合模式%20mix-blend-mode/01.png")
.image-outer
 background: orange
 display: inline-block
 padding: 40px
 img
  mix-blend-mode: multiply

原本白色的部分會被背景的橘色吃掉,做了簡單的去背處理
(但只適用在在圖片只有黑白兩色的情況而已!)

濾色模式 screen

背景色與目標圖片的補值相乘後,成為新的顏色代換掉圖片自己的顏色
上面的multiply會做去背,這次的screen則會做去黑

  • 背景色×目標圖片的黑色部分背景色
  • 背景色×目標圖片的白色部分白色(原地不動的意思)
.image-outer
 background: orange
 display: inline-block
 padding: 40px
 img
  mix-blend-mode: screen

這次換層黑色的部分被背景的橘色吃掉,達成了去黑效果
(一樣只適用在在圖片只有黑白兩色的情況而已!)

視差滾動

視差滾動,讓圖片不會隨著卷軸上下移動,有強調的效果

br*10
.my-image
br*30

上下各放10個與30<br>,創造出足以產生拉霸的網頁

.my-image
 width: 600px
 height: 400px
 background: url(https://picsum.photos/600/400)
 background-attachment: fixed
 background-size: cover
 margin: 0 auto

dataset

dataset是一個方便取用元素屬性的規範
基本上元素屬性的命名是任意,但在dataset的規範下,會命名成以data-開頭的名稱,例如

#div(data-color="green" data-size="20")

如此一來,可以用強大的dataset物件快速取得屬性值

div.dataset.color; //'green'

div.dataset.size; //'20'

針對不能用dataset的瀏覽器(例如很舊的IE),就只能使用傳統的getAttribute()

div.getAttribute('data-color'); //'green'

div.getAttribute('data-size'); //'20'

PHP 會員留言板系統

延續前篇:PHP 會員登入系統

結構

  • MySQL留言資料庫(message)
  • 留言板頁面(msg.php)
  • 留言新增功能(msg-add.php)
  • 留言刪除功能(msg-del.php)

MySQL留言資料庫(message)

編碼:utf8_unicode_ci

名稱 型態 備註
id int AI
guest_id int
content text
date date

留言板頁面(msg.php)

  • 結合 message 的 guest_id 與 member 的 id
    在 message 的提取迴圈裡面,寫下指令 mysql_query( 讀取 member 資料表,WHERE條件設定 id=’$row[guest_id]’ )
<?php
mysql_connect("localhost", "root", "");
mysql_select_db("hahow");
mysql_query("SET NAMES UTF8");

//如果沒有登入的SESSION,就轉址
if (isset($_SESSION["email"])==FALSE) {
 header('Location: login.php');
}
?>
<!DOCTYPE html>
<html>
<head>
 <title>Message Board</title>
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>
<div class="container">
 <h3 class="text-center">會員留言板</h3>
 <hr>

<?php
$re=mysql_query("SELECT * FROM message");
//檢査有沒有留言數( message 資料表的資料筆數是否大於0):參考
if(mysql_num_rows($re)>0){
 //如果有留言,一筆一筆印出留言
 //使用 while 迴圈從 message 資料表一筆一筆讀取留言的方法:參考
 while($row=mysql_fetch_array($re))
 {
 //mysql_query() 括弧裡面的指令,使用變數時,要用單引號包起來,參考
 $memberRe=mysql_query("SELECT * FROM member WHERE id='$row[guest_id]'");
 $memberRow=mysql_fetch_array($memberRe);

 echo "<div class=\"panel panel-default\">
 <div class=\"panel-heading\">$memberRow[name] 
 <span class=\"pull-right\">$row[date]
 //以更改網址的方式強制讀取GET表單資料
 //正常的流程:使用者在GET表單輸入資料→送出轉址→新網址裡面有使用者輸入的資料
 //轉址到有特定 GET 表單 id 的 msg-del.php
 <a href=\"msg-del.php?id=$row[id]\" class=\"btn btn-danger btn-xs\">
 DELETE
 </a>
 </span>
 </div>
 <div class=\"panel-body\"> $row[content]
 </div>
 </div>";
 }
}

else{
 //沒有留言的話
 echo "<p class=\"text-center\">沒有任何訊息!</p>";
}
?>
 <hr>
 <p class="pull-right">以 <?php echo $_SESSION["name"]; ?> 的身份留言</p>
 <h4>新增留言</h4>
 <form action="msg-add.php" method="post">
 <textarea name="msg" class="form-control"></textarea>
 <br>
 <input type="submit" name="submit" value="送出" class="btn btn-primary btn-sm pull-right">
 </form>
</div>
</body>
</html>

留言新增功能(msg-add.php)

<?php
require("msg.php");

//設定日期格式
$date=date("Y-m-d");

$SaveNewMsg=mysql_query("INSERT INTO message(guest_id, content, date) 
//對應 msg.php 新增留言區的 POST表單
VALUES('$_SESSION[id]','$_POST[msg]','$date')");

//檢査
if(!$SaveNewMsg){
 echo "留言失敗";
}else{
 echo "留言成功";
}

//新增完畢轉回留言板
header('Location: msg.php');
?>

留言刪除功能(msg-del.php)

<?php
require("msg.php");

//抓取 msg.php 布好的GET表單的刪除ID
mysql_query("DELETE FROM message WHERE id='$_GET[id]'");

//刪除完畢轉回留言板
header('Location: msg.php');
?>

範例:會員登入網站

測試用帳號:123@123
測試用密碼:123

結語

  • 善用 Sublime + SFTP,存檔即上傳主機可以節省時間
  • bug 可能出現在各種地方,資料庫連線,上一份檔案的 session 沒有寫好,都可能有錯,必須小心

Date()

  • 抓現在是西元幾年 new Date().getFullYear()
  • 抓現在是幾點 new Date().getHours()
  • 抓現在是幾分 new Date().getMinutes()
  • 抓現在是幾秒 new Date().getSeconds()

顯示現在時間

function runTime(){
 var now=new Date();
 var seconds=now.getSeconds();
 var mins=now.getMinutes();
 var hours=now.getHours();
 console.log(`現在是${hours}時${mins}分${seconds}秒`);
}
//每一秒偵測一次時間
setInterval(runTime, 1000);

PHP 會員登入系統

結構

  • MySQL會員資料庫(member)
  • 登入頁面(login.php)
  • 註冊頁面(regist.php)
  • 會員頁面(member.php)

MySQL會員資料庫結構(member)

編碼:utf8_unicode_ci

名稱 型態 備註
id int AI
name text
email text
password text
nick text

登入頁面 login.php

<?php
//解決網頁亂碼問題
mysql_query("SET NAMES UTF8");

$error_flag = FALSE;
$notfound_flag = FALSE;

//對資料庫伺服器進行連線,並選擇對應的會員資料庫
mysql_connect("localhost", "root", "");
mysql_select_db("hahow");

//如果收到 POST 表單送來的登入資料,到資料庫檢査是否有這個人存在
//(使用 mysql_query("SELECT ...... "),然後把回傳的東西透過 mysql_fetch_array(......) 來檢査)
$result=mysql_query("SELECT * FROM member");


//如果有找到,檢査密碼是否相符
while($row = mysql_fetch_array($result)){

 //先檢査使用者有沒有輸入資料
 if(empty($_POST["email"])==FALSE && empty($_POST["pass"])==FALSE){

 //防範攻擊
 $userEmail=$_POST["email"];
 $userEmail=mysql_real_escape_string($userEmail);
 $userPassword=$_POST["pass"];
 $userPassword=mysql_real_escape_string($userPassword);
 //有輸入資料的話,再來看輸入的email跟資料庫是否一致
 if($row["email"]==$_POST["email"]){

 if($row["password"]==$_POST["pass"]){
 //如果相符合,則設定 Session(記得要先 session_start()!),並轉址到會員中心(member.php)
 session_start();
 $_SESSION["email"]=$_POST["email"];
 $_SESSION["password"]=$_POST["pass"];
 $_SESSION["name"]=$row["name"];
 $_SESSION["id"]=$row["id"];

 //讓網頁轉址的 PHP 寫法:header('Location: member.php');
 header('Location: member.php');

 }else{
 //如果不符合,則設定 $error_flag 為 TRUE,繼續顯示網頁内容
 $error_flag = TRUE;
 break;
 }

 }else{
 //如果沒有找到,則設定 $notfound_flag 為 TRUE,繼續顯示網頁内容
 $notfound_flag = TRUE;
 }

 }else{
 //如果沒收到,繼續顯示網頁内容
 }
 
}
?>

<!DOCTYPE html>
<html>
<head>
 <title>會員登入</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>

<div class="container">
 <div class="row jumbotron">
 <div class="col-md-6 col-md-offset-3">
 <h2 class="text-center">會員登入</h2><br/>
 <form action="<?php echo htmlspecialchars($_SERVER[">" method="POST">
 <input class="form-control input-lg" id="pass" type="text" name="email" required="TRUE" placeholder="E-Mail"/><br/>
 <input class="form-control input-lg" id="pass" type="password" name="pass" required="TRUE" placeholder="密碼"/><br/>
 <input class="btn btn-primary btn-lg btn-block" type="submit" value="登入"/><a class="btn btn-default btn-lg btn-block" href="register.php">會員註冊</a>
 </form>
 <br/>
 <?php if($error_flag){ ?>
  <div class="alert alert-danger alert-dismissible" role="alert">
  <button class="close" type="button" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button> 密碼錯誤!
  </div>
 <?php }?>

 <?php if($notfound_flag){ ?>
  <div class="alert alert-danger alert-dismissible" role="alert">
  <button class="close" type="button" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button> 未找到本使用者,請重新確認!
  </div>
 <?php }?>
 </div>
 </div>
</div>
</body>
</html>

會員頁面 member.php

<?php
//關閉系統提示
error_reporting(0);
session_start();

// 檢査是否有登入(Session 有被設定)
if(isset($_SESSION["email"])==FALSE) {
 //如果沒有,轉址到登入頁面
 header('Location: login.php');
}
?>

<!DOCTYPE html>
<html>
<head>
 <title>會員中心</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>
<div class="container">
 <div class="row jumbotron">
 <div class="col-md-6 col-md-offset-3"> 
 <h2 class="text-center">會員登入</h2>
 <!-- 在這裡請把 HTML 修改一下,讓他可以顯示出現在登入的會員姓名! -->
 <!-- 提示:先取用 Session 中登入的人的 ID,然後再去資料庫中找出對應 ID 的姓名。 -->
 <h3>姓名:<?php echo $_SESSION["name"];?></h3>
 <a class="btn btn-primary btn-lg btn-block" href="msg.php">留言板</a>
 </div>
 </div>
</div>
</body>
</html>

註冊頁面 register.php

<?php

$noInfo_flag = false;
$duplicate_flag = false;
$success_flag=false;
$fail_flag=false;

//關閉提示
error_reporting(0);
mysql_connect("localhost", "root", "");
mysql_select_db("hahow");
mysql_query("SET NAMES UTF8");

//送出鍵按出後
if(isset($_GET['submit'])==true){
 //檢査所有欄位有沒有輸入
 if(empty($_GET['email'])==true || empty($_GET['password'])==true || empty($_GET['name'])==true || empty($_GET['nick'])==true){
 //有缺的話,叫使用者寫完
 $noInfo_flag = true;
}
}

//送出鍵按出,使用者也有輸入資料的情況
if(isset($_GET['submit'])==true && empty($_GET['email'])==false && empty($_GET['password'])==false && empty($_GET['name'])==false && empty($_GET['nick'])==false){

 //用 WHERE 檢查是否重複註冊
 //mysql_query()裡面要用'' 參考
 $result=mysql_query("SELECT * FROM member WHERE email='$_GET[email]'");
 $row=mysql_fetch_array($result);
 if($row["email"]==$_GET["email"]){
 重覆到的話,退回
 $duplicate_flag = true;
 }else{
 
 //沒有重複到,寫入資料
 $SaveNewData=mysql_query("INSERT INTO member (name, email, password, nick) VALUES('$_GET[name]','$_GET[email]','$_GET[password]','$_GET[nick]')");
 
 //檢查註冊是否成功
 if(!$SaveNewData){
 $fail_flag=true;
 }else{
 $success_flag=true;
 }
 }
}

?>

<!DOCTYPE html>
<html>
<head>
 <title>會員註冊</title>
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>
<div class="container">
 <div class="row jumbotron">
 <div class="col-md-6 col-md-offset-3"> 
 <h2 class="text-center">會員註冊</h2>
 <hr>
 <form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="GET">
 <div class="form-group">
 <label for="input-email">Email 帳號 *</label>
 <input type="email" class="form-control" id="input-email" name="email">
 </div>
 <div class="form-group">
 <label for="input-name">真實姓名 *</label>
 <input type="text" class="form-control" id="input-name" name="name">
 </div>
 <div class="form-group">
 <label for="input-nick">匿稱 *</label>
 <input type="text" class="form-control" id="input-nick" name="nick">
 </div>
 <div class="form-group">
 <label for="input-password">密碼 *</label>
 <input type="password" class="form-control" id="input-password" name="password">
 </div>
 </div>
 <br>
 <input type="submit" class="btn btn-primary btn-lg btn-block" value="註冊" name="submit">
 </form>
 <?php if($noInfo_flag){ ?>
 <div class="alert alert-danger alert-dismissible" role="alert">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 請輸入所有欄位!
 </div>
 <?php }?>

 <?php if($duplicate_flag){ ?>
 <div class="alert alert-danger alert-dismissible" role="alert">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 此 Eamil 帳號已經註冊過!
 </div>
 <?php }?>

 <?php if($success_flag){ ?>
 <div class="alert alert-danger alert-dismissible" role="alert">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 註冊成功!
 </div>
 <?php }?>

 <?php if($fail_flag){ ?>
 <div class="alert alert-danger alert-dismissible" role="alert">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 註冊失敗!
 </div>
 <?php }?>
 </div>
 </div>
</div>
</body>
</html>

範例:會員登入網站

測試用帳號:123@123
測試用密碼:123

接續後篇:會員留言板系統

箭頭函數

箭頭函數是ES6新規範,旨在將method裡面呼叫的functions做出字面上的簡化,例如

window.addEventListener('keydown', function(e){
 console.log(e.keyCode);
})

上面這段程式碼表示在畫面上按下任何一個按鍵,會回傳該按鍵的keyCode
經過箭頭函式簡化後,可以變成這樣

window.addEventListener('keydown', e=>console.log(e.keyCode));

簡化後,他跟第一版程式具有一樣的功能

簡化進程詳解

首先,箭頭函式其實只是把function拿掉,並在(){}之間加了一個箭頭=>以代表他其實是一個函式而已

window.addEventListener('keydown', (e)=>{console.log(e.keyCode);})

但是,()裡面只有一個參數而已,{}裡面也只有一行程式,實在沒有框起來的必要
所以當()的參數只有一位時可以省略括弧
而當{}的程式只有一行時,也能省略大括弧

window.addEventListener('keydown', e=>console.log(e.keyCode));

經過最終進化後成了超簡明版本

與陣列的關係

箭頭函數經常跟陣列相關的method搭配使用,例如nodeList的新式迴圈forEach()、陣列計算的reduce()、陣列整形的map()

forEach() 範例

forEach()可以代替jQuery$()簡便的抓取nodeList並且做出迴圈處理

ul
 li apple
 li grape
 li banana
const mylist=document.querySelectorAll('ul li');
mylist.forEach(item => console.log(item.textContent))

//apple
//grape
//banana

reduce() 範例

const arr=[1,3,5,7,9];
arr.reduce((total,number)=>{
 return total+=number;
},0)

map() 範例

const arr=['apple', 'grape', 'banana'];
arr.map(item=>`GOOD ${item}`);
//(3) ["GOOD apple", "GOOD grape", "GOOD banana"]

綁定多種事件時的做法

畫面上有3個按鈕,每個按鈕都要各榜clickmouseover事件,一樣可以用同一個forEach()寫,各個事件之間用分號;隔開

button click
button click
button click
const btns=document.querySelectorAll("button");
btns.forEach((btn, n) => {
  btn.addEventListener("click", function(){
   console.log(`you clicked btn ${n+1}`);
 });
  btn.addEventListener("mousemove", function(){
   console.log(`you touched btn ${n+1}`);
 })
})

keyCode

鍵盤上的每個鍵都有自己的識別代碼,稱為keyCode
在實作key相關事件時,keyCode是不可或缺的情報

網頁查詢keyCode

KeyCode Info

進入上述網站後,隨便按下想要查詢的按鍵,畫面就能顯示出該案件的keyCode

JavaScript查詢keyCode

window.addEventListener("keydown",
 function(e){
  console.log(e.keyCode);
  //console.log(e.which);
 }
);

打開網頁,隨便按下一個案件,就能看到開發者工具回傳該案件的keyCode代碼
除了e.keyCode可以查到代碼外,e.which也能夠查到代碼。

Bootstrap 格線系統

外層是橫向的row,內層切成12row

.row
 .col-4
 .col-8

不使用CSS,只用html class指定排版空間

格線RWD

關鍵字解釋寬度定義
.col-超小螢幕所有
.col-sm-小螢幕576px以上
.col-md-中螢幕720px以上
.col-lg-大螢幕992px以上
.col-xl-超大螢幕1200px以上

格線系統的寬度指定為向上繼承模式,意即

  • 如果只有指定最小螢幕.col-,那不論裝置多小或多大,他都會套用到.col-的設定
  • 如果只有設定最大螢幕.col-xl-,那他只有在螢幕大於1200px的情況,才會套用到.col-xl-指定的比例,只要螢幕小於1200px,他全部都會變成.col-12

所以,如果沒有希望版面有任何RWD的設定,不論螢幕多小或多大格線系統的比例都一樣的話,建議使用.col-這組class

Access 過長文字被截斷

用Microsoft Access開啟.csv文件的時候,如果有欄位內容文字太長超過255個字,載入Access後他會幫你自動消除255字以後的字元,導致內容缺失

將資料類型改成「長文字」

匯入.csv檔案後的設定畫面,按下左下角的「進階」,彈出匯入規格的小視窗
在欄位資訊的資料類型選「長文字」

這樣,他就能編出255字以後的內容了

定位三劍客:scrollTop、offsetTop、clientTop

scrollTop:滑動多少的距離

#outer
 #inner
#outer
 height: 500px
 border: 1px solid #000
 overflow-y: scroll
 
#inner
 height: 800px
 background: teal

上述範例在滑動到最底時,外層的#outer一共是滑動了300px,因為#outer高度只有500px,但內層的#innter卻有800px,外層不夠的300px就會變成滑動的距離。當滑到最底部時,一共滑了300px

網頁整體的拉霸

若想取得現在整體網頁拉霸滑動到哪裡的位置,可以用以下JS

document.documentElement.scrollTop

如果用了document.body.scrollTop不論滑動到哪裡都會回傳0,所以必須用documentElement(實測Chrome/IE/FireFox/Safari)

offsetTop:本體相對於offsetParent的距離

offsetTop的指涉的概念大概就像上圖這樣
offsetParent所在的位置可以透過targetElement.offsetParent來查詢

offsetParent就是擁有position: relative定位的母層元素
如果我把上圖中的<article>的相對定位屬性移除,offsetParent會再找到更上面擁有相對定位的母層
如果把上面所有相對定位都掃掉,offsetParent就會回傳<body>

※例外:當目標元素為position: fixed時,由於他是相對於視窗做定位,不存在什麼絕對母層,所以他的offsetParent會回傳null

padding / border 會不會影響到offsetTop呢?
由於padding / border 都是屬於上圖咖啡色方塊的目標元素之中
所以他們不會影響到offsetTop

clientTop:簡單來說就是border-top

但其實clientTop指涉的不是border-top,他是指元素外層與內層之間的距離。
如果把OS設定為阿拉伯文 / 希伯來文這種拉霸在左邊的語言,就可以看出端倪

這時的clientLeft會加上拉霸的15px,所以總和值為50px+15px=65px

參考資料

要素サイズとスクローリング

SVG stroke-linecap

stroke-linecap用來描述線段<line>的端點呈現方式,提供三種屬性

  • stroke-linecap: butt 預設值,端點不外擴也不變形
  • stroke-linecap: round 端點外擴並呈現圓角
  • stroke-linecap: square 端點外擴並呈現方角

(中間畫一條<path>比較好觀察外擴情形)

butt(預設值)

svg(viewbox="-50 -50 100 100" width="500")
    line(x1=-40 y1=0 x2=40 y2=0 stroke="black" stroke-width=8 stroke-linecap="butt")
    path(d="M-40, 00 L40, 0" stroke="white")

round

svg(viewbox="-50 -50 100 100" width="500")
    line(x1=-40 y1=0 x2=40 y2=0 stroke="black" stroke-width=8 stroke-linecap="round")
    path(d="M-40, 00 L40, 0" stroke="white")

square

svg(viewbox="-50 -50 100 100" width="500")
    line(x1=-40 y1=0 x2=40 y2=0 stroke="black" stroke-width=8 stroke-linecap="square")
    path(d="M-40, 00 L40, 0" stroke="white")

SVG漸層

<defs>呼叫<linearGradient>

SVG的漸層要先在文件開頭定義<def>標籤,並且在裡面設定<linearGradient>寫漸層指示
然後再回到CSS用fill呼應漸層的ID

svg(viewbox="-50 -50 100 100")
 defs
  linearGradient#mycolor
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 circle(cx=0 cy=0 r=20)
svg
 border: 1px solid #000
 width: 500px
circle
 fill: url(#mycolor)

預設的漸層方向是由左至右

漸層方向

預設漸層方向是由左至右,設定漸層方向要在<linearGradient>寫參數設定。值填入0%100%

  • 起點:x1y1
  • 終點:x2y2

由左至右(預設值)

svg(viewbox="-50 -50 100 100" width="500")
 defs
  linearGradient#mycolor(x1="0%" y1="0%" x2="100%" y2="0%")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50 fill="url(#mycolor)")

由右至左

svg(viewbox="-50 -50 100 100" width="500")
 defs
  linearGradient#mycolor(x1="100%" y1="0%" x2="0%" y2="0%")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50 fill="url(#mycolor)")

由下至上

svg(viewbox="-50 -50 100 100" width="500")
 defs
  linearGradient#mycolor(x1="0%" y1="100%" x2="0%" y2="0%")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50 fill="url(#mycolor)")

由上至下

svg(viewbox="-50 -50 100 100" width="500")
 defs
  linearGradient#mycolor(x1="0%" y1="0%" x2="0%" y2="100%")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50 fill="url(#mycolor)")

漸層渲染方式 gradientUnits

gradientUnits指定漸層的渲染方式,看是要以該生效物件為區域,在裡面切%畫漸層,還是以整個SVG為區域,整份SVG切%畫漸層,然後再把指定物件框出來

objectBoundingBox (預設值)

gradientUnits=objectBoundingBox 只針對生效物件畫

svg(viewbox="-50 -50 100 100")
 defs
 
 linearGradient#mycolor(gradientUnits="objectBoundingBox")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=-25 y=-25 width=50 height=50)
svg
 border: 1px solid #000
 width: 500px
rect
 fill: url(#mycolor)

userSpaceOnUse

gradientUnits=userSpaceOnUse 針對整份SVG畫,在切出指定元素份的漸層
※這裡有一個Bug,當在使用userSpaceOnUse的時候,SVG的viewbox起點一定要從0,0開始。不然漸層的位置也會一起偏掉

svg(viewbox="0 0 100 100")
 defs
 
 linearGradient#mycolor(gradientUnits="userSpaceOnUse")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 rect(x=25 y=25 width=50 height=50)
svg
 border: 1px solid #000
 width: 500px
rect
 fill: url(#mycolor)

<line>漸層

由於<line>不視作一個區域(他不像<rect><circle>可以用fill填色)
所以<line>不能用好用的gradientUnits漸層,只能用userSpaceOnUse漸層。所以效果那些就不是那麼一目瞭然

svg(viewbox="0 0 100 100")
 defs
  linearGradient#mycolor(gradientUnits="userSpaceOnUse")
   stop(offset="5%" stop-color="skyblue")
   stop(offset="80%" stop-color="teal")
 line(x1=25 y1=50 x2=75 y2=50)
svg
 border: 1px solid #000
 width: 500px
line
 stroke: url(#mycolor)
 stroke-width: 3px

PHP與MySQL的資料庫互動

PHP版本:7.3.11
MySQL版本:8.0.18
執行環境:Mac OS 10.15.3

資料庫連線 mysqli_connect()

網址127.0.0.1
登入帳號root(預設)
密碼zaq1@WSX

選擇資料庫

選擇test資料庫(帶有水庫圖示的就叫做資料庫,資料庫底下的叫做資料表)

$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');

檢查是否連線成功

if(!$connect){
    echo '連線失敗';
}else{
    echo '連線成功';
}

抓取資料與迴圈處理

test裡面有一張資料表mylist,記載了一些水果的品項與價格

⑴ 抓取資料mysqli_query(),並且存成變數$re

mysqli_query()要放2個參數,
第1個是mysqli_connect的連線資訊
第2個放MySQL的語法
$re=mysqli_query($connect, 'SELECT * FROM mylist');

⑵ 回傳資料處理 mysqli_fetch_array()

mysqli_fetch_array()在沒寫迴圈下只會回傳第一筆符合條件的資料
所以搭配while迴圈,只要條件符合,就會一筆筆將資料丟出來

while($row=mysqli_fetch_array($re)){
    echo $row['name'].'<br/>';
}

進階:處理完整資料

資料表mylist裡面有「水果名」、「序號」、「價格」這3項資訊
更進一步地將所有資料都印出來

$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');
if(!$connect){
    echo '連線失敗';
}else{
    echo '連線成功';
}

echo '<hr>';

echo '<table border="1">';

echo '<caption>水果價目表</caption>';

echo 
    '<thead>
        <tr>
            <th>序號</th>
            <th>水果名</th>
            <th>價格</th>
        </tr>
    </thead>';

echo '<tbody>';

$re=mysqli_query($connect, 'SELECT * FROM mylist');
while($row=mysqli_fetch_array($re)){
    echo '<tr>';

    echo '<td align="center">'.$row['index'].'</td>';
    echo '<td>'.$row['name'].'</td>';
    echo '<td>$'.$row['price'].'元</td>';

}

動態查詢:MySQL指令塞變數

mysqli_query()塞變數$_GET['name'],需注意引號" / '必須要包成以下格式

"SELECT * FROM mylist WHERE name='$_GET[name]'"

水果店今天要做查價網頁,使用者在網頁輸入水果名,系統回傳價格

<h2>請輸入品項名</h2>
<form action="" method="GET">
    <input type="text" name="name" />
    <input type="submit" name="submit" value="查詢" />
</form>
$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');

if(!isset($_GET['submit'])) return;

$re=mysqli_query($connect, "SELECT * FROM mylist WHERE name='$_GET[name]'");
$row=mysqli_fetch_array($re);
if(!$row){
    echo '查無此人';
}else{
    echo '查詢品項:'.$row['name'].'<br>';
    echo '價格:$'.$row['price'].'元';
}

總筆數 mysqli_num_rows()

mysqli_num_rows()會回傳$result總共抓了幾筆資料

$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');
$re=mysqli_query($connect, "SELECT * FROM mylist");
$total=mysqli_num_rows($re);
echo '總共有'.$total.'筆資料';

SQL Injection 資料庫隱碼注入攻擊

假設今天是做水果會員網站,用戶可以用水果ID登入,目前一共有4個ID

<h2>請輸入ID登入</h2>
<form action="" method="GET">
    <input type="text" name="id" />
    <input type="submit" name="submit" value="登入" />
</form>
$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');

if(!isset($_GET['submit'])) return;

$re=mysqli_query($connect, "SELECT * FROM mylist WHERE name='$_GET[id]'");
$row=mysqli_fetch_array($re);
if(!$row){
    echo '登入失敗';
}else{
    echo '您已經登入'.'<br>';
    echo '您的ID:'.$row['name'].'<br>';
}

如果有駭客在前台<input>輸入' OR '1'='1

這時,後端的MySQL搜尋語法就會變成

SELECT * FROM mylist WHERE name='' OR '1'='1'

'1'='1'一定會為true,所以駭客就能騙過程式,成功登入系統

隱碼注入對策 mysqli_escape_string()

mysqli_escape_string()可以讓值變成不具程式碼效力的普通字串
因此建議掛在任何使用者輸入的內容上,以防止被駭客攻擊

$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');

if(!isset($_GET['submit'])) return;

$safe_id=mysqli_escape_string($connect, $_GET['id']);
$re=mysqli_query($connect, "SELECT * FROM mylist WHERE name='$safe_id'");
$row=mysqli_fetch_array($re);
if(!$row){
    echo '登入失敗';
}else{
    echo '您已經登入'.'<br>';
    echo '您的ID:'.$row['name'].'<br>';
}

MySQL 基本知識

資料型別

參數名稱意義
text文字
varchar固定型態的文字
int整數數字
bool布林值

phpmyadmin介面操作:建立資料庫與資料表

點選右邊水庫圖示旁的新增後,輸入新資料庫名,編碼選uf8_unicode_ci
(避免某些中文國字出不來)

接者建立資料庫底下的資料表,輸入好資料表名後,按「建立」

建立資料欄位,AI代表Automatic Increment,自動累加
如果資料欄位有索引欄,可以勾選AI,他每新增一筆就會給+1

MySQL指令操作

MySQL指令操作可以透過端末終端機,或從phpmyadmin的SQL頁籤進入,撰寫指令

建立資料表

CREATE TABLE newsheet2(
 ID INT,
 name TEXT,
 price INT,
 weight INT
)

塞資料進去

在資料表newsheet塞一筆資料進去

欄位
nameapple
price100
weight12
INSERT INTO newsheet(name, price, weight) VALUES ('apple', 100, 12)

刪資料

要把name="badbad"這筆資料刪掉

DELETE FROM newsheet WHERE name="badbad"

撈資料

SELECT * FROM newsheet WHERE name="apple"

SELECT後面接要搜尋的「資料欄位」,*代表全部欄位
WHERE後面接搜尋條件,可省略

SELECT * FROM newsheet WHERE name="apple" AND price=100

多條件篩選,關鍵字意涵請參考下表

關鍵字意思
AND
OR
BETWEEN ★★★ AND ☆☆☆指定區間

更新(修改)資料

要把banana的價格改成500

UPDATE newsheet SET price=500 WHERE name="banana"

PHP 陣列

陣列

建立陣列

$fruits=array('apple', 'banana', 'orange', 'grape');

印出整個陣列

print_r($fruits);
( [0] => apple [1] => banana [2] => orange [3] => grape )

印出特定索引值的陣列

print_r($fruits[0]);
apple

自訂索引值

PHP的陣列可以自訂索引值,做出類似物件的效果

$fruits2=array('name'=>'apple', "price"=>100, 'weight'=>10);
print_r($fruits2['name']);
apple

JS動畫 requestAnimationFrame

前端動畫

在前端領域裡,一共有幾種方式可以處理動畫

  • CSS animation 參考舊文
  • CSS transition
  • SVG SMIL <animate> 參考舊文
  • JavaScript setInterval
  • JavaScript setTimeout
  • JavaScript requestAnimationFrame

requestAnimationFrame是一個專門用來處理動態的方法,相對於前5項作法,其用法也相當複雜(但同時也很精細)
注意他不支援IE9(含)以下

RAF(略)用法初探

requestAnimationFrame的用法跟setInterval / setTimeout有一點點很像
這3種動畫函數都是window呼叫(並且都能省略window)
塞的第一個值都必須是動作的function
不過,setInterval / setTimeout都要指定第二個值:「秒數」
然後RAF不用塞秒數,所以我們只要寫成這樣就好▼

window.requestAnimationFrame(function(timestamp){
 console.log(timestamp)
})

注意RAF塞進去的第一個值:函數的裡面
我又在塞了一個callback name:timestamp
這個callback name名字隨意取就行,不叫timestamp也可以

這個timestamp代表什麼。我們開啟開發者工具看看console.log(timestamp)的回傳結果

timestamp回傳的結果是「毫秒」

第一個值15147.488毫秒,換算成正常秒數約等於15秒。代表我打開這個網頁後,等了大約15秒後,我才打開了開發面板,鍵入了window.requestAnimationFrame(...)這坨函數

第二個值16281.32毫秒,約等於16秒。等於網頁準備好後過了16秒,window.request...(...)才被執行(所以他跟第一個console相距1秒)

但是,實務上執行動畫時不能夠像這樣一直key函數上去,所以需要改寫成循環函數,讓RAF可以不斷地被重複執行

function call(timestamp){
 console.log(timestamp);
 window.requestAnimationFrame(call);
}

window.requestAnimationFrame(call);

【補充說明】
想要抓到RAF的index時,可以這樣做

function call(timestamp){
 var idx=window.requestAnimationFrame(call);
 console.log(idx);
}

window.requestAnimationFrame(call);

實作動態

本次實作利用rgb參數調整的方式
做出動態的變色效果

  • b值最小為0
  • b值最大為255

⑴ 用canvas畫一個圓

canvas#canvas(width=500 height=500)
canvas
 border: 1px solid #000
 width: 250px
 height: 250px
var ctx=canvas.getContext('2d');
ctx.beginPath();
ctx.arc(250, 250, 100, 0, 2*Math.PI, false);
ctx.fill();

⑵ 加入RAF函數

定義每毫秒colorIndex就會跑一次

  • colorIndex加到255時,反向遞減
  • colorIndex減到0時,又變成遞增
var colorIndex=0;
var isPlus=true;

function call(timestamp){
 if(colorIndex==255){
  isPlus=false;
 }
 
 if(colorIndex==0){
  isPlus=true;
 }
 
 if(isPlus==true){
  colorIndex++;
 }else{
  colorIndex--;
 }
 
 ctx.fillStyle=`rgb(0, 150, ${colorIndex})`;
 ctx.fill();
 
 window.requestAnimationFrame(call);
}

window.requestAnimationFrame(call);

⑶ RAF的停止

停止運作要用到cancelAnimationFrame這個方法
用法如下

cancelAnimationFrame(★這裏填入要停下的RAF★)

★要停下的RAF★可以用變數的方式預先儲存好
完整範例如下

canvas#canvas(width=500 height=500)
button(onclick="stopAnimation()") STOP IT
canvas
 border: 1px solid #000
 width: 250px
 height: 250px
var ctx=canvas.getContext('2d');
ctx.beginPath();
ctx.arc(250, 250, 100, 0, 2*Math.PI, false);
ctx.fill();

var colorIndex=0;
var isPlus=true;
var myAnimation;

function call(timestamp){
 if(colorIndex==250){
  isPlus=false;
 }
 
 if(colorIndex==0){
  isPlus=true;
 }
 
 if(isPlus==true){
  colorIndex++;
 }else{
  colorIndex--;
 }
 
 ctx.fillStyle=`rgb(0, 150, ${colorIndex})`;
 ctx.fill();
 
 myAnimation=window.requestAnimationFrame(call);
}

window.requestAnimationFrame(call);

function stopAnimation(){
 window.cancelAnimationFrame(myAnimation);
}

會動的圓形進度條

本範例使用SVG處理圖形,並搭配使用stroke-dasharraystroke-dashoffset讓圖形能夠動態增長
注意本範例僅能適用在stroke類圖形,如果UI設計師把圖形轉成path的話,它就不適用了

stroke-dasharray屬性介紹

stroke-dasharray描述實線與虛線的配置,用法如下

stroke-dasharray: 實線值 虛線值

實線值與虛線值的單位不是px也不是pt
他的單位會跟viewbox的設定一起跑

svg(viewbox="-50 -50 100 100")
 line(x1=-50 y1=0 x2=50 y2=0)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 line
  stroke: #000
  stroke-dasharray: 10 20

本例viewbox的設定值為-50 -50 100 100,即表示x軸起點值為-50,且長度為100,因此x軸的終點值為50

stroke-dasharray: 10 20的指定下,設定實線(黑線)為10,虛線為20
所以整個線段的虛實分佈為

-50-40實線段
-40-20虛線段
-20-10實線段
-1010虛線段
1020實線段
2040實線段
4050實線段

上述範例中viewbox的長度設定為100,所以整個實線/虛線可以用的總額就是100
寫成stroke-dasharray: 0 100的話,線就會不見(全都是虛線)
寫成stroke-dasharray: 100 0的話,就會看到一條全黑的實線(全都是實線)

補充:stroke-dasharray的多層次指定

stroke-dasharray可以指定多個值,一樣都是奇數值放實線長度,偶數值放虛線的長度描述
只要viewbox的長度不夠他走完,它就會回到最前面輪迴
前例的輪迴是2 base會比較單純,多層次指定的輪迴的虛實設定就會更複雜
(本章動態圓形進度條只需要用到2個值就可以了)

svg(viewbox="-50 -50 100 100")
 line(x1=-50 y1=0 x2=50 y2=0)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 line
  stroke: #000
  stroke-dasharray: 10 15 20 25
位置虛線/實線段stroke-dasharray線段輪迴
-50-40實線段10
-40-25虛線段15
-25-5實線段20
-520虛線段25
2030實線段10
3045虛線段15
4550實線段20
這裡沒走完所以被卡掉
只剩短短一段而已

stroke-dashoffset屬性介紹

stroke-dashoffset提供開始位置偏移的效果
其實就是更動x1的數值,如果是stroke-dashoffset: 10的話x1的數值就會比原本的還要在-10

舉例來說

svg(viewbox="-50 -50 100 100")
 line(x1=-50 y1=0 x2=50 y2=0)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 line
  stroke: #000
  stroke-dasharray: 100 100
  stroke-dashoffset: 10

這個範例中可以看到線段整個往左邊偏移了10,但是他其實也就是等於這個意思

svg(viewbox="-50 -50 100 100")
 line(x1=-60 y1=0 x2=50 y2=0)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 line
  stroke: #000
  stroke-dasharray: 100 100
  // stroke-dashoffset: 10

圓形的stroke:百分比進度條

理解上述stoke-dasharraystroke-dashoffset原理之後,我們來看看圓形的版本要怎麼做

首先,普通的畫一個圓

svg(viewbox="-50 -50 100 100")
 circle(cx=0 cy=0 r=40)
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 circle
  fill: none
  stroke: teal

接著,再用stroke-dasharray讓這個圓具有進度條的感覺(嘗試畫75%的圓)

svg
 width: 300px
 height: 300px
 border: 1px solid #000
 circle
  fill: none
  stroke: teal
  stroke-dasharray: 188.4 62.8

圓周長的算法為

半徑(r)×2×3.14

上述範例的半徑r40,所以總圓周長為40×2×3.14251.2
再往下規劃實線/虛線的分配,實線線段要配給75%,虛線線段要配給25%,所以

  • 實線長度:251.2×75%=188.4
  • 虛線長度:251.2×25%=62.8

雖然成功畫出了75%的實線段與25%的虛線段,但是…位置看起來不像一個進度條
這是因為,<circle>的起始點是位於90deg的位置,也就是圓形的右方
然後再順時鐘往前畫出去

順時鐘這個方向是對的,但是90deg這個起點不理想
所以要再透過stroke-dashoffset偏移起始點,讓起始點變成0deg

svg
 width: 300px
 height: 300px
 border: 1px solid #000
 circle
  fill: none
  stroke: teal
  stroke-dasharray: 188.4 62.8
  stroke-dashoffset: 62.8

stroke-dashoffset的值也是以圓周長來算,由於要偏移1/4的圓周長的距離,所以給定總長度251.225%,也就是62.8的偏移量

這樣就是我們所熟悉的圓形進度條了!

接下來來透過stroke-dasharrayanimation實作動態
動態的技巧是:讓實線的值從0188.4,視覺上就能做出圓形長出來的效果了

svg(viewbox="-50 -50 100 100")
 circle(cx=0 cy=0 r=40)
 text(x=0 y=0) 75%
svg
 width: 300px
 height: 300px
 border: 1px solid #000
 circle
  fill: none
  stroke: teal
  stroke-dashoffset: 62.8
  animation: mycircle 3s infinite
 text
  fill: teal
  dominant-baseline: middle
  text-anchor: middle
  
 @keyframes mycircle
  0%
   stroke-dasharray: 0 251.2
  100%
   stroke-dasharray: 188.4 62.8

選單hover效果:Magic Line

效果說明

這是一個選單的hover特效,本來底線會放在現在訪問的那一頁的選單項目之下,當滑鼠碰到某個其他項目時,那條底線就會滑過去

做法詳解

首先,做一個簡單的頁首選單

header
 a.logo(href="javascript") LOGO
 nav
  a(href="javascript").visited 首頁
  a(href="javascript") 服務項目
  a(href="javascript") 產品一覽
  a(href="javascript") 關於我們
  a(href="javascript") 聯絡表單
  #line

這裡假定現在造訪的位置在「首頁」,所以給「首頁」掛了一個.visited
並且可以看到,提示的線不是一個固定的border,他是一個叫做#line的元素,要假裝成border掛在底下而已
之後會隨著JS設定而飄來飄去

header
 display: flex
 justify-content: space-between
 padding: 0 20px
 background: #333
 border-radius: 4px

a
 text-decoration: none
 color: #fff
 padding: 20px 0

nav
 position: relative
 
nav *
 margin: 0 10px
 display: inline-block

#line
 margin: 0
 position: absolute
 height: 2px
 background: #fff
 bottom: 10px
 left: 0
 width: 32px
 transition-duration: 0.3s
 border-radius: 2px

JS詳解⑴ 抓取visited的固定位置

現在我們還沒法看到魔法底線,所以先讓他顯示出來
注意假如造訪了首頁,底線會出現在「首頁」底下
但如果又造訪了關於我們,底線會出現在「關於我們底下」

所以底線的長度位置都不能用CSS寫死,必須用JS動態設定才行

const visitedMenu=document.querySelector('.visited');
const allMenu=document.querySelectorAll('nav a');
const nav=document.querySelector('nav');

line.style.width=`${visitedMenu.clientWidth}px`;
line.style.left=`${visitedMenu.offsetLeft}px`;

這裡使用clientWidth抓取寬度,offsetLeft抓取該選單項目與左邊邊界的距離
注意由於<a>預設的display: inline之下,回傳的clientWidth會為0
(畢竟inline元素有寬度也是件奇怪的事情)
所以必須將<a>改成display: inline-block

JS詳解⑵ 動態抓到每個hover的位置

接著,來實作當滑鼠滑到別的選單項目時,底線移動(與改變長度)的效果
因為JS沒有hover這個事件,所以改成用mouseover來實作滑鼠移入

我們在原本的JS程式碼下方加入這段:

allMenu.forEach(item => item.onmouseover=function(){
 line.style.width=`${this.clientWidth}px`;
 line.style.left=`${this.offsetLeft}px`;
})

這時我們的小白線就能隨著滑鼠位置而動來動去了
不過,本範例中的visited位置在首頁,我今天滑到了「關於我們」後
小白線就直接停留在「關於我們」下面了,不會回到「首頁」下面
這樣邏輯不通,所以要在補寫一個mouseout事件,讓他在滑來滑去結束後
回到原本visited的位置

nav.onmouseout=function(){
 line.style.width=`${visitedMenu.clientWidth}px`;
 line.style.left=`${visitedMenu.offsetLeft}px`;
}

這裡雖然直覺上會想要把mouseout事件掛在allMenu
但看倌們仔細想想,我們這些選單項目<a>都有設定margin啥的
滑鼠非常容易就滑出去<a>,然後小白線就會像中風一樣一直想要回到visited的位置
因此,本例將此還原事件掛在更為全面的<nav>上面,以防小白線動得太靈敏了

JS詳解⑶ 函式化整理程式碼

到此為止,功能已經做好了
只是看看我們亂七八糟的JS程式碼

const visitedMenu=document.querySelector('.visited');
const allMenu=document.querySelectorAll('nav a');
const nav=document.querySelector('nav');

line.style.width=`${visitedMenu.clientWidth}px`;
line.style.left=`${visitedMenu.offsetLeft}px`;

allMenu.forEach(item => item.onmouseover=function(){
 line.style.width=`${this.clientWidth}px`;
 line.style.left=`${this.offsetLeft}px`
})

nav.onmouseout=function(){
 line.style.width=`${visitedMenu.clientWidth}px`;
 line.style.left=`${visitedMenu.offsetLeft}px`;
}

可以看到很多line.style....的東西一直在重複
這裡也是可以做簡化的,我們再把它寫成進階的函式整理成以下

const visitedMenu=document.querySelector('.visited');
const allMenu=document.querySelectorAll('nav a');
const nav=document.querySelector('nav');

function lineMove(target){
 line.style.width=`${target.clientWidth}px`;
 line.style.left=`${target.offsetLeft}px`
}

// line.style.width=`${visitedMenu.clientWidth}px`;
// line.style.left=`${visitedMenu.offsetLeft}px`;
lineMove(visitedMenu);

allMenu.forEach(item => item.onmouseover=function(){
 // line.style.width=`${this.clientWidth}px`;
 // line.style.left=`${this.offsetLeft}px`
 lineMove(this);
})

nav.onmouseout=function(){
 // line.style.width=`${visitedMenu.clientWidth}px`;
 // line.style.left=`${visitedMenu.offsetLeft}px`;
 lineMove(visitedMenu);
}

簡化後,不到20行JS就能搞定!

參考資料:https://css-tricks.com/jquery-magicline-navigation/

EXCEL函數 vlookup

函數說明

vlookup用途會縱向比對。
通常使用的情境是當手上有一個「固定資料索引」,比方說一個中日文顏色詞彙對照表好了,內容可能類似

A欄(中文)B欄(日文)
紅色レッド
藍色ブルー
黃色イエロー
粉紅色ピンク
白色ホワイト
黑色ブラック
綠色グリーン
咖啡色ブラウン

大概像這樣,就是一個索引表,這張索引表不會出現重複的顏色
然後,某天你從主管手上收到一份清單,清單裡面充滿了各式顏色的中文詞彙
主管請你把C欄全都翻成日文,並貼到D欄上去

C欄(各式顏色)D欄(要翻成日文貼上)
紅色
紅色
紅色
粉紅色
粉紅色
黑色
綠色

C欄因為是一份真實情況下會有的資料集(可能是服飾的顏色清單)
所以他不會按照順序排列,也會有一堆重複的顏色出現
比如「紅色」就重複了4次,粉紅色也重複了2次

D欄的製作,當然也能土法煉鋼地逐欄搜尋其對應的日文,然後一個一個手動貼到D欄上去。
或者,也能使用vlookup,讓函數自動幫你找比對,自動幫忙生成D欄

用法及參數

在D欄下面的第一個儲存格輸入函數=vlookup
這隻函數一共有4個參數

  • 第1個參數:需要比對的那一欄(本例中為C欄)
  • 第2個參數:資料索引表那坨(本例中為A~B欄)
  • 第3個參數:比對答案值要參考的欄位是資料索引表的第幾欄(本例因為A~B欄中,A欄是第1欄,B欄是第2欄,B欄是我們要找的日文,所以要填2
  • 第4個參數:填0,表示資料索引表找無的話回傳N/A
C欄(各式顏色)D欄(要翻成日文貼上)
紅色=vlookup(C:C, A:B, 2, 0)
紅色
紅色
粉紅色
粉紅色
黑色
綠色

第一個儲存格寫好公式後,一路往下拉到底,就能一口氣比對好整張表格了

寫RWD的方法

RWD寬度範例

max-width的指定值為包含
該當寬度與該當寬度以下的裝置,都會套用到RWD的CSS內容

@media (max-width: 768px)
 body
  background: pink
  • 寬度767px:粉紅色背景(手機版)
  • 寬度768px:粉紅色背景(手機版)
  • 寬度769px:白色背景(電腦版)

圖片RWD

圖片也能透過<picture>與 <source>做到當大螢幕時讀A圖,小螢幕時讀B圖的效果

picture
 source(media="(max-width: 480px)" srcset="手機版的圖.png")
 img(src="電腦版的圖.png" width="100%")

不論在電腦版,或是手機版,畫面都會顯示出<img>,並且一樣繼承width="100%"的參數
只是,當他變成手機版時(依照上例的設定,當寬度等於或小於480px時),<img>src會作切換,切換成source所定義的「手機版的圖.png」的位置

Again,因為max-width具備了包含的含義,所以顯示情形如下(以上例為例)

  • 寬度479px:手機版的圖.png
  • 寬度480px:手機版的圖.png
  • 寬度481px:電腦版的圖.png

JavaScript與螢幕寬度

在JavaScript實作螢幕寬度的功能,我們需要使用到innerWidth這個方法。

需要注意的一點是,假如今天這份網站的break point統一設定為768px好了。
767px768px時切換為手機版,但在769px時切換成電腦版。

由於media max-width的包含的特性,參數會下成

@media (max-width: 768px)
 body
  background: pink
picture
 source(media="(max-width: 768px)" srcset="手機版的圖.png")
 img(src="電腦版的圖.png" width="100%")

但在JavaScript,如果我們也想要使用768px這個參數,if statement需要寫成「小於等於」,也就是「<=

if(innerWidth<=768){
  document.body.style.backgroundColor='pink';
}

如此一來,才能與CSS沿用同一個參數,並同樣達成以下效果

  • 寬度767px:粉紅色背景(手機版)
  • 寬度768px:粉紅色背景(手機版)
  • 寬度769px:白色背景(電腦版)

當然,如果搞剛的話,也可以加上resize事件,讓畫面一動到尺寸就重新渲染JavaScript。
但基於使用者(基本上)不會亂切尺寸,所以本例就沒有加工了。

Media Query的三種寫法

⑴ 直接寫在CSS內

最常見的方法就是直接寫在CSS內,但這樣做CSS的易讀性會很差
@media (max-width: 768px)
 body
  background: pink

⑵ <link>外連

<link>也能指定Media Query,讓只符合某些Media條件下時,才能連到某特定CSS檔案
<link rel="stylesheet" media="screen and (max-width: 768px)" href="mobile.css" />
body
 background: pink

⑶ import引入

import法適用於複雜的Media寬度指定,比方說寫了好幾個版本的RWD:iPad直版、iPhone SE版、iPhone X版、iPad橫板…
Media條件太多時,全部寫進CSS會很亂,都寫到<link>裡面也會落落長
所以最好就是寫進一隻index.css裡面進行指定
style.css一般的樣式
index.css索引CSS(只用來寫import
mobile.css手機版樣式

STEP① HTML

import的時候,<link>指派的順序相當重要
必須要把index.css放到最末位
<link rel="stylesheet prefetch" href="style.css" />
<link rel="stylesheet prefetch" href="index.css" />

STEP② 索引CSS

index.css只能用來寫各種import,沒辦法寫一般的CSS樣式
如果寫的CSS樣式的話,import的效力就會消失
@import url(mobile.css) screen and (max-width: 768px);

STEP③ 手機版CSS

body {
 background: pink;
}

inline-block排滿後會產生的空隙

發生狀況

使用inline-block排版時,給定的元素寬度加總等於100%時會掉下來。

.item1 item1
.item2 item2
div
 border: 1px solid #000
 display: inline-block
 height: 200px
 
.item1
 width: 30%
 
.item2
 width: 70%

上述範例中,本來預期元素排成一行
但item2被擠下來了,所以變成兩行了

這是因為,由於各種原因,item1跟item2之間有空隙,所以寬度下得太滿時,他們會無法排成一行

針對這個問題,有兩個做法可以讓他們排成一行

方法⑴ 偷改寬度法(爛方法)

只要偷偷地將item2的寬度減個1%(或2%)他就能排上去了

div
 border: 1px solid #000
 display: inline-block
 height: 200px
 
.item1
 width: 30%
 
.item2
 width: 68%

方法⑵ 消除間隙法(認真法)

認真地從各個方面消除排版產生的間隙,讓他們真實地能夠排成一行

間隙處① html產生的文字空白

HTML架構中有一種元素叫做textNode
這個textNode不是畫面上看得到的元素
他是開發人員在寫html時,為了程式碼排版美觀,所以在文件上產生的
「既沒有被tag包覆,也沒有內容的空白」

如上圖,雖然看似是一份普通的HTML文件
但橘色虛線框部分跟綠色虛線框部分因為的確存在著排版上的空隙
所以上圖範例會產生2個textNode
(和一個elementNode<h1>

消除textNode造成的空白間隙影響,有兩個方法。
方法之一是排版時給他連在一起

這樣子,就能夠物理上消除textNode產生的影響了。
(或是使用pug等前處理器,也能在編譯時自動幫忙把textNode給移走)

另外一個方法是使用font-size: 0(比較常用,因為前一個方法太難讀程式碼了)
這時,即便存在著textNode,但因為顯示的空間都被設定成0了,所以畫面上就真正沒有了

body
 h1 嗨你好,我是標題
body
 font-size: 0

h1
 font-size: 14px

回到一開始的兩個item的範例,這時程式碼會變成

.item1 item1
.item2 item2
div
 border: 1px solid #000
 display: inline-block
 height: 200px
 font-size: 14px
 
.item1
 width: 30%
 
.item2
 width: 70%
 
body
 font-size: 0

textNode的影響移除了,但空間還是不夠,item仍舊掉下來了

間隙處② border(或是padding等人)導致的外溢

在預設的box-sizing: content-box之下borderpadding這些空間都會往外長
因為本例用了border: 1px solid,這左框+右框×2個item,合計4px的空間就導致了空間不足,所以item2就掉下來了
參考舊文

這時,需要使用box-sizing: border-box,將border等人的空間設定為往內長

div
 border: 1px solid #000
 display: inline-block
 height: 200px
 font-size: 14px
 box-sizing: border-box
 
.item1
 width: 30%
 
.item2
 width: 70%
 
body
 font-size: 0

螢光筆效果

原理說明

利用linear-gradient中的色塊效果,做出彷彿畫了螢光筆似的醒目提示效果
舊文連結:CSS漸層

span 這是一段文字,我想要強調
span.mark-pen 這個關鍵字
span ,強調的效果類似畫上螢光筆
.mark-pen
 background-image: linear-gradient(transparent 50%, rgba(173, 234, 234, 0.5) 50%)

同場加映:HTML <mark>

<mark>也有很類似的效果,但沒法像linear-gradient做出透明區域,會比較不像螢光筆一些。

span 這是一段文字,我想要強調
mark 這個關鍵字
span ,強調的效果類似畫上螢光筆

漸層

線性漸層

background: linear-gradient(角度, 開始色, 結束色)
角度說明
to left270deg由右往左
to right90deg由左往右
to top0 deg由下往上(default)
to bottom90 deg由上往下
.box
.box
 width: 500px
 height: 300px
 background: linear-gradient(to bottom, skyblue, aliceblue)

顏色的百分比係數

百分比係數,指的是圖的位置。
假設一張漸層圖的角度是to bottom時,40%指的是由上到下40%的位置
由上到下40%的位置,差不多就在上圖紅線的地方
如上,若看到一個範例寫為
background: linear-gradient(to bottom, skyblue 40%, aliceblue 70%)
則他代表的是
  • 0% - 40%為skyblue色
  • 70% - 100%為aliceblue色
  • 40% - 70%則為sky跟alice的混色

色塊

當前色百分比係數等同於後色百分比係數時,兩色的間隔空間為0,沒有漸層的空間,就會呈現色塊樣式
.box
 width: 500px
 height: 300px
 background: linear-gradient(to bottom, skyblue 60%, aliceblue 60%)

圓形漸層

.box
 width: 500px
 height: 300px
 background: radial-gradient(skyblue, aliceblue)

進階:用圓形漸層做radio button

label
 input(type="radio" name="rd1")
 .widget
 span 選項一

label
 input(type="radio" name="rd1")
 .widget
 span 選項二

label
 input(type="radio" name="rd1")
 .widget
 span 選項三
input
 display: none
 
label
 margin-right: 30px
 display: inline-flex
 align-items: center
 cursor: pointer
 
.widget
 margin-right: 8px
 width: 20px
 height: 20px
 border: 1px solid #cecece
 display: inline-block
 vertical-align: middle
 border-radius: 100%
 background: radial-gradient(#cecece 20%, #fff 25%)
 cursor: pointer

input:checked+.widget
 background: radial-gradient(cadetblue 45%, #fff 50%)
 border-color: cadetblue
 
input:checked~span
 color: cadetblue

用CSS做出三角形

原理剖析

做一個width為0、height為0,border為40的元素
div
div
 width: 0
 height: 0
 border: 40px solid #000
4個border都給予不同顏色,發現邊框與邊框之間呈現45度角
div
 width: 0
 height: 0
 border-top: 40px solid aliceblue
 border-bottom: 40px solid cadetblue
 border-left: 40px solid lightblue
 border-right: 40px solid skyblue
移除border-top,並將border-bottom的寬度改成2倍大
div
 width: 0
 height: 0
 border-bottom: 80px solid cadetblue
 border-left: 40px solid lightblue
 border-right: 40px solid skyblue
border-rightborder-left的顏色都設為transparent
div
 width: 0
 height: 0
 border-bottom: 80px solid cadetblue
 border-left: 40px solid transparent
 border-right: 40px solid transparent

快速複製區

div
 width: 0
 height: 0
 border-bottom: 80px solid cadetblue
 border-left: 40px solid transparent
 border-right: 40px solid transparent
div
 width: 0
 height: 0
 border-top: 80px solid cadetblue
 border-left: 40px solid transparent
 border-right: 40px solid transparent
div
 width: 0
 height: 0
 border-left: 80px solid cadetblue
 border-top: 40px solid transparent
 border-bottom: 40px solid transparent
div
 width: 0
 height: 0
 border-right: 80px solid cadetblue
 border-top: 40px solid transparent
 border-bottom: 40px solid transparent

SVG animate

稱為SMIL Animation,有別於CSS keyframes、JavaScript Animation,SMIL Animation為第三種前端動畫技巧

使用<animate>控制

<animate>寫在要動的元素之內

  • attributeName:控制要動的屬性,如半徑r 、寬度width等等
  • values:屬性移動的參數,用;分割
    attributeName=r values=10;30;10即表示
    動畫0%的時候半徑為10
    動畫50%的時候半徑為30
    動畫100%的時候半徑為10
  • dur:動作時間,單位為s
  • repeatCount:重複次數,如果要重複無限次的話則填入indefinite
svg(viewbox="0 0 100 100" width=300 height=300)
 line(x1=30 y1=50 x2=70 y2=50)
 line(x1=50 y1=30 x2=50 y2=70)
 circle(cx=50 cy=50 r=10)
  animate(attributeName="r" values="10;30;10" dur="2s" repeatCount="indefinite")
svg
 border: 1px solid #000
 *
  stroke-width: 3
  stroke: #000
  fill: #fff

animateTransform變形動畫

比起animate多了一個type屬性,用來指定變形動作例如rotatescaletranslate等等

transform預設的旋轉中心點為元素左上角,所以也跟CSS一樣,需要transform-origin來控制旋轉中心點
參考前文

svg(viewbox="0 0 100 100" width=300 height=300)
 rect(x=30 y=30 width=40 height=40)
  animateTransform(attributeName="transform" type="rotate" values="0;360" dur="2s" repeatCount="indefinite")
 circle(cx=50 cy=50 r=10)
 line(x1=30 y1=50 x2=70 y2=50)
 line(x1=50 y1=30 x2=50 y2=70) 
svg
 border: 1px solid #000
 rect
  transform-origin: center center
 *
  stroke-width: 3
  stroke: #000
  fill: #fff

group

使用g標籤,將需要群組化的元素都放進去後,再指定動畫,這坨元素都能一起動了
※使用g後,animate標籤就放在g以下,跟群組內元素平行

svg(viewbox="0 0 100 100" width=300 height=300)
 g
  rect(x=30 y=30 width=40 height=40)
  circle(cx=50 cy=50 r=10)
  line(x1=30 y1=50 x2=70 y2=50)
  line(x1=50 y1=30 x2=50 y2=70) 
  animateTransform(attributeName="transform" type="rotate" values="0;360" dur="2s" repeatCount="indefinite")
svg
 border: 1px solid #000
 g
  transform-origin: center center
 *
  stroke-width: 3
  stroke: #000
  fill: #fff

begin

透過begin指定開始時間,也可以指定負數,負數的話會在一開始時就直接動

svg(viewbox="0 0 100 100" width=300 height=300)
 rect(x=0 y=0 width=10 height=70)
  animate(attributeName="height" begin="0s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=15 y=0 width=10 height=60)
  animate(attributeName="height" begin="-0.3s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=30 y=0 width=10 height=80)
  animate(attributeName="height" begin="-0.6s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=45 y=0 width=10 height=90)
  animate(attributeName="height" begin="-0.9s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=60 y=0 width=10 height=50)
  animate(attributeName="height" begin="-1.2s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=75 y=0 width=10 height=40)
  animate(attributeName="height" begin="-1.5s" dur="2s" values="50;100;50" repeatCount="indefinite")
 rect(x=90 y=0 width=10 height=30)
  animate(attributeName="height" begin="-1.8s" dur="2s" values="50;100;50" repeatCount="indefinite")
svg
 border: 1px solid #000

SVG

viewbox

viewbox="右上角的X座標 右上角的Y座標 寬度 高度"

右上角的X座標,建議設定為0
右上角的Y座標,建議設定為0
這樣,svg圖形的中心點會為(寬度/2, 高度/2)

圓形

circle(cx=圓心X座標 cy=圓心Y座標 r=半徑)
svg(viewbox="0 0 100 100" width="300" height="300")
 circle(cx=50 cy=50 r=10)
svg
 border: 1px solid #000

方形

rect(右上角X座標 右上角Y座標 寬度 高度)
svg(viewbox="0 0 100 100" width="300" height="300")
 rect(x=40 y=40 width=20 height=20)

stroke與fill:框線與填色

svg圖形的填色不使用backgroundborder等CSS屬性
他要使用專門的定義顏色的系統

svg(viewbox="0 0 100 100" width="300" height="300")
 rect(x=40 y=40 width=20 height=20 stroke="skyblue" fill="aliceblue")

這些指定樣式的屬性也可以寫到CSS裡面

svg(viewbox="0 0 100 100")
 rect(x=40 y=40 width=20 height=20)
svg
 border: 1px solid #000
 width: 300px
 height: 300px
 rect
  stroke: skyblue
  fill: aliceblue

上圖將<svg>widthheight<rect>strokefill都寫到CSS裡了

直線

line(x1=起始點X座標 y1=起始點Y座標 x2=終結點X座標 y2=終結點Y座標 stroke=線條顏色) 

stroke不是必備屬性,只是不指定的話就看不到線條了

svg(viewbox="0 0 100 100" width="300" height="300")
 line(x1=30 y1=50 x2=70 y2=50 stroke="black")

文字

text(x=起始X座標 y=起始Y座標) ★★文字內容★★
svg(viewbox="0 0 100 100" width="300" height="300")
 text(x=50 y=50) 文字

文字的中心線

仔細看上圖,雖然xy都設定了中心點的值,但是描繪出來的文字看起來還是偏一邊
這是因為,<text>的中心線預設位在右下方,所以他是以右下方等於(xy)為基準來描繪文字

我們可以透過dominant-baselinetext-anchor來重新定義<text>的垂直/水平中心線

svg(viewbox="0 0 100 100" width="300" height="300")
 text(x=50 y=50) 文字
svg
 border: 1px solid #000
 text
  dominant-baseline: middle
  text-anchor: middle

存成svg檔案

  • 確認widthheight有寫在<svg>裡面
  • <svg>加上xmlns="http://www.w3.org/2000/svg",指定xml檔案類型為svg
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewbox="0 0 100 100">
    <circle cx="50" cy="50" r="10"></circle>
</svg>

CSS animation

  • 指定keyframes,定義動畫動作
  • 設定animation,定義動畫秒速、次數
.outer
 .block 上下移動
.outer
 width: 300px
 height: 200px
 border: 1px solid #000
 text-align: center
 position: relative
 display: flex
 justify-content: center
 
.block
 width: 50px
 height: 50px
 border: 1px solid #000
 position: absolute
 bottom: 0
 animation: UpAndDown 2s infinite
 
@keyframes UpAndDown
 0%
  bottom: 0
 50%
  bottom: 100px
 100%
  bottom: 0

動畫速度曲線

animation-timing-function: linear

CSS偽元素

使用說明

  • 透過:after或是:before指定偽元素
  • 指定偽元素後,需要設定content屬性才能顯示出來
    ※偽元素不能被JS抓取到
    <img><br>等沒有close tag的元素,不能指定偽元素

content指定固定文字

固定文字用引號包覆
content: '固定文字'
div
div
 width: 300px
 height: 200px
 border: 1px solid #000

div:before
 content: '固定文字'

content指定HTML Attribute

偽元素內容會隨attribute的值而變動
content: attr(○○)
.tag1(food="高麗菜" price="100")
.tag2(food="大蒜" price="20")
.tag3(food="玉米" price="300")
div
 width: 300px
 height: 200px
 border: 1px solid #000

div:before
 content: '品項:' attr(food) ' | 價錢:$' attr(price)

文字光暈

text-shadow

text-shadow: 水平陰影 垂直陰影 擴散程度 顏色
.outer
 h1 微弱光暈
.outer
 width: 400px
 height: 200px
 background: #24260f
 line-height: 200px

h1
 color: #fff
 text-shadow: 0px 0px 15px #ffdd65
 text-align: center

text-shadow 重複疊加

藉由參數的重複疊加,增強光暈效果

.outer
 h1 刺眼光暈
.outer
 width: 400px
 height: 200px
 background: #24260f
 line-height: 200px

h1
 color: #fff
 text-shadow: 0px 0px 15px #ffdd65, 0px 0px 5px #fff
 text-align: center

置中的4種方法

絕對定位法

  • 父層設定position: relative
  • 子層設定position: absolute; top: 50%; left:50%;
  • 由於上述設定不會計算到子層自己的寬與高,產生了偏移
    因此,再另外設定transform: translate(-50%, -50%)補正子層寬高偏移
.outer
 .inner
.outer
 width: 300px
 height: 200px
 border: 1px solid
 position: relative
 
.inner
 width: 80px
 height: 80px
 background-color: skyblue
 position: absolute
 top: 50%
 left: 50%
 transform: translate(-50%, -50%)

flexbox法

  • 父層設定display: flex
  • 水平置中:justify-content: center
  • 垂直置中:align-items: center
    ※以上忽略flex-direction等因素
.outer
 width: 300px
 height: 200px
 border: 1px solid
 display: flex
 justify-content: center
 align-items: center
 
.inner
 width: 80px
 height: 80px
 background-color: skyblue

text-align與line-height

  • 水平置中:text-align: center
  • 當父層高度為○○px、子層高度為△△px
    垂直置中:line-height: ○○-(△△/2)px
    ※此法適用於:子層沒有2行以上的文字內容
.outer
 width: 300px
 height: 200px
 border: 1px solid
 text-align: center
 line-height: 260px
 
.inner
 width: 80px
 height: 80px
 background-color: skyblue
 display: inline-block

table-cell法

  • 父層設定display: table-cell
  • 水平置中:text-align: center
  • 垂直置中:vertical-align: middle
.outer
 width: 300px
 height: 200px
 border: 1px solid
 display: table-cell
 vertical-align: middle
 text-align: center
 
.inner
 width: 80px
 height: 80px
 background-color: skyblue
 display: inline-block

不用svg畫圓弧的方法

25%、30%、75%圓弧

.outer
 .sq1
 .sq2
 .inner
  .num 75%
.outer
 width: 150px
 height: 150px
 background: #c1d7ae
 border-radius: 100%
 position: relative
 overflow: hidden

.sq1
 width: 150px
 height: 150px
 position: absolute
 left: 75px
 background-color: #90a955
 
.sq2
 width: 75px
 height: 75px
 position: absolute
 top: 75px
 background-color: #90a955

.inner
 width: 140px
 height: 140px
 border-radius: 100%
 position: absolute
 top: 5px
 left: 5px
 background-color: white

.num
 text-align: center
 line-height: 140px
 font-size: 40px
 font-weight: 300
 color: #6A7152
 display: block

上述以外的圓弧(需使用傾斜矩形)

.outer
 .sq1
 .sq2
 .sq3
 .inner
  .num 90%
.outer
 width: 150px
 height: 150px
 background: #c1d7ae
 border-radius: 100%
 position: relative
 overflow: hidden

.sq1
 width: 150px
 height: 150px
 position: absolute
 left: 75px
 background-color: #90a955
 
.sq2
 width: 75px
 height: 75px
 position: absolute
 top: 75px
 background-color: #90a955

.sq3
 width: 75px
 height: 75px
 position: absolute
 top: 0
 background-color: #90a955
 transform: skewX(-36deg)
 transform-origin: bottom

.inner
 width: 140px
 height: 140px
 border-radius: 100%
 position: absolute
 top: 5px
 left: 5px
 background-color: white

.num
 text-align: center
 line-height: 140px
 font-size: 40px
 font-weight: 300
 color: #6A7152
 display: block

傾斜角度分析

transform: skewX(-36deg)
transform-origin: bottom

skewX角度的算法:

完全傾斜至水平為skewX(-90deg),此時圓弧為75%的狀態
完全不傾斜為skewX(0deg),此時圓弧為100%

-90deg:0deg = 75%:100%
-3.6deg = 1%

換算出1%等於-3.6deg

100%圓弧skew(0deg)
99%圓弧skew(-3.6deg)
98%圓弧skew(-7.2deg)
97%圓弧skew(-10.8deg)
96%圓弧skew(-14.4deg)

如上表,換算出本範例的90%圓弧為skew(-36deg)

會動的圓弧

本範例使用的方法不能讓圓弧動起來,必須改用SVG stroke的方式才能讓他會動
詳細請參考這篇

box-sizing

box-sizing: content-box

預設屬性。
框線本身的高度不包含在元素高度內。

假如一個div的高度為100px,上框線為2px,下框線為2px
div的總高度為100+2+2=104px

※因此,容易因為少計算了框線高度導致內元素過大而撐壞版面

div content-box
div
 height: 100px
 width: 300px
 border: 2px solid #000
 text-align: center
var mydiv=document.querySelector('div');
console.log(mydiv.offsetHeight); //104

【補充】這題抓高度不能用clientHeight,因為clientHeight不包含border高度,所以必須使用offsetHeight才能抓到包含border的高度

box-sizing: border-box

框線本身的高度內加於元素高度之中

假如一個div的高度為100px,上框線為2px,下框線為2px
div的總高度仍會維持100pxdiv內容高度變為96px
div高度會受到擠壓(-4px

div
 height: 100px
 width: 300px
 border: 2px solid #000
 text-align: center
 box-sizing: border-box
var mydiv=document.querySelector('div');
console.log(mydiv.offsetHeight); //100

CSS Display屬性

display: block

  • <div><h1>的預設屬性
  • 元素寬度預設填滿寬度,並且會往下掉
  • 排版css支援情況
    • height:支援
    • width:支援
    • margin:支援
    • padding:支援
div block
div block
div block
div block
div block
div
 border: 1px solid #000
 display: block
 height: 50px
 margin: 10px 20px

display: inline

  • <span><a>的預設屬性
  • 不會像block一直往下掉
  • 排版css支援情況
    • height:不支援
    • width:不支援
    • margin:僅支援margin-leftmargin-right
    • padding:僅支援padding-leftpadding-right
div inline
div inline
div inline
div inline
div inline
div
 border: 1px solid #000
 display: inline
 height: 50px
 margin: 10px 20px

display: inline-block

  • <img>預設屬性
  • 元素不會往下掉,會往右排
  • 排版css支援情況
    • height:不支援
    • width:不支援
    • margin:支援
    • padding:支援
div inline-block
div inline-block
div inline-block
div inline-block
div inline-block
div
 border: 1px solid #000
 display: inline-block
 height: 50px
 margin: 10px 20px

科技部大專院生研究計畫 心得分享

題目

我的題目是『年齡因素對日語學習之影響——探討台灣小學生、國中生、高中生的日語教與學』。
構想是日文老師的打工經驗 ,我遇過各種年齡的學生、小學國中高中都有,但小學生學習成效最好!我想可能是因為我自製教材有關,其他學生我都是用「大家的日本語」授課,所以我想探討各年齡層適合的教學法以及教材。
我的指導教授宗禾老師說過

日文研究分成三類:文學,教育,語學 文學好切入,適合大學生寫
教育需要教學經驗跟實際學生,有點門檻
語學研究難度高,是教授等級

< p class=”has-text-align-left”>實際上徵選比率還蠻平均的,文學多一點,教育最少。

審查通過關鍵

題目(想解決的問題)是審查通過的關鍵,一定要把握兩個大原則:

  • 前瞻性→類似的研究不多,所以我的研究很有意義
  • 需求性 →很多人被這個問題困擾著,並且能證明這一點

如果題目符合上面兩點,並且在研究動機具體說明,大概就十拿九穩了。
但通常,構想來自課堂的題目沒有1.也沒有2.(如果是人文學科的話)
所以我覺得生活經驗很重要

困難點

第一個困難是理論部分,我的題目很難找到相關資料,特別在『各年齡學習者適合的教材』上,幾乎一無所獲。
所以這點也變成實驗的目的。
第二個跟我設計的實驗有關:質性研究法,一對一教學紀錄與訪談。所以…需要經過打工學生的同意進行錄音,臉皮要很厚XD。
再來一週3個學生真的吃不消…非常非常的忙!!

心得

交稿前修改時,發現審查前的前半,與合格後寫的後半,語氣截然不同。
前半寫得很急躁,句子寫很長都沒斷句,語氣很重,一副很想要通過的樣子。
後半語氣就相當悠哉了(茶)。

心得分享簡報

EXCEL轉檔CSV方法,解決日文亂碼問題

csv,是一種逗點分隔形式的文字檔。比方說在excel中一個表格長這樣。

這是一個表格

這一段資料轉成csv後就會變成

"這是","一個","表格"

轉成csv後,文件會大幅輕量化,所以很適合資訊量很多的文件使用。
而excel支援內建csv存檔格式,所以要存csv也很方便(存檔時選擇存成csv格式)。
BUT!!
因為excel不支援utf-8格式。
如果文件裡面有日文,轉檔出來的csv會變成亂碼。
一開始是安裝OpenOffice轉檔解決問題,但公司的acer win10毎次用OpenOffice開.xlsx都會出現重複字問題。
比方說某一格是

コード

用OpenOffice開啟後會變成

コードコードコードコードコードコードコード。

毎次開檔案還要一格一格檢査實在很麻煩(眼睛都要花了…)
所以我後來研究出用access轉檔的方法!既可以解決重複字問題也可以快速轉檔。

條件限制

  1. 需安裝access
  2. 原始文件儲存格內不能有換行資訊,像是這樣
這是
換行部分
一個表格

如上例,最左邊第一格有換行資訊。
如果取消換行也沒差的話可以用先用excel取代功能,將換行資訊(輸入Ctrl+J)取代為無。

excel轉檔csv步驟

①準備好excel文件,確認儲存格內沒有換行資訊。

Ctrl+A全選,格式改為「文字」,存檔,關閉excel。

③找到剛剛存好的xlsx檔案,按右鍵「開啟檔案」,「選擇其他應用程式」,「Access」

④出現這個視窗。甚麼都不要設定一直按「下一步」,「完成」

⑤點選標籤「外部資料」,「匯出-文字檔」 【快速鍵:Alt +X + T

⑥接著出現匯出視窗,指定好儲存位置後按確定(中間的指定匯出選項不用勾)

⑦匯出格式選「分欄字元」,按右下角的進階【快速鍵:V

⑧語言選「全部」,字碼頁選「日文(Shift-JIS)」
確認最上面的欄位分隔符號是,,文字辨識符號是"

⑨然後一直按下一步按到完成,結束,關掉Access,接著用excel確認看看剛剛做好的檔案

excel開啟csv查看步驟

轉換成csv後,因為不好閱覽了,所以要檢查文件有沒有錯也不方便
這時還是會使用EXCEL開啟做好的csv檔案查看
但要注意只能看,不能存
畢竟只要一存,日文字又會變回亂碼了

  1. 開一個新的excel
  2. 標籤「資料」,取得外部資料「從文字檔」
  3. 原始資料類型「分隔符號」,檔案原始格式「日文(Shift-JIS)」,按下一步
  4. 分隔符號「逗點」打勾,按下一步,按完成
  5. 打開後檢査完畢,不要存檔直接關掉(如果在這裡存檔的話會變亂碼)