2020年9月22日 星期二

jQuery Mobile 學習筆記 (一) : 環境配置與頁面結構

第一次學習 jQuery Mobile 是在 2013 年時, 但當時卻只開了個頭就忙別的學習科目去了. 最近因為阿正學弟的詢問, Web app 又重新進入眼簾, 但睽違了七年之後行動網頁前端技術面貌已大大改變, React/Angular/Vue 引領風騷, jQuery/jQuery Mobile 似乎已黯然失色. 雖曾求新學過一些 Vue, 但是時間有限, 我又不是做大專案, 用 jQuery 能做到的功能為何還要花時間去學新的前端技術? 所以還是回來重溫 jQuery/jQuery Mobile 較實在.

首先須釐清的是, jQuery Mobile 只是行動網頁的 UI (使用者介面) 框架, 負責生成與原生 App 類似之行動網頁介面. 如果要存取手機的照相, 訊息, 簡訊等功能則需依靠 Cordova (舊名 PhoneGap), 將 jQuery Mobile 所設計的 Web App 轉成手機原生 App. 其次, jQuery Mobile 依賴 jQuery 才能運作.

以下測試參考了這幾本書 (都很舊了 2014~2017 年的) :
  1. jQuery Mobile 智慧型手機程式開發 (博碩, 岡本隆史)
  2. HTML5+CSS3+jQuery Mobile 輕鬆打造 App 與行動網站 (博碩, 陳婉凌)
  3. PhoneGap 跨平台手機程式開發實戰 (上奇, 張亞飛)
  4. 徹底研究 jQuery Mobile + PHP 手機程式及網站開發 (上奇, 張亞飛)
  5. jQuery Mobile 跨平台開發寶典 (上奇, 陶國榮)
  6. PHP+MySQL+jQuery Mobile 跨行動裝置網站開發 (碁峰, 陳會安)
  7. Visual Studio 2015 X Cordova跨平台App實戰特訓班 (碁峰, 文淵閣工作室)
  8. Javascript + jQuery Mobile + Node.js 跨平台網頁設計範例教本 (碁峰, 陳會安) 
  9. 錢沾計畫啟動:jQuery Mobile 跨平台賺錢 App 錢途無量 (佳魁, 李科泉) 
其中第一本岡本隆史寫的很好, 適合初學者自學.

jQuery Mobile 教學文件參考 :

https://demos.jquerymobile.com/1.4.5/
https://www.tutorialspoint.com/jquery_mobile/index.htm
CHAPTER 12 - jQuery Mobile 教學 ( 基礎 )

之前的 jQuery Mobile 筆記參考 (2013 年) :

jQuery Mobile 測試 (一) : 環境佈置與頁面控制

以下測試程式放在 GitHub 上, 可用手機瀏覽器測試.


1. 環境配置 :

jQuery Mobile 最新版本為 v1.4.5, 而 jQuery 最新版為 v3.5.1, 但根據官網資料, jQuery Mobile v1.4.5 支援 jQuery v1.8~v1.11 與 v2.1 :




下載頁建議使用 jQuery v1.11.1 :

https://jquerymobile.com/download/

解壓縮後將 jQuery, jQuery Mobile 之 js 程式碼與 css 樣式檔分別複製到專案資料夾的 js 與 css 子目錄下 :
  • jquery-1.11.1.min.js (94KB)
  • jquery.mobile-1.4.5.min.js (196KB)
  • jquery.mobile-1.4.5.min.css (203KB)
另外 images 資料夾也要複製到專案資料夾下.

本地端供檔的網頁範本如下 :

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="js/jquery-1.11.1.min.js"></script>
    <script src="js/jquery.mobile-1.4.5.min.js"></script>
    <link href="css/jquery.mobile-1.4.5.min.css" rel="stylesheet">
  </head>
  <body>
    <script>
      $(function(){
        }); 
    </script>
  </body>
</html>

利用 CDN 供檔即無需準備執行環境, CDN 的網頁模板如下 :

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
    <link href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" rel="stylesheet">
  </head>
  <body>
    <script>
      $(function(){
        }); 
    </script>
  </body>
</html>

注意, 如果是 HTTPS 網站, 則 CDN 的 URL 也要改成 https 開頭.


2. 網頁架構: 

jQuery Mobile 的 Web App 網頁是以兩層 div 元素搭配 data-role 屬性建立行動網頁之頁面結構, 行動網頁內容可分散在不同 HTML 檔, 也可以整合在一個 HTML 檔案內, 透過超連結 a 元素之 href 屬性切換不同之瀏覽頁面, 幾乎不用寫一行程式碼即可建立漂亮的行動網頁使用者介面.

jQuery Mobile 依據 HTML5 制定了 data-role 屬性, 其值如下表所示 :


 data-role 屬性 說明
 page 建立一個瀏覽頁面 (page view)
 header 建立瀏覽頁面的標題列
 content 建立瀏覽頁面的內容列
 footer 建立瀏覽頁面的註解
 dialog 建立一個對話框
 navbar 建立一個導覽工具列
 button 建立一個按鈕
 controlgroup 建立一個控制群組
 listview 建立列表 (list view)
 slider 建立一個滑桿
 fieldcontain 建立一個表單欄位容器
 collapsible 建立一個可折疊容器
 collapsible-set 建立一個折疊選單


關於 jQueryMobile 的 data-xxx 屬性參考 :

https://api.jquerymobile.com/data-attribute/


基本的頁面結構分成 header (頁首或標題), content (內容), 以及 footer (頁尾或註腳) 三個部分 :

<div data-role="page">
  <div data-role="header">
    <h1>頁首</h1>
  </div>
  <div data-role="content">
    <p>內容</p>
  </div>
  <div data-role="footer">
    <h3>頁尾</h3>
  </div>
</div>

目前手機瀏覽器都已支援 HTML5, 故可將上面的 div 改成 HTML5 如下的語意標籤 :

<section data-role="page">
  <header data-role="header">
    <h1>頁首</h1>
  </header>
  <article data-role="content">
    <p>內容</p>
  </article>
  <footer data-role="footer">
    <h3>頁尾</h3>
  </footer>
</section>

其中 content 是必要的部分, header 與 footer 則是可有可無. header 用來放置頁面標題, 通常用 h1~h6 元素來包覆標題文字, 如果文字太長, 則後面無法顯示的部分會以 "..." 縮略. content 用來呈現文字, 圖片, 視訊等主體資訊, footer 部分通常用來放置作者, 公司, 或版權資訊.


(1). 多檔案架構 : 

如果網頁內容太多, 可分割放在多個 HTML 檔案頁中, 以 a 元素的 href 屬性切換 :

<a href="page2.htm" data-ajax="false">第二頁</a>

注意, 因為 jQuery Mobile 預設是以 Ajax 方式載入網頁, 而 Ajax 不支援載入本地檔案, 因此這裡超連結必須加入 data-ajax 屬性並將其設為 false (預設為 true), 否則會出現 "error loading page" 錯誤訊息, 參考 :

使用 JQueryMobile 點選超連結提示“error loading page” 錯誤

超連結 a 元素在 jQuery Mobile 中扮演了關鍵的頁面切換角色, jQuery Mobile 為其定義了一些 data- 開頭之屬性, 如下表所示 :


 a 的 data-xxx 屬性 說明
 data-ajax 是否要以 Ajax 方式載入網頁, 值="true" (預設), "false"
 data-rel 超連結的行為, 值="back" (上一頁), "dialog" (對話框), "external" (外部) 等
 data-icon 超連結按鈕上的小圖示, 值="home" (首頁), "back" (上一頁) 等
 data-transition 頁面轉換特效, 值="fade" (預設), "pop", "slide", "flip" 等六種


例如下面測試 1 有三個網頁檔 :


測試 1 : 多檔案換頁 [看原始碼 1_1看原始碼 1_2看原始碼 1_3]

第一頁 : jqueryuimobile_test_1_1.htm

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
    <link href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" rel="stylesheet">
  </head>
  <body>
    <section data-role="page">
      <header data-role="header">
        <h1>第一頁</h1>
      </header>
      <article data-role="content">
        <a href="jqueryuimobile_test_1_2.htm" data-ajax="false">第二頁</a><br>
        <a href="jqueryuimobile_test_1_3.htm" data-ajax="false">第三頁</a>
      </article>
      <footer data-role="footer">
        <h3>頁尾</h3>
      </footer>
    </section>
    <script>
      $(function(){
        });
    </script>
  </body>
</html>

第二頁 : jqueryuimobile_test_1_2.htm

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
    <link href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" rel="stylesheet">
  </head>
  <body>
    <section data-role="page">
      <header data-role="header">
        <h1>第二頁</h1>
      </header>
      <article data-role="content">
        <a href="jqueryuimobile_test_1_1.htm" data-ajax="false">第一頁</a>
      </article>
      <footer data-role="footer">
        <h3>頁尾</h3>
      </footer>
    </section>
    <script>
      $(function(){
        });
    </script>
  </body>
</html>

第三頁 : jqueryuimobile_test_1_3.htm

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
    <link href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" rel="stylesheet">
  </head>
  <body>
    <section data-role="page">
      <header data-role="header">
        <h1>第三頁</h1>
      </header>
      <article data-role="content">
        <a href="jqueryuimobile_test_1_1.htm" data-ajax="false">第一頁</a>
      </article>
      <footer data-role="footer">
        <h3>頁尾</h3>
      </footer>
    </section>
    <script>
      $(function(){
        });
    </script>
  </body>
</html>

瀏覽首頁 jqueryuimobile_test_1_1.htm 按內容區的 "第二頁", "第三頁" 超連結會分別載入 jqueryuimobile_test_1_2.htm 與 jqueryuimobile_test_1_3.htm, 結果如下 :






此範例網址可用 Quick Mark 線上轉換為 QR code, 方便用手機直接掃描測試 :

http://www.quickmark.com.tw/cht/qrcode-datamatrix-generator/default.asp?qrLink





注意, 這種多檔方式有三個限制 :
  • 不可連結至其它網域之檔案
  • 連結之檔案只能有單一頁面
  • a 元素不可以有 target 屬性
也可將多頁內容整合於單一檔案中方便管理, 每一個頁面的 div 都需指定 id 屬性值, jQuery Mobile 就是以頁面的 id 屬性作為錨點, 利用 a 元素的 href 切換不同之錨點 id 達到頁面切換之效果.

例如在第一頁的超連結中設定 href 指向第二頁頁面的 (id=page2) :

<a href="#page2">第二頁</a><br>

則按此超連結就會切換到第二頁 :

<section data-role="page" id="page2">

例如上面的三檔案範例可以整合於下面範例的單一檔案中 : 


測試 2 : 單檔案換頁 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
    <link href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" rel="stylesheet">
  </head>
  <body>
    <!-- 第一頁頁面 -->
    <section data-role="page" id="page1">
      <header data-role="header">
        <h1>第一頁</h1>
      </header>
      <article data-role="content">
        <a href="#page2">第二頁</a><br>
        <a href="#page3">第三頁</a>
      </article>
      <footer data-role="footer">
        <h3>頁尾</h3>
      </footer>
    </section>
    <!-- 第二頁頁面 -->
    <section data-role="page" id="page2">
      <header data-role="header">
        <h1>第二頁</h1>
      </header>
      <article data-role="content">
        <a href="#page1">第一頁</a>
      </article>
      <footer data-role="footer">
        <h3>頁尾</h3>
      </footer>
    </section>
    <!-- 第三頁頁面 -->
    <section data-role="page" id="page3">
      <header data-role="header">
        <h1>第三頁</h1>
      </header>
      <article data-role="content">
        <a href="#page1">第一頁</a>
      </article>
      <footer data-role="footer">
        <h3>頁尾</h3>
      </footer>
    </section>
    <script>
      $(function(){
        });
    </script>
  </body>
</html>

此例中三個頁面的 div 都必須指定 id 屬性, 以便 a 元素利用此 id 屬性值當作錨點以 href 屬性進行換頁, 結果與上面測試一完全相同. 此範例測試網址 QR code 如下 :




參考 :


2020年9月21日 星期一

奇怪的高鐵付費網頁

晚上幫姊姊支付中秋節回程高鐵票時又遇到這個問題, 實在不吐不快. 在查詢訂票後按信用卡付費鈕, 正要輸入卡號才想到剛剛的班次資訊沒核對, 就按上一頁回去看, 核對沒問題再按付費鈕結果出現 "請再等 15 分鐘" 訊息, 即使關掉網頁重來也一樣 :




之前曾有一次因為這樣等啊等就忘記了, 過了 12 點姊姊好不容易訂到的大學生優惠票被取消, 真的好棒棒! 造成的不便我是要怎要見諒? 請問這是委託哪家公司設計的系統?

2020年9月20日 星期日

2020 年第 38 周記事

上個月鄉下家一隻常出現在豬舍的野貓生了一隻小花貓, 大概一個月大母貓就跑了, 小貓每天喵喵叫, 爸就拿肉鬆拌飯給它吃, 居然住下來了, 由於睫毛下垂, 看起來像是衰尾道人模樣, 上上次菁菁回鄉下時給它起名 "小雖貓", 最近睫毛不再明顯下垂, 所以我都叫它 "小貓貓". 晚上我在晒穀場散步, 它也會跟前跟後找我玩, 尖牙利爪但都不伸爪.





至於養在高雄的萬萬與小咪, 只要我在房間寫程式, 這兩隻在客廳窮極無聊就會進來窩在床頭櫃陪我, 嗯, 我覺得養貓蠻療癒的, 我應該不會得精神病, 哈 !





本周因為買的 Mac mini 寄來了, 所以花了點時間玩 macOS, 並開始學習 Swift 程式設計. 每一種程式語言其實都差不多, 有資料型態, 分支, 迴圈, 函數, 類別等等, 學一種新語言只要弄清楚這些基本語法的格式就差不多了.

最近在愛奇藝看的韓劇是林秀香主演的 "我最漂亮的時候", 目前看到第 10 集, 但在預告時似乎看到後面哥哥會出意外 (賽車?), 這讓我有點不想往下看. Line TV 也可看 :

https://www.linetv.tw/drama/11506/eps/1

明天是 921, 也是母親仙遊六周年紀念.


2020年9月19日 星期六

蘋果 macOS Catalina 操作心得整理

在露天買的 Mac Min 2012 late 昨天到貨後, 我將它放在輝達的 Jetson Nano 下面, 插上鍵鼠組的 USB 與壁掛螢幕的 HDMI 馬上就能使用了. 這台雖然已經是七年前的機器了, 但 16GB DRAM 加上 512GB SSD 效能超好, 而且 SSD 靜悄悄又不發燙. 




不過 macOS 操作介面與 Win10 差異很大, 一開始比較讓人抓狂的是例如中英文輸入切換, 在 Windows 是用 SHIFT 鍵切換, 但在 macOS 則是 STRL+SPACE, 書上說大概需要兩周學習才會熟悉. 茲將學習心得記錄如下 : 

  1. 切換中英文輸入 : CTRL + SPACE 會在英文與注音之間切換. 
  2. Safari 瀏覽器將網站加入我的最愛 :
    (1). 滑鼠移到網址列最前方的 + 圖示, 按右鍵點選 "喜好項目" (最快).
    (2). 按 Safari 左上角 < > 右邊的按鈕開啟左側邊欄, 將網址列拉到側邊欄內, 若要網站修改名稱, 點選後選 "重新命名" 加以修改. 
  3. 更改螢幕字型大小 : 
    從底下 Docker 進入 "系統偏好設定" (齒輪圖示), 點選 "選示器", 點選 "解析度" 項下的 "縮放", 選擇一個解析度較小的設定即可. 
  4. 關閉英文大小寫 Caps Lock 的輸入法切換功能 : macOS 預設把 Caps Lock 鍵當成輸入法切換鍵 (用 CTRL+SPACE 就好了啊), 結果在輸入大寫英文字母時按此鍵卻進入注音輸入法, 解決方式是由 Docker 進入 "系統偏好設定" (齒輪圖示), 點選 "鍵盤/輸入方式/ABC", 取消勾選第二項 "使用大寫鎖定鍵來切換 ABC 及目前輸入方式". 


~進行中~


2020年9月18日 星期五

momo 買書兩本 (數學 & 貝氏統計學)

 今天是 momo 買書 68 折截止日, 我又買了兩本數學的書 : 

機器學習的數學:用數學引領你走進AI的神秘世界 $580 元折後 395 元

寫給大家的統計學|秒懂機率與統計,你也可以是人生勝利組 $480 元折後 327 元



總定價 1060 元,  折後 722 元, 約 68 折. 本來想加購 399 元的 MK220 羅技鍵鼠組, 但又覺得目前不需要 (Mac Mini 已經用明誠路燦坤開幕買的那組 mk235 了), 所以就沒加購了. 

市圖還書 3 本

本周市圖還書 3 本 :
  1. jQuery最強圖解實戰講座 =Lectures and exercises
  2. Make:一讀就懂micro:bit : 給程式新手的開發板入門指南 
  3. 經濟與財務數學 :使用R語言
第一本書有許多 jQuery 實用範例, 我已大致看完, 因有人預約故須先還. 第二本也是 MicroPython 版的, 以後再借; 第三本值得買起來 (但五南的書很貴).

Javascript 函數傳回 return false 的問題

在 Javascrip/jQuery 的事件處理函數中常看到最後用 return false 結尾的用法, 常令人疑惑其用意是甚麼, 其實這是不要讓 HTML 元素執行其預設動作的方法, 例如超連結 a 元素的預設動作是將網頁導向 href 所指之位置; 而 form 元素的預設動作是提交表單 :
  • href="#" : 表示導向該網頁中的一個錨點 (anchor)
  • href="hello.htm" : 表示導向此網站的另一個網頁 hello.htm
  • href="http://www.foo.com/bar.htm" : 表示導向另一個網站之網頁
如果在 a 元素的 click 事件處理函數中最後傳回 false, 則 a 元素預設的連結導向動作會被取消, 例如下面範例中的兩個 a 元素都連結到本網頁之錨點 "#" :

  <a href="#" id="a1">a1</a>
  <a href="#" id="a2">a2</a>

其中的 id=a2 的超連結有特別掛上 click 事件處理器, 並且傳回 false, 而 id=a1 則沒有 : 

$("#a2").on("click", function(){
  return false;
  });

按 a1 超連結後網址列最後會多出一個 "#" 表示網頁會回到網頁的最上方, 但按 a2 超連結則不會, 參考 :


測試 1 : 超連結導向本網頁錨點 href="#" [看原始碼]

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>jQuery test</title>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</head>
<body>
  <a href="#" id="a1">a1</a>
  <a href="#" id="a2">a2</a>
  <script>
    $(function(){
      $("#a2").on("click", function(){
        return false;
        });
      });    
  </script>
</body>
</html>

傳回 false 常見於將 a 元素當作按鈕用時, 當按下按鈕會執行事件處理函數, 如果不禁止其執行預設之網頁重導向動作, 事件處理函數執行完後網頁會回到網頁最上方 (href="#" 的預設動作), 造成網頁移動的不好觀感, 例如 : 


測試 2 : 超連結當作按鈕時使用 return false 停止預設動作 [看原始碼]

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>jQuery test</title>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
  <link href="https://code.jquery.com/ui/1.12.1/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <style>
    body {font-family: Arial, Helvetica, sans-serif;}
    .ui-widget {font-size:80%;}
  </style>
</head>
<body>
  <div style="height: 1000px;"></div>
  <a href="#" id="a1">a1</a>
  <a href="#" id="a2">a2</a>
  <script>
    $(function(){
      $("#a1").button();
      $("#a2").button();
      $("#a1").on("click", function(){
        alert("你按了 a1");
        });
      $("#a2").on("click", function(){
        alert("你按了 a2");
        return false;
        });
      });    
  </script>
</body>
</html>

此例中使用一個高度為 1000px 的 div 元素將按鈕推到網頁底下, 可見按 a1 按鈕後因為沒有垂回 false 導致執行 href="#" 的預設動作 (回到網頁最上方), 但按 a2 按鈕則網頁不會移動

但是在 "jQuery 應用程式設計極速上手" (Jay Blanchard, 上奇) 這本書裡的第 1-15 頁提到, 使用 return false 來停止預設動作可能會有問題, 因為 return false 實際上會先後呼叫事件物件 e 的e.preventDefault() 與 e.stopPropogation() 函數, 前者會停止預設動作, 後者會停止事件上升 (bubble up) 功能, 因此會把之後加入之超連結的 click 事件給阻擋掉, 使得預期的功能沒有出現.

比較妥當的做法應該是呼叫 preventDefault() 即可而不是 return false :

$("#a2").on("click", function(e){
   e.preventDefault();
  });

參考下面這篇文章, 作者清楚描述了 return false 的作用, 如果只是要停掉預設動作, 那就呼叫事件物件 e 的 preventDefault() 就好, 不要使用 return false : 


jQuery 中的 this 與 $(this)

在許多 jQuery 範例中常出現 this 與 $(this), 甚麼時候該用 this, 甚麼時候該用 $(this), 很多人時常會被搞混,  在 jQuery 語法中 this 是被選取器選取的 DOM 物件, 而 $(this) 才是此 DOM 物件經過包裹後的 jQuery 物件, 因此如果要存取 DOM 物件的屬性或方法時須用 this; 而要存取 jQuery 物件之屬性與方法時則須用 $(this), 參考 :

淺談jQuery this和$(this)的區別及獲取$(this)子元素物件的方法

在 jQuery 敘述中可以用 $(this) 來避免重複使用選擇器選取元素, 例如對於按鈕元素 button :

&lt;button id="btn"&gt;OK&lt;/button&gt;

用 jQuery 監聽 click 事件, 每按一次顯示已按次數 :

var cnt=0;
$("#btn").on("click", function(){
  $("#btn").html("你按了" + (++cnt) + "次");
  });

此例中的事件處理函式需要再一次存取按鈕之 jQuery 物件, 若用 $("#btn") 就重複使用選擇器兩次, 應該改為 :

var cnt=0;
$("#btn").on("click", function(){
  $(this).html("你按了" + (++cnt) + "次");
  });

完整程式碼參考 :

測試 1 : this 與 $(this) [看原始碼]

一般常犯的錯誤例如在存取表單時, this.val() 是錯誤的, 因為 DOM 物件沒有 val() 方法, 要 jQuery 物件才有, 即應該是 $(this).val() 才對. 

Blogger 新版上線

 昨天關掉 blogger 視窗重開後發現 Google 強制使用新版編輯器了, 底下原先有的切回舊版超連結已消失, 所以舊版 Blogger 編輯器一關掉就回不去了, 這熟悉的舊介面很好用啊為啥要換?  舊版的網址列格式如下 :

# https://www.blogger.com/blogger.g?blogID=yourID&useLegacyBlogger=true#allposts




新版 Blogger 介面最令人討厭的缺點是超連結編輯視窗, 你必須多按幾次 Enter 把超連結往上頂, 這樣才能按到底下的套用按鈕, 沒按套用鈕根本就編輯無效. 但新版的優點是可以直接將 HTML 碼貼進去, 它會自動轉成 Entity, 以前舊版必須先在記事本進行人工轉換. 

momo 買 iOS 13 書籍兩本

 因為買了二手的 Mac mini, 想要開始學習很久以前就想涉獵的 Apple Swift 語言, 所以上 momo 找 iOS 13 的書, 發現 Apple 真的是小眾耶, 市面上 iOS 的書真的不多, 只找到下面兩本還不錯的 : 

iOS 13程式設計實戰- Swift 5.1/SwiftUI框架|快速上手的開發技巧200+ $580 折後 $395

iOS 13 App程式開發實務心法:30個製作專業級iOS App完全實戰攻略 $850 折後 $578



兩本定價共 1430 元, 折後 973 元, 相當於 68 折, 扣掉 momo 幣 183 元, 實付 790 元, 相當於 55 折, 真是太划算了! 所以在 momo 買書還是要刷 momo 卡才能加速累積 momo 幣. 

2020年9月17日 星期四

好書 : Javascript & jQuery 網站互動設計程式進化之道

 最近從市圖借到一本好書 :

Javascript & jQuery 網站互動設計程式進化之道 (2017, 碁峰)


Source : 博客來

此書分成兩半部, 前半部講純粹的 Javascript 網頁操作; 後半部則介紹 jQuery 以及它如何減輕純粹用 Javascript 開發的負荷, 編排體例清晰, 左邊是原理說明, 右邊是程式範例, 而且搭配精心繪製的圖表, 令人一目瞭然, 是非常適合初學者的學習教材, 例如關於 jQuery 的四個插入元素的方法, 書中的圖示就描述得很清楚 :  



此書譯自 John & Wiley 出版的 "Javascript & jQuery : Interactive Front-End Web Development" 一書, 書範例程式碼可在下列網站下載 :

# http://www.javascriptbook.com/code/

2020年9月16日 星期三

博客來買書一本 : macOS活用萬事通

因為在露天買了二手的 Mac min 電腦, 要學習如呵操作 macOS, 故於博客來買了此書 :

macOS活用萬事通:Catalina一本就學會! $363




可能 Mac 電腦是小眾, 此書居然是 66 折.

購買 Mac mini A1347 (2012 Late) 二手電腦

八月底姊姊結束在後製公司的實習後終於買了 MacBook Pro, 上周則有一位同事買了 MacBook Air, 讓我想起很久以前想買的 Mac Mini, 加上最近在學習用 jQuery Mobile 寫 Web app, 若要用 Cordova 轉成 iOS App 的話必須用到 Xcode, 於是再度上網搜尋二手良品, 找到基隆賣家這款  2012 年 Late 出廠的 A1347 :

2013 Mac mini A1347  $10000

經過一周來不斷詢問機器的功能版本, 賣家不厭其煩地回覆我隨時想到的問題, 還下載安裝了 Xcode 測試是否 OK, 所以今天決定購買此電腦, 老闆折手續費 200 元但要加運費 60, 故總價是 9860 元, 我想這是我目前學習 macOS 與 Swift 最低的成本了 (全新的要 3 萬元左右).

i5 CPU + 16GB DRAM + 512GB SSD 的組合運行效能應該不錯, 比今年年初找到的同款 A1347 (i5 CPU + 8GB DRAM + 240GB SSD, 已下架) 貴了 1000 元, 參考 :

# 關於 Mac Mini 與 Swift 5 版本問題

不過衡量後覺得那台是 8G DRAM, 240 GB SSD, 而且還是 2011 mid 款, 所以想想也還 OK. 我在網路上搜尋此型電腦之一般價格, 找到今年五月蝦皮有賣出一台同樣 2012 late 的 Mac mini (i5, 16GB DRAM, 628GB 融合硬碟) 9200 元成交, 所以看來 10000 元應該是 OK :

Mac mini 2012 16G $9200

Mac mini A1347 規格參考 :

Apple Mac mini "Core i5" 2.5 (Late 2012) Specs

Mac mini(2012 年末)
處理器:2.5GHz 雙核心 Intel Core i5 (Turbo Boost 可達 3.1GHz),配備 3MB L3 快取
儲存設備:512GB SSD
繪圖處理:Intel HD Graphics 4000
記憶體:8GB
視訊支援:支援多達兩部 2560 x 1600 像素的顯示器,均可顯示千萬種顏色
連接與擴充
Thunderbolt 連接埠 (傳輸速率高達 10 Gbps)
FireWire 800 連接埠 (傳輸速率高達 800 Mbps)
4 個 USB 3 連接埠 (高達 5 Gbps)
HDMI 連接埠
SDXC 卡插槽
Gigabit 乙太網路連接埠
音訊輸入/輸出
紅外線接收器
通訊功能
Wi-Fi
802.11n Wi-Fi 無線網路3;IEEE 802.11a/b/g 相容
Bluetooth
Bluetooth 4.0 無線技術
乙太網路
10/100/1000BASE-T 乙太網路 (RJ-45 插座)

賣家已將 macOS 升版至 v10.15.6 Catalina, 此電腦最高也就只能升至 v10.15.x :




參考 :

Max Supported Version of Mac OS X (OS X, macOS)

macOS 10.15 SDK 開發工具為 Xcode 11 (支援 SwiftUI), 參考 :

https://developer.apple.com/documentation/xcode-release-notes/xcode-11-release-notes
  • Xcode 11 supports development with SwiftUI
  • Xcode 11 is available in the Mac App Store and includes SDKs for iOS 13, macOS Catalina 10.15, watchOS 6, and tvOS 13. Xcode 11 supports development for devices running iOS 13.1. Xcode 11 supports on-device debugging for iOS 8 and later, tvOS 9 and later, and watchOS 2 and later. Xcode 11 requires a Mac running macOS Mojave 10.14.4 or later.

Xcode 目前最新版本是 Xcode 11, 支援 iOS 13 與 WatchOS 6 開發. 既然買了 Mac mini, 就得找一下 macOS 的書, 但不管是市圖還是母校圖書館, 都沒有找到最新的 Catalina 的書, 只好到博客來買這本 :

macOS活用萬事通:Catalina一本就學會! $363 元 (66 折)


Source : 博客來


參考 :

Apple Mac mini 2012 Late 開箱
Late 2012 Mac mini 開箱!
Late 2012 Mac mini 開箱!
(59)省錢+長知識 - Mac Mini 2014年款購買前請注意其限制及優缺點 NOT User friendly

我在下面文章看到 2014 時 Mac mini 賣 19400 元 :

小空間買桌電的小確幸:Apple Mac mini




過了七年還能有一半價值, 蘋果的產品還真保值啊! (我的 iPad 2 買了十年還天天在用).

2020-09-18 補充 :

今天心血來潮查詢 2020 最新版 Mac min 價格, 官網賣 25900, PChome 限時優惠價也要 24605 :




同事買的 MacBook Air 是 31700, 即使用教育方案便宜 3000 也還要 28700, 所以 Mac Mini 是學習 Swift 成本最低的選項. 

2020年9月15日 星期二

基於 Canvas 的 Chart.js 圖表函式庫

最近在一本 2014 的 jQuery 書上看到一個 jQuery 繪圖函式庫 jqPlot.js, 稍微閱讀之後發現蠻好用的, 但在 2018 年就因為缺乏開發人員投入而停止開發了, 參考 :

https://groups.google.com/g/jqplot-dev/c/tDkY2z0q2t4

作者建議使用者改用 Chart.js 這個函式庫, 此乃以 Canvas 為基礎的開源 Javascript 圖表函式庫, 據說 Angular Chart 也是以 Chart.js 為基礎建構的, 相關網站參考 :

https://www.chartjs.org/
https://github.com/chartjs/Chart.js

教學文件與範例參考 :

https://www.chartjs.org/docs/latest/
https://www.chartjs.org/samples/latest/
[Day 30]Chart.js - 輕鬆完成資料視覺化
[十分鐘學習] Chart.js - 圖表繪製

Chart.js 最新版為 v2.9.3, 可從 GitHub 下載, 原始檔約 420 KB, 壓縮檔約 170 KB :

https://github.com/chartjs/Chart.js/releases/tag/v2.9.3

將 Chart.js 放在專案目錄下就可以馬上用它來繪製 line (折線圖), bar (柱狀圖), radar (雷達圖), polarArea, pie (圓餅圖), doughnut, bubble (泡泡圖) 等七種圖表, 網頁模板如下:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="js/Chart.min.js"></script>
  </head>
  <body>
    <script>
      //your codes
    </script>
  </body>
</html>

或者也可以使用 CDN, 例如 cdn.js.com 或 jsdelivr.com :

https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js
https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js

模板如下 :

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
  </head>
  <body>
    <script>
      //your codes
    </script>
  </body>
</html>

Chart.js 是基於 Canvas 的圖表函式庫, 其 HTML 部分使用 canvas 元素做為畫布, 依照官網教學文件中的範例, 此 canvas 元素可用 width 與 height 屬性設定畫布大小 :

<canvas id="myChart" width="400p" height="300"></canvas>

接著呼叫 canvas 元素的 getContext('2d') 方法取得畫布的 Context 物件 :

var ctx=document.getElementById('myChart').getContext('2d');

最後用 new Chart(ctx, options) 建立圖表物件 :

var myChart=new Chart(ctx, options)

其中第一參數 ctx 為上面呼叫 getContext() 所傳回之 Context 物件, 第二參數 options 為選項物件, 其 labels 屬性可設定 X 軸刻度標籤; dataset 屬性可設定資料集之標題 label 與資料 data 等屬性. 下面參考官網教學範例改寫如下 :


測試 1 : 繪製柱狀圖 (1) [看原始碼]

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Chart.js test</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
</head>
<body>
  <canvas id="myChart" width="400" height="300"></canvas>
  <script>
    var ctx=document.getElementById('myChart').getContext('2d');
    var myChart=new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['一月', '二月', '三月'],
        datasets: [{
          label: '銷售業績(百萬)',
          data: [60, 49, 72]
          }]
        }
      });
  </script>
</body>
</html>

此例顯示 1~3 月氏銷售業績柱狀圖, 但是奇怪的是, canvas 中的 width 與 height 屬性設定沒有作用, 畫布佔據了整個頁面, 我找到下面這篇文章 :

https://stackoverflow.com/questions/37621020/setting-width-and-height

試了全部方法只有其中編號 48 回應的做法有效, 亦即在 canvas 外再包覆一層 div 元素, 然後設定此 div 之尺寸即可 :

<div style="width: 400px; height: 300px">
  <canvas id="myChart"></canvas>
</div>


測試 2 : 繪製柱狀圖 (2) : 設定畫布尺寸 [看原始碼]

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Chart.js test</title>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
</head>
<body>
  <div style="width: 400px; height: 300px">
    <canvas id="myChart"></canvas>
  </div>
  <script>
    var ctx=document.getElementById('myChart').getContext('2d');
    var myChart=new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['一月', '二月', '三月'],
        datasets: [{
          label: '銷售業績(百萬)',
          data: [60, 49, 72]
          }]
        }
      });
  </script>
</body>
</html>

結果如下, 畫布大小與所指定之尺寸相同 :




另外一篇文章也提供了一個解決辦法 : 


其中編號 2 的回應是先取得 canvas 的父元素 (即 div), 然後設定其 width 與 height 屬性 :

ctx.canvas.parentNode.style.width="300px";
ctx.canvas.parentNode.style.height="500px";

事實上官方教學文件也有提到這個方法 : 


例如 : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Chart.js test</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
</head>
<body>
  <div>
    <canvas id="myChart"></canvas>
  </div>
  <script>
    var ctx=document.getElementById('myChart').getContext('2d');
    ctx.canvas.parentNode.style.width="400px";
    ctx.canvas.parentNode.style.height="300px";
    var myChart=new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['一月', '二月', '三月'],
        datasets: [{
          label: '銷售業績(百萬)',
          data: [60, 49, 72]
          }]
        }
      });
  </script>
</body>
</html>

此例 div 元素沒有指定大小, 而是用 parentNode 屬性取得 canvas 元素之父元素 (div) 後設定其尺寸, 結果與上面範例 2 相同. 

如果配合使用 jQuery, 可以用選擇器取得 canvas 物件後直接當作 Context 物件傳給 new Chart() , 不可呼叫 getContext('2d'), 因為 jQuery 物件並無此方法. 如果要取得 Context 物件, 應先取出 jQuery 物件中的 DOM 元素, 這有兩個方法 :

$('#myChart')[0].getContext("2d");

或者 :

$('#myChart').get(0).getContext("2d");

用索引 [0] 或呼叫 get(0) 就能從 jQuery 物件中取出 DOM 物件了, 例如 :


測試 4 : 繪製柱狀圖 (4) : 搭配 jQuery [看原始碼]

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Chart.js test</title>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
</head>
<body>
  <div style="width: 400px; height: 300px">
    <canvas id="myChart"></canvas>
  </div>
  <script>
    var ctx=$('#myChart');
    //var ctx=$('#myChart')[0].getContext("2d");
    var myChart=new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['一月', '二月', '三月'],
        datasets: [{
          label: '銷售業績(百萬)',
          data: [60, 49, 72]
          }]
        }
      });
  </script>
</body>
</html>

此例中的 ctx 可以是 canvas 元素之 jQuery 物件, 也可以是 DOM 物件.

上面範例中的圖形預設背景與外框均為灰色, 可以用下列屬性設定 :
  • backgroundColor : 設定背景色 
  • borderWith : 設定邊框寬度 (單位 px) 
  • borderColor : 設定邊框顏色 
backgroundColor 與 borderColor 的值為陣列, 其元素為對應各資料之顏色字串 "#FF0000", 例如 :


測試 5 : 繪製柱狀圖 (5) : 設定框邊與背景色 [看原始碼]

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Chart.js test</title>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
</head>
<body>
  <div style="width: 400px; height: 300px">
    <canvas id="myChart"></canvas>
  </div>
  <script>
    var ctx=$('#myChart');
    var myChart=new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['一月', '二月', '三月'],
        datasets: [{
          label: '銷售業績(百萬)',
          data: [60, 49, 72],
          backgroundColor: [
            "#FF0000",
            "#00FF00",
            "#0000FF"
          ],
          borderColor: [
            "#000000",
            "#000000",
            "#000000"
          ],
          borderWidth: 1
          }]
        }
      });
  </script>
</body>
</html>

結果如下 :




如果需要設定透明度則可呼叫 rgba(R, G, B, A) 函數, 其參數依序為紅 R, 綠 G, 藍 B, 與透明度 A, 顏色值範圍 0~255, 透明度範圍 0~1. 例如 :


測試 6 : 繪製柱狀圖 (6) : 設定框邊與背景色 [看原始碼]

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Chart.js test</title>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
</head>
<body>
  <div style="width: 400px; height: 300px">
    <canvas id="myChart"></canvas>
  </div>
  <script>
    var ctx=$('#myChart');
    var myChart=new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['一月', '二月', '三月'],
        datasets: [{
          label: '銷售業績(百萬)',
          data: [60, 49, 72],
          backgroundColor: [
            'rgba(255, 99, 132, 0.2)',
            'rgba(54, 162, 235, 0.2)',
            'rgba(255, 206, 86, 0.2)'
          ],
          borderColor: [
            'rgba(255,99,132,1)',
            'rgba(54, 162, 235, 1)',
            'rgba(255, 206, 86, 1)',
            'rgba(75, 192, 192, 1)'
          ],
          borderWidth: 1
          }]
        }
      });
  </script>
</body>
</html>

結果如下 :




可見經過設定, 外觀比預設要好看多了, 但比起 jqPlot 會自動派顏色來說, 這似乎有點麻煩.

如果要繪製寬高比為 1:1 的圖形, 可以將 canvas 的 width 與 height 都設為 1, 然後用外層包覆的 div 控制寬或高 : 

  <div style="width:400px;">
    <canvas id="myChart" width="1" height="1"></canvas>
  </div>

例如 : 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Chart.js test</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
</head>
<body>
  <div style="width:400px;">
    <canvas id="myChart" width="1" height="1"></canvas>
  </div>
  <script>
    var ctx=document.getElementById('myChart').getContext('2d');
    //ctx.canvas.parentNode.style.width="300px";
    var myChart=new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
           label: '# of Votes',
           data: [12, 19, 3, 5, 2, 3],
           backgroundColor: [
             'rgba(255, 99, 132, 0.2)',
             'rgba(54, 162, 235, 0.2)',
             'rgba(255, 206, 86, 0.2)',
             'rgba(75, 192, 192, 0.2)',
             'rgba(153, 102, 255, 0.2)',
             'rgba(255, 159, 64, 0.2)'
             ],
           borderColor: [
             'rgba(255, 99, 132, 1)',
             'rgba(54, 162, 235, 1)',
             'rgba(255, 206, 86, 1)',
             'rgba(75, 192, 192, 1)',
             'rgba(153, 102, 255, 1)',
             'rgba(255, 159, 64, 1)'
             ],
           borderWidth: 1
           }]
        },
      options: {
        scales: {
          yAxes: [{
            ticks: {
            beginAtZero: true
            }
          }]
        }
      }
    });
  </script>
</body>
</html>

因為畫布寬高比已經設定為 1:1, 所以只須設定 width 或 height 其中一個即可, 此例是在 canvas 父元素 div 中設定 width (也可以在程式中用 parentNode 設定), 結果如下 : 




只要用 rgba() 善加設定, Chart.js 的繪圖效果真的很棒. 

2020年9月14日 星期一

高科大還書 4 本 (R 語言)

以下四書前三本有人預約要先還 :
  1. 輕鬆學習R語言 : 從基礎到應用,掌握資料科學的關鍵能力
  2. R語言與資料分析實戰 = Practical data processing and analysis using R
  3. Python神乎其技 : 精要剖析語法精髓,大幅提升程式功力!
  4. 微積分之倚天寶劍
兩本 R 語言書還沒看完, 我覺得都值得買. 第二本是韓國人徐珉久寫的, 是具有實戰與教學經驗的人, 唯一缺點是東華在翻譯時沒有把行與列換成台灣慣用法, 韓國似乎與中國一樣, 把陣列的 row 翻成中文的行; 把 column 翻成列, 這會讓台灣人搞不清楚.

2020 年第 37 周記事

本周菁菁有一起回鄉下, 本來週六是要去逛漂漂河的市集, 但傍晚在飄雨, 似乎沒有辦成. 二哥說要準備開學的功課沒回去. 姊姊上回回來時說現在大學生還有在預習下學期功課不多見啊! 我說我以前就這樣啊! 二哥是聽我講我以前做法, 且他對學習很積極, 上大學前就在思考研究所了. 理工科就必須這麼做吧? 姊姊學藝術的就不需要了.

本周大部分時間都在複習整理 jQuery, 太久沒用生鏽了, 連帶多年前剛學就停的 jQuery Mobile 也重新學習, 希望能將目前的桌上型應用搬到手機上, 這是最省事的方式, 現在要我學 Java 與 Kotlin 手機應用開發太耗時間心力了, 學 Swift 還可以接受 (如果有買 Mac mini 的話).

小安的女兒上周滿月, 週六帶回鄉下讓我們瞧瞧, 胖嘟嘟的臉龐與白皙的皮膚真可愛, 小安說希望她快快長大, 哈哈, 我回說等你過了四十歲就知道甚麼叫快轉了, 我真希望時間能慢下來, 一切都慢慢來就好.

二哥大學今天開學, 周六早上跟他去加工區站將腳踏車載到工學院側門, 因為改騎電動車通勤後, 腳踏車變成校內交通工具. 姊姊升大四, 本學期只有十學分, 說要努力接案跟做畢展.

2020年9月13日 星期日

以 jQuery 為基礎的繪圖函式庫 jqPlot

最近在一本舊書上看到 jqPlot 這個以 jQuery 為基礎建構的繪圖函式庫 :

# Beginning JavaScript Charts-With jqPlot, d3, and Highcharts (Apress, 2013)


Source : Apress


利用此函式庫可以在網頁上輕鬆地繪製折線圖, 分布圖, 圓餅圖等等資料分析常見的統計圖形, 但很可惜因為缺乏開發人員繼續投入, 此開源專案已於 2018-09-30 終止開發而封存了, 參考 :

https://groups.google.com/g/jqplot-dev/c/tDkY2z0q2t4

原開發者建議使用者可改用 Chart.js 專案.

jqPlot 首頁與 GitHub 寄存庫如下 :

http://www.jqplot.com/
https://github.com/jqPlot/jqPlot/

下載 jqPlot (最新也是最後版本 1.0.9) :

http://www.jqplot.com/download/

教學文件參考 :

https://www.jsdelivr.com/package/npm/jqplot

中文教學文件參考 :

jqPlot圖表外掛使用說明(一)
[JQUERY]輕鬆使用jquery開發的統計圖表UI---JQPLOT

雖然 jqPlot 很舊了而且已停止開發, 但基於想跟其他繪圖函式庫做個比較, 所以花了點時間緬懷一下這個函式庫, 當初開發者應該花了不少心力.

使用 jqPlot 的方法很簡單, 下載的 zip 解開後, 複製其中的 jquery.jqplot.min.css 與 jquery.jqplot.min.js 這兩個檔案到專案資料夾下即可, 例如 :

<script src="jquery/jquery.min.js"></script>
<script src="jquery/jquery.jqplot.min.js"></script>
<link href="jquery/jquery.jqplot.min.css" rel="stylesheet">

也可以使用 CDN 資源, 參考 :

https://cdnjs.com/libraries/jqPlot
https://www.jsdelivr.com/package/npm/jqplot

例如 :

<script src="jquery/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">

注意, jqPlot 是以 jQuery 為基礎而開發的函式庫, 因此務必先匯入 jQuery 函式庫. 事實上這三個檔案只是 jqPlot 繪圖功能的基本款而已, 較複雜之設定與功能必須另外添加外掛 (plug-in).

jqPlot 的繪圖容器是一個 div 元素, 可用 style 設定容器的大小 :

<div id="chart" style="width:400px; height:300px;"></div>

然後呼叫 $.jqplot("chart", data) 方法傳入容器 id 與繪圖資料 data 即可 :

$.jqplot('chart', data [, options]);

其中第三參數為用來設定繪圖參數的選項物件, 若未指定則以 jqPlot 預設值設定. 其次 data 是資料陣列, 可以是二維 (只有 Y 軸資料) 或三維陣列 (提供 X,Y 軸座標對資料), 這樣可以同時繪製多組圖形, 例如 :

var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77]];

我參考教學文件做了一些簡單的測試 :


測試 1 : jqPlot 繪製單組資料折線圖 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">
    <style>
    </style>
  </head>
  <body>
    <div id="chart" style="width:400px; height:300px;"></div>
    <script>
      $(function(){
        var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77]];
        $.jqplot('chart', data);
        });
    </script>
  </body>
</html>

此例中二維陣列 data 中只有一個元素, 表示只有一組 Y 軸資料, jqPlot 預設會以折線圖繪製圖形, 結果如下 :




可見沒有提供 X 軸資料時預設是以 1 起始之序數 1, 2, 3, 4, ... 為 X 軸座標, 且 X 軸會前後多 1 個單位. 設定繪圖容器 style 屬性中的 width 與 height 就可以控制圖形的大小.

下面是同時繪製多組資料的範例 :


測試 2 : jqPlot 繪製多組資料折線圖 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">
    <style>
    </style>
  </head>
  <body>
    <div id="chart" style="width:400px; height:300px;"></div>
    <script>
      $(function(){
        var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77],
                  [78, 55, 66, 60, 80, 75, 80, 83, 71],
                  [45, 49, 53, 56, 72, 50, 75, 78, 67]];
        $.jqplot('chart', data);
        });
    </script>
  </body>
</html>

此例所傳入之 data 變數為具有多組資料之二維陣列, 結果如下 :




可見當有多組資料時, jqPlot 會自動以不同顏色繪製圖形.

上面兩個例子都只有提供 Y 軸資料, 如果以 [X, Y] 座標對表示的話, 資料就要用三維陣列表示, 範例 1 的 data 應是這樣 :

var data=[[[1, 100], [2, 100], [3, 75], [4, 99], [5, 89], [6, 100], [7, 85], [8, 95], [9, 77]]];

而範例 2 則是如下 :

var data=[[[1, 100], [2, 100], [3, 75], [4, 99], [5, 89], [6, 100], [7, 85], [8, 95], [9, 77]],
                [[1, 78], [2, 55], [3, 66], [4, 60], [5, 80], [6, 75], [7, 80], [8, 83], [9, 71],
                [[1, 45], [2, 49], [3, 53], [4, 56], [5, 729], [6, 50], [7,75], [8, 78], [9, 67]];

實務上同時提供 [X, Y] 座標的場合較常見, 因為 X 軸是 1, 2, ,3, 4 ... 這種畢竟是少數. 注意, [X, Y] 座標軸對不一定要照 X 大小排序, jqPlot 讀取後會自動排序, 當然以資料可讀性來說還是依照 X 由小到大排序為宜.

上面兩個範例所繪製的圖形都沒有傳入第三參數選項物件 options, 這時 jqPlot 就以預設設定繪圖, 預設並無圖形標題 (title) 與軸標籤 (Axes labels), 如果要加上這些東西必須傳入帶有 title 與 axes 屬性之選項物件, 其中 axes 值為一個子物件, 可用 xaxis 與 yaxis 分別設定其標籤 lable 等屬性, 例如 :


測試 3 : 傳入選項物件 options 設定標題與 X, Y 軸標籤 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">
    <style>
    </style>
  </head>
  <body>
    <div id="chart" style="width:400px; height:300px;"></div>
    <script>
      $(function(){
        var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77],
                  [78, 55, 66, 60, 80, 75, 80, 83, 71],
                  [45, 49, 53, 56, 72, 50, 75, 78, 67]];
        var options={title: "月收盤價",
                     axes: {xaxis: {label: "月份"},
                            yaxis: {label: "價格"}
                            }
                     };
                     };
        $.jqplot('chart', data, options);
        });
    </script>
  </body>
</html>

結果如下 : 


可見圖形標題與 X, Y 軸標籤這下都有了.

坐標軸物件 axes 的子物件中, 除了 label 屬性外還有許多其他屬性可用來控制座標軸的顯示, 例如 pad 屬性是用來設定資料邊界與座標軸上下限之間的空隙, 預設會坐標軸上下限會比資料邊界多一個單位, 例如上面範例中 X 軸資料邊界為 1~9, 但預設 X 座標是 0~10, 而 Y 軸也是多出一格空隙, 如果不想要這個空隙, 可將 pad 屬性設為 0 或 1, 例如 :


測試 4 : 用 pad 屬性去除資料邊界與座標軸上下限之間的空隙 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">
    <style>
    </style>
  </head>
  <body>
    <div id="chart" style="width:400px; height:300px;"></div>
    <script>
      $(function(){
        var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77],
                  [78, 55, 66, 60, 80, 75, 80, 83, 71],
                  [45, 49, 53, 56, 72, 50, 75, 78, 67]];
        var options={title: "月收盤價",
                     axes: {xaxis: {label: "月份",
                                    pad: 0 
                                    },
                            yaxis: {label: "價格",
                                    pad: 0 
                                    }
                            }
                     };
        $.jqplot('chart', data, options);
        });
    </script>
  </body>
</html>

結果如下 :




可見現在資料與 X 坐標軸起訖都是 1~9, 而 Y 軸也去掉一格空隙了.

如果 pad 設為 2 的話, 表示空隙會變 2 倍, 例如 :


測試 5 : 將 pad 屬性設為 2 的效果 (空隙增兩倍) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">
    <style>
    </style>
  </head>
  <body>
    <div id="chart" style="width:400px; height:300px;"></div>
    <script>
      $(function(){
        var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77],
                  [78, 55, 66, 60, 80, 75, 80, 83, 71],
                  [45, 49, 53, 56, 72, 50, 75, 78, 67]];
        var options={title: "月收盤價",
                     axes: {xaxis: {label: "月份",
                                    pad: 2 
                                    },
                            yaxis: {label: "價格"}
                            }
                     };
        $.jqplot('chart', data, options);
        });
    </script>
  </body>
</html>

結果如下 :




空隙變兩倍, 所以 X 軸刻度變成從 -4 到 14 了.

其實控制坐標軸起始點除了 pad 屬性外還可以用 min 與 mix 屬性來設定, 只要將其分別設為資料的邊界即可消除空隙了, 例如將範例 3 的 X 軸 xaxis 加上 min 與 max 屬性 :


測試 6 : 用 min 與 max 屬性去除資料邊界與座標軸上下限之間的空隙 [看原始碼]


<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">
    <style>
    </style>
  </head>
  <body>
    <div id="chart" style="width:400px; height:300px;"></div>
    <script>
      $(function(){
        var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77],
                  [78, 55, 66, 60, 80, 75, 80, 83, 71],
                  [45, 49, 53, 56, 72, 50, 75, 78, 67]];
        var options={title: "月收盤價",
                     axes: {xaxis: {label: "月份",
                                    min: 1,
                                    max: 9
                                    },
                            yaxis: {label: "價格",
                                    min: 40,
                                    max: 100 
                                    }
                            }
                     };
        $.jqplot('chart', data, options);
        });
    </script>
  </body>
</html>

結果如下 :




雖然空隙消除了, 但坐標軸刻度卻變成浮點數, 月份變成有小數點, 原因是刻度數目變少了, 在上面範例 4 中 X 軸有 9 個刻度; Y 軸則有 7 個刻度, 現在卻變成 X 軸只有 7 個, 而 Y 軸只有 5 個, 補救的辦法是用 numberTicks 來設定刻度數目, 例如 :


測試 7 : 用 numberTicks 設定坐標軸刻度數目 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">
    <style>
    </style>
  </head>
  <body>
    <div id="chart" style="width:400px; height:300px;"></div>
    <script>
      $(function(){
        var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77],
                  [78, 55, 66, 60, 80, 75, 80, 83, 71],
                  [45, 49, 53, 56, 72, 50, 75, 78, 67]];
        var options={title: "月收盤價",
                     axes: {xaxis: {label: "月份",
                                    min: 1,
                                    max: 9,
                                    numberTicks: 9   
                                    },
                            yaxis: {label: "價格",
                                    min: 40,
                                    max: 100,
                                    numberTicks: 7 
                                    }
                            }
                     };
        $.jqplot('chart', data, options);
        });
    </script>
  </body>
</html>

結果如下 :




這與上面範例 4 繪製之圖形完全一樣. 另外一個方式是利用 ticks 屬性用一個陣列指定要顯示哪些刻度值, 例如 :


測試 8 : 用 ticks 屬性指定要顯示那些刻度值 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/jqplot@1.0.9/jquery.jqplot.min.css" rel="stylesheet">
    <style>
    </style>
  </head>
  <body>
    <div id="chart" style="width:400px; height:300px;"></div>
    <script>
      $(function(){
        var data=[[100, 100, 75, 99, 89, 100, 85, 95, 77],
                  [78, 55, 66, 60, 80, 75, 80, 83, 71],
                  [45, 49, 53, 56, 72, 50, 75, 78, 67]];
        var options={title: "月收盤價",
                     axes: {xaxis: {label: "月份",
                                    min: 1,
                                    max: 9,
                                    ticks: [1,2,3,4,5,6,7,8,9]   
                                    },
                            yaxis: {label: "價格",
                                    min: 40,
                                    max: 100,
                                    ticks: [40,50,60,70,80,90,100]   
                                    }
                            }
                     };
        $.jqplot('chart', data, options);
        });
    </script>
  </body>
</html>

此例用 ticks 陣列列舉要顯示之刻度值, 結果如下 :




但是 X, Y 坐標軸刻度卻變成浮點數!