顯示具有 ♥♥ 標籤的文章。 顯示所有文章
顯示具有 ♥♥ 標籤的文章。 顯示所有文章

2022年2月7日 星期一

MicroPython v1.18 釋出 (以 Witty Cloud ESP8266 開發板測試)

今日追蹤發現 v1.18 已在 2022-01-17 發布, 映像檔下載位址如下 : 

v1.18 版的最大亮點為提升虛擬機器 (VM) 與執行環境 (runtime) 的執行效能, 這是針對在 ESP32, STM3, RP2 以及 MIMXRT 等開發板而設的功能, 在 ESP32 板可提升約 2~3 倍效能, MIMXRT 則達 6 倍, 這主要是透過兩個措施來達成的 : 
  • 添加額外的快取 (cache) 來提升雜湊表的搜尋速度
  • 在執行環境中添加一個快速路徑來加速物件實例運算碼 (op code) 的提取
參考 : 

以下就以去年 11 月底在 Aliexpress 上面買的 Witty Cloud ESP8266 板子來燒錄韌體吧! 參考 :





ESP8266 可用 ESP8266Flasher.exe, Thonny 編輯器, 或 esptools.py 來燒錄韌體. 

第一種方法是使用 Windows 軟體 ESP8266Flasher, 可從 GitHub 下載 :


這塊 Witty Cloud ESP8266 開發板與 D1 mini 或 NodeMCU 一樣都是 4MB Flash, 設定方式完全相同, ESP8266Flasher.exe 的設定如下 : 





然後按住右下角底板 (USB 下載板) 上的 Flash 按鈕後, 再按 ESP8266Flasher.exe 的 Flash 鈕開始燒錄, 只要燒錄開始進行就可以放開底板的 Flash 鈕了. 燒錄完畢用裝置管理員確認 COM 埠後開啟 Putty 連線, 按板上的 Reset 鈕即可看到 MicroPython v1.18 的提示號出現 (若無就按 Ctrl + B 或再 Reset 一次) :




燒錄韌體的第二種方法是使用 Thonny 編輯器內建的映像檔燒錄功能, 參考 :


第三種燒錄 ESP8266 韌體的方法是使用 esptool.py, 其功能最豐富, 包含檢視 Flash 記憶體大小與抹除記憶體內容等功能, 指令如下 :
  • 檢查 Flash 記憶體 :
    esptool.py --port COM8 flash_id
  • 抹除 Flash 記憶體
    esptool.py --chip esp8266 --port COM8 erase_flash
  • 燒錄韌體 :
    esptool.py --port COM8 --baud 115200 write_flash --flash_size=detect -fm dio 0 esp8266-20220117-v1.18.bin
參考 :



以下進行系統檢查 : 

1. 查詢韌體版本 :   

MicroPython v1.18 on 2022-01-17; ESP module with ESP8266
Type "help()" for more information.
>>> print('Hello World!')    
Hello World!
>>> import os    
>>> os.uname()    
(sysname='esp8266', nodename='esp8266', release='2.2.0-dev(9422289)', version='v1.18 on 2022-01-17', machine='ESP module with ESP8266')


2. 系統診斷 : 

>>> import port_diag   
FlashROM:
Flash ID: 164054 (Vendor: 54 Device: 4016)
Flash bootloader data:
Byte @2: 02
Byte @3: 40 (Flash size: 4MB Flash freq: 40MHZ)    # Flash 容量
Firmware checksum:
size: 635976     # 韌體大小約 636 KB
md5: 40b5ad861d83667d6c8ea59fd38c7ff9
True      # 韌體完好無損 

Networking:
STA ifconfig: ('0.0.0.0', '0.0.0.0', '0.0.0.0', '208.67.222.222')
AP ifconfig: ('192.168.4.1', '255.255.255.0', '192.168.4.1', '208.67.222.222')
Free WiFi driver buffers of type:
0: 8 (1,2 TX)
1: 0 (4 Mngmt TX(len: 0x41-0x100))
2: 8 (5 Mngmt TX (len: 0-0x40))
3: 4 (7)
4: 7 (8 RX)
lwIP PCBs:
Active PCB states:
Listen PCB states:
TIME-WAIT PCB states:

如果只是檢查韌體可直接呼叫 esp.check_fw() :

>>> import esp   
>>> esp.check_fw()    
size: 635976   
md5: 40b5ad861d83667d6c8ea59fd38c7ff9
True      


3. 檢查 SRAM 記憶體 : 

>>> import micropython  
>>> micropython.mem_info()
stack: 2096 out of 8192
GC: total: 37952, used: 1392, free: 36560    
 No. of 1-blocks: 16, 2-blocks: 10, max blk sz: 18, max free sz: 2270

可見 Witty Cloud ESP8266 開發板的 SRAM 記憶體大小約 38 KB (與 D1 mini 一樣).


4. 檢查檔案系統 : 

>>> import os   
>>> os.listdir()   
['boot.py']   

可見根目錄下只有 boot.py 一個檔案. 用 open() 開啟 boot.py 後列印檔案內容 :

>>> f=open('boot.py','r')       # 開啟檔案
>>> lines=f.readlines()           # 以列為單位讀取檔案內容
>>> for line in lines:               # 列印檔案
...     print(line)   
...
...
...
# This file is executed on every boot (including wake-boot from deepsleep)

#import esp

#esp.osdebug(None)

import uos, machine

#uos.dupterm(None, 1) # disable REPL on UART(0)

import gc

#import webrepl

#webrepl.start()

gc.collect()

>>> f.close()    # 關閉檔案

可見 boot.py 預設已匯入 os, machine, 以及 gc 模組. 

2020年8月24日 星期一

jQuery 常用技法整理

最近因為改寫公司作業自動化工具的需要重新複習 jQuery, 同時把向市圖借的 jQuery 書籍摘要整理一下趕快還掉, 以免佔用我的書架. 參考書目如下 :
  1. 鋒利的 jQuery (佳魁)
  2. 打造 jQuery 網頁風暴 (佳魁)
  3. jQuery 應用程式設計極速上手 (上奇)
  4. jQuery 高手精技 (旗標)
  5. jQuery 實戰手冊第三版 (碁峰)
  6. jQuery 最強圖解實戰講座 (旗標)
  7. JavaScript 函數活用範例速查辭典 (博碩)
  8. Learning jQuery3 5th Edition (Packt)
  9. JavaScript & jQuery-The Missing Manual (O'Reilly)
其中第 4 與第 6 本旗標的書均翻譯自日文著作, 裡面有許多不依賴 UI 的實作練習, 在寫特色網站時非常值得參考 (尤其是高手精技這本特色是於練習中附帶 API 解說), 但對於只是要呈現內容的網站 (例如顯示或查詢工程感測數據) 而言就不需要, 直接用 jQuery UI 更簡單方便. 第 8 本歐萊里的 Missing Manual 裡面有非常多的實用簡短範例, 是可快速翻查的工作手冊.

jQuery 的 API 參考 :

# https://api.jquery.com/

jQuery 的語法很簡單, 就是先用工廠函數 $() 以選擇器取得元素的 jQuery 物件 (集合), 再呼叫其物件方法 :

$(選擇器).方法(參數1, 參數2, ....)   

選擇器語法是在 CSS 選擇器基礎上再添加一些 jQuery 自己的選擇器, 因此用法基本上與 CSS 選擇器相同, 其次, 有些方法的參數可能是個函式. 

以下是 jQuery 的常用技法, 大多是 jQuery 物件的方法或函數. jQuery 的方法與函數在使用上與 Javascript 最大的不同有如下兩點 :

1. 函式串接 (chaining functions 或 method chains) :

jQuery 物件的方法或 jQuery 頂層函式都可以用點運算符連續串接, 例如 :

$("#img1").width(200).height(300);

2. 自動迴圈 (automatic loops) :  

如果使用 Javascript 操作 DOM 來處理頁面元素的屬性與行為, 例如隱藏頁面中的所有圖片 :

$("img").hide();

選擇器 $("img") 選取了頁面中全部圖片, 如果用 Javascript 去設定這些圖片的樣式來隱藏勢必需要使用迴圈, 呼叫 hide() 卻只需要一個指令, 事實上 jQuery 是在背後自動用迴圈處理掉了.


一. 常用選擇器 : 

jQuery 採用 CSS 的選擇器語法來取得網頁元素, 以下為最常用的基本選擇器用法 :


1. id 選擇器 (# id selector) : 

使用 # 與元素的 id 屬性 (具有唯一識別性) 來取得網頁元素, 語法為 $("#id"),  例如下列 id=mydiv 的 div 元素 :

<div id="mydiv"></div>

則 $("#mydiv") 將傳回此 div 元素的 jQuery 物件 (應該是唯一) :

var mydiv=$("#mydiv");


2. 類別選擇器 (. class selector) : 

類別選擇器使用 "." 與元素的樣式類別來取得網頁元素, 語法為 $(".樣式類別名稱"), 由於網頁中可能有多個元素套用相同樣式類別, 因此它會傳回一個 jQuery 物件集合 (collection), 例如 :

<ul>
  <li class="myclass">A</li>
  <li class="myclass">B</li>
  <li class="myclass">C</li>
</ul>

則 $(.myclass) 將傳回一個 jQuery 物件集合, 類似陣列, 可用 length 屬性取得其長度, 也可用 each() 工具函數拜訪集合之元素 :

var myclass=$(".myclass");
console.log(myclass.length);    //輸出 3
myclass.each(function(idx){       
  console.log(idx + " " + $(this).text());   //分別輸出 0 A, 1 B, 2 C
  });

此處回呼函數內的 this 為配選取之 DOM 元素, 不是被選取的 jQuery 物件, 必須傳入 $() 才是.


3. 元素選擇器 (element selector)  : 

元素選擇器以元素的標籤篩選網頁中的元素, 語法為 $("標籤"), 例如下列選項清單中的 li 元素 :

<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ul>

由於指定的標籤在網頁中可能有多個, 因此也是傳回一個 jQuery 物件集合 :

var myli=$("li");
console.log(myli.length);    //輸出 3
myli.each(function(idx){       
  console.log(idx + " " + $(this).text());   //分別輸出 0 A, 1 B, 2 C
  });


4. 後代選擇器 (descendant selectors) : 

此選擇器可篩選某元素的所有後代元素, 可精確選取所要的元素, 語法為 $("父元素選擇器 後代元素選擇器"), 即中間空一格, 例如下面的 li 是 div 的後代元素 :

<div id="mydiv">
  <ul>
    <li>A</li>
    <li>B</li>
    <li>C</li>
  </ul>
</div>

如果只用元素選擇器 $("li") 將選取頁面上的全部 li 元素, 而使用 $(#myul li) 才能精確選取這三個 li 元素.

var myli=$("#mydiv li");
console.log(myli.length);    //輸出 3
myli.each(function(idx){       
  console.log(idx + " " + $(this).text());   //分別輸出 0 A, 1 B, 2 C
  });


5. 子元素選擇器 (child selector) : 

此選擇器用來選取子元素 (即直接後代, 不含孫元素), 語法是 $("父元素 > 子元素"), 例如下面的 li 是 ul 元素的子元素 :

<ul id="myul">
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ul>

先用 id 選擇器選取父元素 ul, 再用子元素選擇器選取子元素 :

var myli=$("#myul > li");
console.log(myli.length);    //輸出 3
myli.each(function(idx){       
  console.log(idx + " " + $(this).text());   //分別輸出 0 A, 1 B, 2 C
  });


6. 次元素選擇器 (adjacent selector) : 

此選擇器選取緊鄰的下一個元素, 語法為 $("前元素 + 次元素"), 傳回值為一個 jQuery 物件, 不是物件集合, 例如下列網頁 :

<h3>A</h1>
<div>B</div>
<div>C</div>

則 $("h3 + div") 會選取 h3 元素後面跟著的第一個 div 元素 :

var next=$("h3 + div");
console.log(next.length);    //輸出 1
console.log(next.text());     //輸出 B


7. 所有次元素選擇器 (following-all-siblings selector) : 

此選擇器選取所有後面的兄弟元素 (siblings), 語法為 $("前元素 ~ 次元素"), 傳回值為 jQuery 物件集合, 例如下列網頁 :

<h3>A</h1>
<div>B</div>
<div>C</div>

則 $("h3 ~ div") 會選取 h3 元素後面跟著所有同層元素 :

var nextALL=$("h3 ~ div");
console.log(nextALL.length);    //輸出 2
nextALL.each(function(idx){     
  console.log(idx + " " + $(this).text());   //分別輸出 0 B, 1 C
  });


8. 屬性選擇器 (attribute selector) :

此選擇器用來選取屬性值符合特定條件之元素 :


 屬性選擇器 說明
 [attr] 選取全部具有 attr 屬性的元素, 例如 [class] 選取所有含 class 屬性之元素
 [attr=foo] 選取 attr 屬性全等於 foo 之全部元素, 例如 [name=fruits]
 [attr!=foo] 選取 attr 屬性不等於 foo 之全部元素, 例如 [name!=fruits]
 [attr^=foo] 選取 attr 屬性以 foo 開頭之全部元素, 例如 [href^=https]
 [attr$=foo] 選取 attr 屬性以 foo 結束之全部元素, 例如 [href$=tw]
 [attr*=foo] 選取 attr 屬性包含 foo 之全部元素 (含子字串), 例如 [href*=org]
 [attr~=foo] 選取 attr 屬性 (以空格隔開) 包含 foo 之全部元素 (不含子字串), 例如 [class~=foo]
 [attr|=foo] 選取 attr 屬性全等於 foo 或以 foo 開頭之全部元素, 例如 [class|=foo]


其中 [attr*=foo] 與 [attr~=foo] 的差異在於 [attr*=foo] 會包含子字串; 而 [attr~=foo] 則不包含, 在屬性值有多個以空格隔開情形就能看出差異, 例如 class="foo foo1" 與 class="foofoo1" 若用 [attr*=foo] 兩者都會被選到, 但若用 [attr~=foo] 則只有前者會被選到, 因為後者是在子字串中找到 foo, 而 [attr~=foo] 不含子字串, 它是要在空格隔開中全等才符合.


9. 表單選擇器 (form selector) :

此選擇器用於選取表單元素 (form) 內部的 input, select, button, 以及 textarea 等元素, 均以冒號開頭, 為了便於閱讀, 除了 :input 選擇器外, 其他選擇器最好加上標籤名稱, 例如用 input:text, input:radio 或 select:selected 等等 :


 表單選擇器 說明
 :input 選取所有表單元素, 包含 input, select, textarea, 與 button
 :text 選取所有文字欄位元素, 即 <input type="text">
 :password 選取所有密碼欄位元素, 即 <input type="password">
 :radio 選取所有單選圓鈕元素, 即 <input type="radio">
 :checkbox 選取所有核取方塊元素, 即 <input type="checkbox">
 :submit 選取所有提交按鈕元素, 即 <input type="text"> 與 <button type="button">
 :image 選取所有提交圖片按鈕元素, 即 <input type="image">
 :reset 選取所有重設按鈕元素, 即 <input type="reset"> 與 <button type="reset">
 :button 選取所有按鈕元素, 即 <input type="button"> 與 <button type="button">
 :file 選取所有檔案上傳欄位元素, 即 <input type="file">
 :enabled 選取所有啟用 (可與使用者互動) 之表單元素, 即無 disabled 屬性之表單元素
 :disabled 選取所有停用 (無法與使用者互動) 之表單元素, 即有 disabled 屬性之表單元素
 :checked 選取所有被核取之表單元素, 包含被核取之 checkbox 與 radio 元素
 :selected 選取所有被選擇之 select 元素中的 option 元素



二. 常用 jQuery 物件方法 : 


1. 操作網頁元素之隱藏與顯示 :

呼叫 hide() 方法可隱藏網頁元素, 而 show() 則可使其顯示, 例如 :

$("#element").hide(); 
$("#element").show();

呼叫 hide() 時此函數會先將該元素的 display 樣式值 (inline 或 block 等) 記下來, 然後再將該元素的 display 樣式設為 none, 這與呼叫 $("#element").css("display", "none") 的效果一樣;  而呼叫 show() 時此函數會將原先記下來的 display 樣式值恢復 (例如 inline 或 block).


2. 操作網頁元素的內容 : 

所謂網頁元素的內容是指有開始與結束標籤的元素而言, 其內容即夾在兩者之間的部分 (會顯示在瀏覽器上), 也就是 DOM 物件中 innerHTML 屬性之值, 例如下面的 p 元素 :

<p id="para">Hello World</p>

呼叫 html() 即可取得 p 元素的 innerHTML 屬性值 "Hello World" :

$("#papa").html();   //取得網頁元素的內容

如果傳參數 (字串) 進去, 那就是設定 (修改) p 元素的內容 :

$("#papa").html("<b>How are you?</b>");   ////修改網頁元素的內容

另外還有一個 text() 函數可用來存取元素的純文字內容 (去除 HTML 標籤的部分, 包含跳行等字元), 也就是 DOM 物件中的 innerText 屬性之值 :

$("#papa").text();    //取得網頁元素的 innerText 內容
$("#papa").text("How are you?");    //設定網頁元素的 innerText 內容

如果要將某個元素的 innerHTML 內容清空, 可用 empty() 方法達成, 例如上面 id=para 的 p 元素 :

$("#para").empty(); 

這樣會使該 p 元素內容變空白 (但 p 標籤仍在) :

<p id="para"></p>

如果要將某個元素以新的內容或新的標籤取代, 可呼叫 replaceWith(), 例如要將上面的 p 元素內容改為 "How are you" 可以這麼做 :

$("#para").replaceWith("<p id="para">How are you</p>");

注意, 傳入的參數會完全取代該元素 (包含標籤), 因此若只傳入內容 $("#para").replaceWith("How are you"), 則連 p 元素的開始與結束標籤都不見了, 只剩下 "How are you", 因此 replaceWith() 也可以用來更換元素, 例如將 p 元素換成 div 元素 :

$("#para").replaceWith("<div id="para">How are you</div>");


3. 操作網頁元素的屬性 : 

呼叫 attr() 方法可操作元素的屬性, 只傳入一個參數為 getter; 傳入兩個參數為 setter :

obj.attr("屬性名稱") : 傳回屬性值
obj.attr("屬性名稱", "屬性值") : 設定屬性值

例如網頁元素 p :

<p id="para" style="color: blue;">Hello World</p>

var style=$("#para").attr("style");    //傳回 "color: blue;"
$("#para").attr("style", "color: green;");    //設定前景色為 green

attr() 方法很廣用, 例如可用來操作樣式屬性 style 與類別 class.


4. 操作網頁元素的尺寸 :

呼叫 width() 與 height() 可存取網頁元素的尺寸, 語法如下 :

obj.width() : 傳回元素寬度  (數值 : 單位 px)
obj.width(寬度) : 設定元素寬度 (數值或帶 px 單位字串)
obj.height() : 傳回元素長度  (數值 : 單位 px)
obj.height(長度) : 設定元素長度 (數值或帶 px 單位字串)

注意, width() 與 height() 的傳回值是不附單位的數值 (預設單位是 px), 這與呼叫 css("width") 或 css("heigght") 不同, 後者是有帶單位的. 例如下面的按鈕 :

<button id="btn">OK</button>

用 css() 與 width() 取得的預設按鈕寬度不同 :

console.log($("#btn").css("width"));   //輸出 35.2557px  (帶單位且為外尺寸)
console.log($("#btn").width());           //輸出 19.25947 (不帶單位且為內尺寸)

但若經過設定, 取回的值就一樣了 :

<button id="btn1">OK</button><br>
<button id="btn2">OK</button>

$("#btn1").css("width","100px"); 
$("#btn2").width("100px");
console.log($("#btn1").css("width"));   //傳回 100px
console.log($("#btn2").width());   //傳回 100

但用 width() 設定的寬度會比較大一些.

若要從 css() 的帶單位傳回值取出數值, 可用 Javascript 的 parseInt() 函數 :

console.log(parseInt($("#btn1").css("width")));   //傳回 100


5. 操作網頁元素的樣式 :

呼叫 css() 方法可操作元素的樣式, 只傳入一個參數為 getter; 傳入兩個參數為 setter :

obj.css("樣式名稱") : 傳回樣式內容
obj.css("樣式名稱", "樣式內容") : 設定樣式內容

傳入雜湊物件 (鍵值對) 則可一次設定多個樣式 :

obj.css({"樣式名稱1":"樣式內容1", "樣式名稱2":"樣式內容2"} ...) : 設定多個樣式內容

例如網頁元素 p :

<p id="para" style="color: blue;">Hello World</p>

var style=$("#para").css("color");    //傳回 "blue"
$("#para").css("color", "green");    //設定前景色為 "green"
$("#para").css({"color":"green", "font-color":"#0000ff"});  //設定多個樣式

注意, css() 傳回的是 style 屬性中完整的樣式設定, 例如 :

<p id="foo" style="width:200px;color=blue">A</p>

$("#foo").css("width") 傳回值是帶單位的 "200px", 而 $("#foo").width() 傳回的是不帶單位的 "200".


6. 操作網頁元素的樣式類別 : 

呼叫 addClass() 添加樣式 (原有樣式仍在但可能被覆蓋); 呼叫 removeClass() 則移除指定樣式 :

obj.addClass("樣式類別名稱");    //添加樣式類別
ㄌobj.removeClass("樣式類別名稱");    //移除已存在之樣式類別

例如網頁元素 p :

<p id="para">Hello World</p>

可用 $("#para").addClass("p1") 添加下列樣式類別 :

.p1 {color: blue; font-style: italic}

呼叫 $("#para").removeClass("p1") 則可移除已有之樣式類別 p1.

如果要直接覆蓋已有之全部樣式類別可以呼叫 attr() :

obj.attr("class", "樣式類別名稱");    //設定物件之樣式類別 (完全覆蓋)

判斷物件是否含有指定樣式類別可呼叫 hasClass() 方法 :

obj.hasClass("樣式類別名稱");   //若含有指定之樣式類別傳回 true, 否則傳回 false

這可用來切換 (toggle) 是否要套用某個樣式類別 (如果沒有就套用, 已有就移除). 不過 toggle 的功能 jQuery 已經有一個現成的方法 toggleClass() 可用來切換類別是否套用 :

obj.toggleClass("樣式類別名稱");  //若元素無此類別樣式就套用, 若已套用就移除

也可以傳入第二個參數 switch (布林判斷式, 例如 a == 5) 來決定是否套用 :

obj.toggleClass("樣式類別名稱", switch);  //若 switch=true 就套用樣式類別, 否則就移除


7. 操作表單元素的內容 : 

呼叫 val() 方法可以存取文字類表單元件例如 input 文字欄位 (type=text), 密碼欄位 (type=password), 以及文字區域 textarea 等 :

obj.val();    //傳回物件之 value 屬性值
obj.val("請輸入 email");     //設定物件之 value 屬性值

但是對於選項類元件則需配合選擇器才能取得被選擇之選項值.

對於 radio 或 checkbox 元件, 要取得被選取的選項值 (radio) 或值陣列 (checkbox) 除了使用 $("[name=元素名稱]") 外還必須再加上屬性過濾選擇器 :checked 才行, 如果只用 $("[name=元素名稱]"), 則呼叫 val() 會固定傳回第一個選項之值 (不管有沒有選都一樣), 正確的用法如下 :

$("[name=元素名稱]:radio:checked").val();     //取得被選取之 radio 選項值 (單選)

對於可複選的核取方塊 checkbox, 即使選擇器使用 $("[name=元素名稱]:checkbox:checked"), 呼叫 val() 仍然只會傳回第一個選項值, 因為它的傳回值是一個陣列, 必須使用一個空陣列並透過 each 迴圈來逐一將被選取選項值放入空陣列中以取得被選取值陣列, 例如 :

var selected=[];
$("[name=元素名稱]:checkbox:checked").each(function(){
  selected.push($(this).val());
  })

對於單選的下拉式選單 select (無 multiple 屬性者), 直接呼叫 val() 即可取得被選取之選項值, 若都未選傳回 null; 對於可複選的下拉式選單 (有 multiple 屬性者), 取得被選取選項值的做法與 checkbox 類似, 以過濾選擇器 "[name=元素名稱]:selected" 取得物件後, 呼叫 each() 拜訪傳回值陣列, 並將其存入空陣列之中 :

var selected=[];
$("[name=元素名稱]:selected").each(function(){
  selected.push($(this).val());
  })


8. 事件綁定與處理 : 

jQuery 支援滑鼠, 鍵盤, 以及表單等事件, 在 v1.7 版後它棄用 bind() 與 live() 等方法, 改用 on() 的方法來綁定事件與其事件處理函式, 其 API 有如下兩種 :

obj.on(eventType [, selector] [, data], function(e) {})
obj.on(eventsHash [, selector] [, data])

第一參數是要綁定的事件類型字串或事件雜湊, 前者可以是單一的事件類型字串例如 "click", 也可以是以空格隔開的多個事件類型字串例如 "click mouseover keydown" (多個事件綁定一個事件處理函式). 第一參數也可以是以事件類型與其處理函式組成的鍵值對 (hash), 可用來一次綁定多個事件.

備選之第二參數 selector 是事件委託 (event delegation) 的選擇器字串, 用來過濾被選取元素中觸發事件之子節點, 亦即子節點觸發事件, 但是卻在父元素綁定事件處理器. 事件委託給父元素在 以 Ajax 載入子節點操作時很好用, 因為要觸發事件之子節點尚未存在, 只好將事件處理函式綁定給父元素.

備選之第三參數是要傳給事件物件 e 的參數 (鍵值對), 可在事件處理函式中以 e.data 第四參數為必要之事件處理函數. 如果不需要第二與第三參數也不需要傳入兩個 null, 直接略過即可 (jQuery 會自動處理).

jQuery 支援之事件類型如下 :


 滑鼠事件 說明
 click 點擊元素
 dbclick 雙擊元素
 mouseenter 滑鼠游標滑入元素
 mouseover 滑鼠游標滑入元素
 mouseleave 滑鼠游標移出元素
 mouserout 滑鼠游標移出元素
 mousemove 滑鼠游標在元素上移動
 mouseup 放開滑鼠左鍵
 mousedownn 按下滑鼠左鍵

 鍵盤事件 說明
 keydown 按下鍵盤按鍵
 keypress 持續按下鍵盤按鍵
 keyup 放開鍵盤按鍵

 表單事件 說明
 blur 焦點離開表單元素
 focus 焦點進入表單元素
 change 元素值變更 (text, textarea)
 select 文字被選取 (text, textarea)
 submit 提交表單
 focusin 焦點進入表單元素
 focusout 焦點離開表單元素

 其他事件 說明
 resize 視窗大小改變
 scroll 頁面或元素被捲動
 contextmenu 內容選單顯示之前


其中最常用的是按鈕的 click 事件, 例如下列按鈕 :

<button id="btn">按我</button>

呼叫 button 元素物件的 on() 方法為其綁定 click 事件 :

$("#btn").on("click", function(e){
  alert("你按了按鈕!");
  });

可以多個事件綁定一個事件處理函式, 各事件類型字串以空格隔開, 例如 :

$("#btn").on("click mouseover", function(e){
  alert("你按了按鈕!");
  });

傳入第二參數 data 可將資料以鍵值對 (物件) 傳遞給事件物件 e, 在回呼函數中可用 e.data.屬性名稱取值, 例如 :

$("#btn").on("click", {"name":"Tony"}, function(e){
  alert(e.data.name + " 按了按鈕!");     //顯示 "Tony 按了按鈕"
  })

對於這些事件, jQuery 物件也有捷徑方法可以直接綁定事件處理函式 (語法較簡短), 例如 :

$("#btn").click(function(e){
  alert("你按了按鈕!");
  })

捷徑方法也可以傳遞 data 參數 (備選第一參數) :

$("#btn").click({"name":"Tony"}, function(e){
  alert(e.data.name + " 按了按鈕!");     //顯示 "Tony 按了按鈕"
  })

如果要替元素綁定多個事件, 可以用鏈式呼叫 on(), 例如 :

$("#btn").on("click", function(e){
  console.log("按了按鈕!");
  }).on("mouseover", function(e){
  console.log("滑鼠滑過按鈕!");
  });

也可以用事件雜湊來一次綁定多個事件 (為了可讀性可將事件處理函式命名), 例如 :

var eh1=function(e){console.log("按了按鈕!");};
var eh2=function(e){console.log("滑過按鈕!");};
$("#btn").on({click: eh1, mouseover: eh2});     // 鍵用字串 "click": eh1 也可以

傳遞資料的範例如下 (注意, data 是放在第二參數) :

var eh1=function(e){console.log(e.data.name + "按了按鈕!");};
var eh2=function(e){console.log("滑過按鈕!");};
$("#btn").on({click: eh1, mouseover: eh2},{"name":"Tony"});

事件物件 e 常用的屬性除 data 外還有表示滑鼠位置的 pageX 與 pageY; 常用方法有停止元素預設動作 (例如表單元素 form 之提交, 超連結元素 a 的重導向, 或核取方塊狀態之變化等) 的 preventDefault() 與停止事件沿 DOM 樹向上傳遞的 stopPropagation(). 典型的範例是以超連結當按鈕使用時, 必須於事件處理函式結尾以 return false 避免除新載入頁面, 這與連續呼叫 preventDefault() 與 stopPropagation() 效果相同.

有些情況需要取消事件綁定, 這可用呼叫 off() 達成, 其 API 如下 :

obj.off(eventType [, selector] [, handler])
obj.off(eventsHash [, selector]])

例如 :

$("#btn").off("click mouseover");


9. 用 Ajax 取得遠端網頁或資料 : 

Ajax 必須有後端伺服器配合才行, 在本機無法運作. jQuery 的 Ajax 函式與方法主要是由頂層函數 $.ajax(), 頂層捷徑函數 $.get(), $.post 與 $.getJSON(), 以及 jQuery 物件方法 obj.load() 構成, 其中 $.ajax() 是最底層函數, 其它函數與方法均以 $.ajax() 為基礎.

呼叫 $.ajax() 時必須傳入一個設定物件, 主要屬性與方法如下 :


 $.ajax() 常用屬性 說明
 url HTTP 請求的目標網址 (字串)
 type HTTP 請求的方式 ("GET" 或 "POST"), 預設為 "GET"
 data 請求附帶的參數資料, 可用物件 {a:1, b: 2} 或字串例如 "a=1&b=2"
 dataType 預期伺服器回應的資料類型字串 (text/html/xml/json/script/jsonp)
 success 請求成功時要執行之回呼函數, function(data, textStatus)
 error 請求失敗時要執行之回呼函數, function(XMLHttpRequest, textStatus)
 complete 請求送出時要執行之回呼函數, function(XMLHttpRequest, textStatus)
 beforeSend 請求送出前要執行之回呼函數, function(XMLHttpRequest)
 cache 是否在記憶體快取此結果 (布林值), 預設為 true 


伺服端的 Ajax 回應資料會傳入 success 屬性值的回呼函數參數 data 中, 例如載入遠端伺服器上的 HTML 檔 test.htm 時, 可將回應網頁從回呼函數參數 data 中取出後用 html() 方法填入 id=result 的指定元素內容中 :

$.ajax({
  type: "GET",
  url: "test.htm",
  dataType: "html",
  success: function(data) {
    $("#result").html(data);
    },
  error: function(xhr) {
    alert(xhr.status);
    }   
  });

動態網頁需要傳遞參數給伺服端的程式處理, 傳遞參數通常用 POST 方式將參數放在 HTTP 的 body 中傳送, 也可以用 GET 方式放在 HTTP 標頭中傳遞. 參數可以附在 url 屬性後面以查詢字串方式傳遞; 也可以用鍵值對方式放在 data 屬性中傳遞, 後端程式擷取 HTTP 參數的方式以 PHP 而言可用 $_GET[] 或 $_POST[] 取得, 但要視參數是放在 url 或 data 屬性而定 :
  • type=GET :
    不論 url 或 data 的參數一律轉成查詢字串在標頭中傳送, 後端要用 $_GET[] 擷取參數. 
  • type=POST :
    url 參數在標頭中傳送, 用 $_GET[] 擷取; data 參數在本體中傳送, 用 $_POST[] 擷取.
但不管傳遞方式為何, 後端程式用 $_REQUEST[] 都可以取出所傳遞的參數, 例如 :

$.ajax({
  type: "POST",
  url: "test.php",
  dataType: "html",
  data: {user: "tony", pwd: 123},
  success: function(data) {
    $("#result").html(data);
    },
  error: function(xhr) {
    alert(xhr.status);
    }   
  });

回應 HTML 資料的後端 PHP 範例如下 (注意 header) :

<?php
header("Content-Type: text/html; charset: utf-8");
echo "<p>get :user=".$_GET['user'].", pwd=".$_GET['pwd']."</p>".
     "<p>post : user=".$_POST['user'].", pwd=".$_POST['pwd']."</p>".
     "<p>post : user=".$_REQUEST['user'].", pwd=".$_REQUEST['pwd']."</p>".
     "<p style='font-weight:bold;'>Hello World!</p>".
     "<p id='normal'>Hello World!</p>";
?>

實際應用上是將表單元素之輸入值提交給後端伺服器, 表單元素 form 的 jQuery 物件有一個 serialize() 方法可將表單內有 name 屬性的元件輸入值轉成查詢字串, 例如下列登入表單 :

 <form id="login_form">
   <label for="user">帳號 : </label>
   <input type="text" id="user" name="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd" name="pwd">
   <button id="login">登入</button>
</form>

可用 $("#login_form").serialize() 將表單內的輸入元件值轉成 "user=tony&pwd=12345" 的查詢字串傳遞到後端, 例如 :

$.ajax({
  type: "POST",
  url: "test.php",
  dataType: "html",
  data: $("#login_form").serialize(),
  success: function(data) {
    $("#result").html(data);
    },
  error: function(xhr) {
    alert(xhr.status);
    } 
  });

除了 $.ajax() 外 jQuery 還提供較高階的捷徑函數, 較常用的是這三個 :
  • $.get() : 以 GET 方法取得遠端伺服器檔案
  • $.post() : 以 POST 方法取得遠端伺服器檔案
  • $.getJSON() : 以 POST 方法取得遠端伺服器 JSON 檔案
這些都是以 $.ajax() 為基礎包裝的捷徑函數. 例如 :

$.get({
  url: "test.php",
  dataType: "html",
  data: $("#login_form").serialize(),
  success: function(data) {
    $("#result").html(data);
    }   
  });

呼叫 $.post() 則以 POST 方式發出請求 :

$.post({
  url: "test.php",
  dataType: "html",
  data: $("#login_form").serialize(),
  success: function(data) {
    $("#result").html(data);
    }   
  });

若僅僅是查詢資料, 伺服端更精簡的方式是直接回應 JSON 資料再填入網頁內, 例如 :

{"user":"tony","pwd":"123"}

回應此資料之後端程式 test_json.php 如下 :

<?php
header("Content-Type: application/json");
$user=$_REQUEST["user"];           
$pwd=$_REQUEST["pwd"];
$response=array(
  'user' => 'tony',
  'pwd' => '123'
  );
echo json_encode($response);
?>

用 POST 查詢 JSON 資料 :

$.post({
  url: "test_json.php",
  data: $("#login_form").serialize(),
  success: function(data) {
    var user=data.user;
    var pwd=data.pwd;
    var result="帳號=" + user + " 密碼=" + pwd;
    $("#result").html(result);
    }
  });

用捷徑函數 $.getJSON() 請求 JSON 資料 :

$.getJSON({
  url: "test_json.php",
  data: $("#login_form").serialize(),
  success: function(data) {
    var user=data.user;
    var pwd=data.pwd;
    var result="帳號=" + user + " 密碼=" + pwd;
    $("#result").html(result);
    }
  });

詳細測試參考 :

# 測試 jQuery 的 Ajax 函數 $.ajax()
# jQuery 的 Ajax 捷徑函數測試
# 測試 jQuery 的 Ajax 方法 load()


三. 工具函數 (utilities) :

jQuery 的工具函數是定義在頂層命名空間的函數, 而非任何 jQuery 物件之方法, 因此可用 jQuery. 或 $. 直接呼叫, 以下是較常用的函數 :


1. 迭代 (迴圈) 函數 $.each() : 

此函數可用來迭代陣列與物件, 但不能用來迭代字串中的字元 (迭代字串要用 for in 或 for of).

$.each(array/object, function(key/index, value))
第一個參數是要迭代的陣列或物件, 第二個參數是處理迭代過程的回呼函數. 回呼函數的第一參數是陣列的索引或物件的屬性 (鍵), 第二參數是元素或屬性之值. $.each() 的主要作用場域是在回呼函數而非傳回值, 其傳回值為第一參數 (即被跌代的物件或陣列).

陣列的迭代範例如下 :

var arr=["a", "b", "c"];
$.each(arr, function(idx, value){
  document.write("arr[" + idx + "]=" + value + ", "); 
  });

結果輸出 arr[0]=a, arr[1]=b, arr[2]=c,.

物件迭代的範例如下 :

var obj={name:"tony", gender:"male", age:18};
$.each(obj, function(key, value){
  document.write("obj." + key + "=" + value + ", "); 
  });

結果輸出 obj.name=tony, obj.gender=male, obj.age=18,.


2. 陣列過濾函數 $.grep() : 

此函數用來過濾陣列中的元素, 它有 1~3 個可能參數, 傳回值為一個陣列, API 如下 :

var arr=$.grep(array, function(value [, index]) [, invert]) {})

第一個參數是要過濾的陣列, 第二參數是負責過濾的回呼函數, 其第一參數為陣列之元素值, 第二可選參數為其索引, 第三參數為布林值, 預設為 false (正向過濾).

在迭代陣列中的每一個元素時, 回呼函數必須用條件運算傳回 true 或 false, 如果傳回 true, 則此元素會被 $.grep() 傳回給接收陣列 arr; 否則不會被傳回, 此即正向過濾功能. 若回呼函數第三參數 invert 傳入 true 則剛好相反, 即只有回呼函數傳回 false 時將元素回傳給接收陣列 (反向過濾), 範例如下 :

var arr1=[34,78,99,84,56,22,100];
var arr2=$.grep(arr1, function(value){ //過濾元素值
  return value >= 60;   //傳回及格分數 arr2=[78,99,84,100]
  });
var arr3=$.grep(arr1, function(value, index, true){ //過濾元素值
  return value >= 60;   //傳回不及格分數 arr3=[34,56,22]
  });


3. 陣列或物件用 $.map() 轉換 :

此函數可在迭代陣列元素或物件屬性的過程中, 於回呼函數裏透過運算傳回新陣列元素, 傳回值為一個陣列, API 如下 :

var arr=$.map(array/object, function(value [, index/attr]){})

第一參數為待轉換陣列或物件, 第二參數為回呼函數, 回呼函數的第一參數為陣列元素或物件屬性之值, 備選之第二參數為索引或屬性 (鍵). 此函數 API 與功能與 $.grep() 很像, 都是傳回一個陣列, 差別在於 $.grep() 回呼函數利用 return true/false 來控制要不要傳回原陣列元素, 而 $.map() 回呼函數中的 return 是直接傳回新陣列元素值, 例如 :

var score=[34,78,99,84,56,22,100];
var arr=$.map(score, function(value){
  return value < 60 ? value + 5 : value;     //不及格分數加 5 分後傳回
  });

結果傳回新陣列 arr=[39,78,99,84,61,27,100]. 如果傳回 null 則新陣列不會增加新元素, 例如 :

var score=[34,78,99,84,56,22,100];
var arr=$.map(score, function(value){
  return value < 60 ? null : value;     //傳回及格者分數
  });

結果傳回新陣列 arr=[78,99,84,100].


4. 陣列合併函數 $.merge() :

此函數可合併兩個陣列, 其 API 如下 :

var arr=$.merge(array1, array2)
合併後的新陣列為 array1 並且被傳回, 而 array2 則不變, 例如 :

var arr1=[1,2,3];
var arr2=[4,5,6];
var arr3=$.merge(arr1, arr2);

合併後 arr1 與傳回值 arr3 結果都是 [1,2,3,4,5,6], 而 arr2 仍為 [4,5,6] 不變.


5. 用 $.inArray() 檢查陣列中是否含有特定元素 :

函數 $.inArray() 之 API 如下 :

var idx=$.inArray(value, array [, fromIndex])

第一參數為要檢查之元素, 第二參數為陣列, 可選之第三參數為開始檢查之索引 (預設 0), 如果有找到就傳回該元素之索引, 否則傳回 -1, 例如 :

var fruits=["apple", "grape", "guava"];
var idx=$.inArray("grape", fruits);          //陣列中有此元素, 傳回其索引 1
var idx=$.inArray("pineapple", fruits);   //陣列中無此元素, 傳回 -1

詳細測試請參考 :

# jQuery 的工具函數測試


四. 實用範例 : 

以下摘要整理一些常用的實用範例.


1. 觸發滑鼠移動事件 (mousemove) 顯示座標 : 

此例網頁中有一 p 元素用來顯示滑鼠座標位置 :

<p></p>

當滑鼠移動時, 它會觸動 window 物件的 mousemove 事件, 可以利用事件物件之 pageX 與 pageY 屬性取得滑鼠位置之 (x, y) 座標 :

$(window).mousemove(function(e){
   $("p").html("X=" + e.pageX + " Y=" + e.pageY);
   });


參考 :

# [Jquery] 複選的checkbox取值
# [jQuery] checkbox 及 radio 設定值
# [jQuery] select 元件的取值及給值
# [Javascript/Jquery] 移除textarea中空行、空白行

2020年1月1日 星期三

jQuery 學習筆記索引

我大概在 2008 年開始接觸學習與使用 jQuery, 十幾年來斷斷續續寫下少許筆記, 但最近用 Google 搜尋卻沒找到幾篇, 所以還是靠自己手工做索引較實在. 最近因為重寫系統需要用到 Ajax, 把十年前學過的 jQuery 複習了一遍, 因為當時沒時間寫筆記, 所以現在才要花時間找書重讀, 如果有筆記就可以迅速恢復功力了.

這次改採打游擊策略, 以主題為軸心來整理較靈活, 放棄按部就班的系統性寫法, 前後關係就靠索引來編排啦. jQuery 參考書目如下 :
  1. 鋒利的 jQuery (佳魁)
  2. 打造 jQuery 網頁風暴 (佳魁)
  3. jQuery 應用程式設計極速上手 (上奇)
  4. jQuery 高手精技 (旗標)
  5. jQuery 實戰手冊第三版 (碁峰)
  6. jQuery 最強圖解實戰講座 (旗標)
  7. JavaScript 函數活用範例速查辭典 (博碩)
  8. JavaScript & jQuery-The Missing Manual (O'Reilly)

2013~2014 年 :

# 模仿 jQuery 的 $ 工廠函數
# 測試 jQuery 的 Ajax 方法 load()
# jQuery 的 val() 設值問題 (setter)
# 核取方塊 Checkbox 的存取方法

2019 年 :

# jQuery 還要學嗎?
# 測試 jQuery 的 Ajax 函數 $.ajax()
# jQuery 的工具函數測試
# 擴充 jQuery 的工具函數
# jQuery 的 Ajax 捷徑函數測試

2020 年 :

# jQuery 常用技法整理


jQuery 官方教學文件參考 :

# https://api.jquery.com/
# jQuery API 中文文档

2020 筆記第一篇就給 jQuery 吧! 學了一半的 Vue 的決定暫停, 因為學多不如學精, 所以我想從最熟悉的 jQuery 再出發!

~進行中~

2019年12月17日 星期二

Vue 學習筆記索引

其實 Vue 我只是淺嚐即止就沒時間繼續學習, 但我很喜歡 Vue 的簡約風格, 如同當初學 jQuery 那樣很容易燃起學習的火花. 今日看到程式前沿的這篇 "Vue中容易被忽視的知識點" 讓我想起了 Vue, 就做了索引方便複習, 也刺激自己繼續加油 !

# 關於前端網頁框架 Vue
# Vue 學習筆記 (一) : 環境配置
# Vue 學習筆記 (二) : MVVM 架構與雙向資料綁定
# Vue 學習筆記 (三) : computed (計算函數) 與 filters (過濾器) 測試
# Vue 學習筆記 (四) : Directives (樣版指令) 測試

~進行中~

2019年11月15日 星期五

Hostinger 虛擬主機免費帳戶升級為 Single Web Hosting

上個月本已決定放棄續租 Hostinger 虛擬主機服務, 但這幾天覺得 000a.biz 等免費主機不牢靠, 我的測試網站有時還是需要用到 PHP, 在我將運算轉移到 AWS/GCP 雲端或本地樹莓派之前, 還是需要一個穩定的網站較好.

前天登入 Hostinger 時看到有升級 9 折優惠, 一次繳四年費用平均每月 0.8 英鎊, 四年費用 38.4 英鎊, 折合台幣 38.4*40.12=1541 元, 即每年 385 元台幣, 平均每個月 32 元, 一天一元左右, 這價格真的很便宜, 所以就有了新的想法, 乾脆將最早申請但被停用的免費帳號以這個優惠價升級, 這樣又可以再用四年 Premium 主機了 :




對比 11/26 即將到期的原主機續約價三年 106.2 英鎊 (平均每個月 2.95 英鎊) 是 3.7 倍的價差, 參考下面這兩張將到期的續約報價單 :




今天早上去衛武營勘查周日體育活動場地回來, 中午吃過飯後使用玉山信用卡付帳升級 (合台幣 1506 元), 馬上收到 Hostinger 回函, 表示正在搬移資料到新的伺服器, 24 小時暫時不要存取檔案系統 :




至於域名一年 12 英鎊, 約合台幣 480 元, 平均每月 40 元, 原本傾向不續約, 直接用主機提供的 16mb.com 免費域名, 但要買也 OK, 這幾天再考慮看看.


2019-11-17 補充 :

昨天登入 Hostinger 檢查剛升級的 16mb.com 帳戶時, 發現上面有一個 "Setup" 鈕, 我以為是要進去設定網站才會動, 沒想到那是即將到期的 Premium 帳戶續約用的, 結果就收到一張 4172 元的帳單, 趕緊聯繫客服取消訂單, 約 7 天才會退款.

今天下午從衛武營體育活動回來, 檢查 16mb.com 還是不能用, 再次聯繫客服, 回覆說原升級帳戶不知為何原因有問題待查, 他先幫我見一個新的, 但 shared hosting 帳號後面多加一個 6, 雖然長了些, 也還可接受.

總結這個新的虛擬主機訊息為 :

1. 此為 Single web hosting (只能一個網站, Premium 有 100 個)
2. MySQL 資料庫只有 2 個
3. 2023-11-24 到期 (4 年)

xyz 域名要到 2020-05-24 才到期 (原先應該是 2019-11-24, 今天工程師幫我設定時說有半年展延), 故 2019-11-24 Premium 服務終止後再去設定改連接到 16mb.com.

2019年11月2日 星期六

EasyUICMS (PHP 版) mysqli 函式庫更新備忘

因為昨晚在樹莓派安裝 PHP 出現問題, 所以乾脆在 Win10 筆電上用 xampp 免安裝版來把 EasyUICMS 重新架起來, 參考 :

# 安裝 XAMPP PHP 架站工具包

但安裝 EasyUICMS 時卻發現安裝成功的無回應, 不過用 phpMyAdmin 檢查資料庫又都正常建立, 直接瀏覽首頁有出現登入畫面, 只是輸入預設帳密同樣沒回應 :




開啟 Chrome F12 偵錯模式發現, 原來是 mysqli 函式庫裡面我用了 each() 函數來解析關聯式陣列的 key 與 value, 而此函數已經被廢棄不用了 (deprecated), 訊息如下 :




<b>Deprecated</b>:  The each() function is deprecated. This message will be suppressed on further calls in <b>D:\xampp\htdocs\twstock\lib\mysqli.php</b> on line <b>232</b><br />
{"status":"success","msg":""}

意思是 /lib/mysqli.php 函式庫第 232 行的 each() 函數被廢止, 導致 JSON 回應訊息的 msg 屬性為空字串, 所以登入畫面無回應. 程式流程如下 :

index.php --> sys.php --> /lib/mysqli.php

查了一下 sys.php 相關程式碼, 是在 login 這個操作時呼叫了 mysqli,php 函式庫中的 update() 函數去更新使用者登入失敗次數所導致 :

switch ($op) {
  case "login" : {
     .......
            //處理嚴格認證政策 : 失敗次數歸零
            if ($auth_policy=="strict") { //嚴格認證
              //重設 users 中的 login_fail_count
              $data_array["login_fail_count"]=0;
              $ret=update("sys_users",$data_array,"account",$account);
              $data_array=null;
              } //end of if
     .......
     }
  }

而在 lib/myqli.php 裡, update() 函數使用了 each() 來解開關聯式陣列中的 key 與 value 來製作 SQL 的 UPDATE 指令 :

  for ($i=0; $i<$cnt; $i++) {
       list($key,$value)=each($data_array);
       $setting_list.= $key."="."\"".$value."\"";
       if ($i!=count($data_array)-1) {$setting_list .= ",";}
       }

查詢 PHP 文件發現 each() 已在 PHP 7.2.0 之後被廢棄不用了, 參考 :

# https://www.php.net/manual/en/function.each.php

"Warning This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged."

而我使用的 XAMPP 的 PHP 版本剛好是 7.2.0 之後的 7.2.4 版 :

"Welcome to XAMPP for Windows 7.2.4"

實在懶得去找舊版 XAMPP 來跑, 不如趁此機會將 mysqli 函式庫升版. 替代 each() 的方法很簡單, 就是改用 foreach 迴圈即可, 參考 :

# How to resolve this deprecated function each php

在 mysqli.php 函式庫中用到 each() 的函數不只 update(), 只要含有 list($key,$value)=each($data_array) 的程式碼都改寫, 例如 :

  .......
  $cnt=count($data_array);
  for ($i=0; $i<$cnt; $i++) {
       list($key,$value)=each($data_array);
       $fields_str.="`".$key."` ".$value;
       if ($i!=count($data_array)-1) {$fields_str .= ",";}
       }
  $SQL="CREATE TABLE IF NOT EXISTS `".$table."` (".$fields_str.")";
  .......

更新為如下寫法 :

  .......
  $i=0;
  foreach ($data_array as $key => $value) {
       $fields_str.="`".$key."` ".$value;
       if ($i != count($data_array)-1) {$fields_str .= ",";}
       ++$i;
       }
  ......

注意, mysqli.php 函式庫中的 create_table() 與 create_fields() 這兩個函數原先用的參數名為 $field_array, 這次全都一致改為 $data_array. 經過如此調整後重新瀏覽首頁就可正常安裝系統了 :





沒想到 PHP 升版造成的麻煩還真不少. 雖然 PHP 較少用, 但學過的功夫忘掉卻也可惜, 忙完這陣子後要整理一下 PHP 筆記了, 畢竟目前免費主機還是以 PHP 主機最多.

另外值得一記的是, 我把 index.php 與 main.php裡面的所有 jQuery 相關套件都改為本地提供, 不再使用 CDN, 原因是在檢視 Hostinger 上的網站時, 發現 EasyUI 的版面格式跑掉了, 改成本地供檔就沒問題了 (EasyUI v1.4.1, 還好當時有下載保存).

我想應該是新版 EasyUI 有 bug, 前陣子在測試 Django 模板時也發現 Tab 面版出現標頭與內容不匹配情形 (內容部份凸出來), 等再回頭測試 Django 時也要改為本地供檔, CDN 供檔無法保證網頁外觀品質.

2019-11-02 補充  :

晚上回到鄉下將專案上傳到 000webhost 測試, 安裝正常但無法登入, 檢查 sys_users 資料表竟然無預設之管理員密碼, 即 install.php 中的 insert 指令執行失敗, 檢查研判是定義欄位時, 有些欄位我多加了 NOT NULL, 而 insert 時又沒有填寫這些 NOT NULL 欄位的話, 在 localhost 本機沒問題, 但在某些主機可能會有問題.

    //建立 sys_users 資料表
    $data_array["id"]="smallint(6) NOT NULL AUTO_INCREMENT PRIMARY KEY";
    $data_array["account"]="varchar(255)";
    $data_array["password"]="varchar(255)";
    $data_array["name"]="varchar(255)";
    $data_array["theme"]="varchar(255)";
    $data_array["email"]="varchar(255)";
    $data_array["maillist"]="char(1)"; //"Y","N"
    $data_array["level"]="tinyint(4)"; //0(unapproved),1(users), ~9(admin)
    $data_array["mobile_phone"]="varchar(255)"; //for SMS
    $data_array["send_sms"]="char(1)"; //"Y","N", 是否傳送簡訊
    $data_array["login_count"]="int(11) NOT NULL DEFAULT '0'"; //登入成功次數
    $data_array["last_login"]="datetime NOT NULL"; //最近一次登入時間
    $data_array["login_fail_count"]="tinyint(4) NOT NULL DEFAULT 0"; //失敗次數
    $data_array["last_3_passwords"]="varchar(255)"; //最近三次密碼a-b-c
    $data_array["locked"]="char(1) NOT NULL DEFAULT 'N'"; //'Y'=locked
    $result=create_table("sys_users",$data_array);
    if ($result) {$msg .= "建立資料表 sys_users ... 完成!
";}

去除這些多餘的 NOT NULL 後狀況解除.

2019年10月7日 星期一

測試網站搬遷後要做的配套異動

由於決定不再租用 hostinger 虛擬主機, 所以昨天先下載程式檔案與資料庫, 然後將 test 測試目錄分批上傳 (一次傳 5MB 以下的 zip 檔較保險) 到 000a.biz 免費網站 yhhuang1966.000a.biz 上, 同時更改部落格中的超連結, 測試 OK, 例如 :

# http://yhhuang1966.000a.biz/test/easyuitest/easyui-datagrid-6.htm
# http://yhhuang1966.000a.biz/test/extjstest/extjs_grid_28.htm

關於 000a.biz 申請步驟參考 :

# 申請免費 PHP 虛擬主機 000a.biz 與 000webhost.com

檔案上傳完成後還要做三件事 :


1. 建立資料庫與測試資料表 :

進入主機的資料庫管理程式 myPhpAdmin 新增一個以 "_test" 結尾的資料庫, 例如 "a000b_24296xxx_test", 然後用 SQL 功能建立如下資料表 :
這些是 Ajax 測試程式要用到的資料表.


2. 修改 MySQL 連線資訊 :

因為之前測試 Ajax 功能都是直接把 MySQL 連線資料放在 PHP 程式中, username 與 database 名稱都會不同, 要一一去改很麻煩, 所以這次我乾脆將 MySQL 連線資訊獨立出來, 寫成如下 db.php 檔 :

<?php
$host="sql308.000a.biz";
$username="a000b_24296xxx";
$password="123456";
$database="a000b_24296xxx_test";
?>

然後修改每一個提供 Ajax 資料源的 PHP 程式, 用 include("db.php") 匯進來, 以後搬主機只要改 db.php 即可, 例如如下很多範例程式都會用到的 get_stocks.php :

<?php
header('Content-Type: text/html;charset=UTF-8');
include("db.php");
$conn=mysql_connect($host, $username, $password); //建立連線
mysql_query("SET NAMES 'utf8'"); //設定查詢所用之字元集為 utf-8
mysql_select_db($database, $conn); //開啟資料庫
..... (餘略)


3. 修改部落格文章中的超連結 :

切到 HTML 格式, 將內容複製到編輯器中搜尋替換網址, 這很費時 (因為累積太多測試文章了), 有用到時再改.

等我有空搞定 Git 後再來將全部測試源碼都放在 GitHub 上寄存分享.

參考 :

# PHP 列出當前資料夾中所有目錄_PHP教程
# [ PHP ] – 列出該路徑下所有的檔案名稱包含子目錄

2019年8月1日 星期四

回復舊版 Win10 停止自動更新並建立修復隨身碟

上週我的 Swift 5 筆電  windows Update 顯示 Win10 已有新版 (1903 版), 問是否要下載更新, 查了一下網路發現此版在三, 四月就釋出, 好像沒聽到有災情新聞傳出就升版, 用了一周也還好, 但是昨天下班回到家, 發現睡眠中的 Win10 喚不醒, 長按 Power 鍵強制關機後再開, 原來又有一次小更新, 等它 Update 重新啟動, 發現右下角的 WiFi 電波小圖示不見了 :




沒這個電波符號我要怎麼切換熱點啊? 所以就去爬文如何設定找回 WiFi 電波圖標, 找到下面這篇兩光的 :

# #問 #win10 wi-fi功能不見

裡面有人回復說把鍵盤上方的 F1~F12 功能鍵都按一遍試試, 不按還好, 按到 F2 (此鍵上面有電波) 後竟然無法上網, 連藍芽滑鼠也不動了, 右下角狀態列出現一個飛機圖標 (飛航模式), F2 應該就是開關無線訊號用的, 我再按一次網路恢復了, 但飛機圖標還在, 藍芽滑鼠還是不動, 去裝置管理員那邊重新掃描網路裝置無效, 啊, 實驗做一半遇到這個真的快瘋了, 一定是今天 1903 版的小更新出了甚麼問題.

我去 Win10 設定的 "更新與安全性" 想要看看是否還有更新, 但怎麼按  "更新與安全性" 都沒反應, 應該說, 整個 "更新與安全性" 裡面每一個都沒反應, 連像要從 D 碟裡面購機後製作的 WindowsBackup 復原都沒辦法, 這個該死的 Windows Update 幹嘛有事沒事自動更新咧?





後來我找到 Acer 網站的這篇 :

# 如何透過Hot Key (Alt+F10) 將作業系統還原至出廠預設?(適用原出貨作業系統為Windows 10)

試著在開機 Acer Logo 出現時按下 Alt+F10 想說乾脆還原到出廠狀態, 但是 Alt+F10 似乎無效 (註 : 客服說要一開機就按, 不要等 Logo), 根本不會進入藍底白字畫面. 但重開機幾次後發現 Win10 的設定畫面可以動了, 進去 "更新與安全性" 畫面後, 點選 "復原", 發現右邊有一個 "復原到先前的 Windows 10 版本" 的開始按鈕, 想說就試試看, 或許不用從 WindowsBackup 復原, 只要回到上周升 1903 版之前即可, 所以就按下 "開始", 過了大約 5 分鐘, 果然就恢復到升版前狀態了, 參考 :

# Windows 10 中的復原選項




這次真的領教到 Windows Update 自動更新的禍害了, 它可能會讓你好幾天都無法上工! 雖然我還有一台 INHON 小筆電備用, 但曾經滄海難為水, 習慣 Swift 5 的 15.6 吋大螢幕後實在不習慣小螢幕! 但要如何關閉自動更新呢? 參考下面這篇 :

# Windows 10老是自動更新?教你如何關閉它、工作不再被打斷

原來要在 "服務" 裡面去關閉, 按左下角工具列的放大鏡, 搜尋 services 會找到 "服務", 點進去找 "Windows Update" 這列, 按下 "停止" 鈕並且將啟動類型改為手動按底下確定鈕即可 :







一年做兩次手動更新即可, 我都用手機上網 IP 不時在變, 久沒用就會斷線, 不太需要擔心安全性問題. 反而是三不五時自動更新工作做一半要求重開機真的很煩, 像這次這樣更新後捅出樓子更麻煩.

經過這次教訓, 要把 Acer 這台筆電的緊急救援 SOP 建立起來, 參考 :

# 如何建立USB修復磁碟?(適用原出貨系統為Windows 10)
# Win10-使用 Acer Recovery Management 還原原廠預設值步驟
# 如何透過Hot Key (Alt+F10) 將作業系統還原至出廠預設?(適用原出貨作業系統為Windows 10)

Win10 本身的備份還原程序很麻煩, 參考下面幾篇的圖文說明 :

# 【教學】Windows10 系統備份與還原
# acer 基本還原只要會基本的復原重灌,就可以不用一直請人幫你囉~!
# 重要!! 購買 Windows 10 新筆記型電腦及剛安裝完 Win 10 作業系統一定要做的設定!!

最簡單的是定期設定還原點, 開啟 "控制台/系統", 按 "系統保護" :






先點 C 碟, 再按 "設定", 點 "開啟系統保護", 設定最大使用率按確定後按 "建立" 並為此還原點取一個名字即可. 如果系統只是小小的不順, 只要點上圖中的 "系統還原" 鈕, 從列表中選一個還原點再按下一步即可, 嚴重的才用映像檔還原 :




另外我找到一款強國人的 "易数一键还原" 可以很方便地為 Win10 做系統備份與還原, 且備份檔比 Win10 本身製作的映象檔還小.


20190801 補充 :

下午打 0800258222 按 9 與 Acer 客服詢問, 若筆電的 Acer Care Center 叫不出來無法還原, 想要回到出廠狀態可在開機時立刻按 ALT + F10 即可進入重設電腦的藍底白字畫面. 第二個方式是在開機狀態下按住 SHIFT 鍵, 然後在關機選單裡選 "重新啟動", 這樣重開機就會進入重設電腦畫面 :




另外還詢問了進 BIOS 方法為開機時按下 F2. 客服建議最好準備一支 32GB 隨身碟, 用 Care Center 備份還原中的 "建立出廠預設備份" 來建立系統修復工具, 即使 C 碟無法開機也能透過此隨身碟來修復.

開啟 Acer Care Center 後先插入 32GB 的隨身碟, 然後按 "建立出廠預設備份" 裡的 "開始使用" 鈕, 就會開始掃描 USB 埠是否有隨身碟, 掃到之後確認是這支隨身碟按 "下一步" 即開始備份 :








這過程還蠻久的, 大概要一個半小時. 做完後最好找張貼紙寫上此為某電腦修復隨身碟, 免得久了之後不知這是做啥用的.

2019-08-01 補充 :

找到我之前記下的 Win10 備份與還原文章以及另兩篇網路教學 :

# 升級 Win10 並建立系統映像檔
# [教學]買Windows 10 新筆電後的第一件事- 建立系統映像檔與系統修護
# [教學]Windows 10 之系統復原實作(透過系統映像檔之系統救援)

2019-08-02 補充 :

今天再次詢問宏碁客服, 若電腦無法正常進入 Win10, 必須使用修復隨身碟時該怎麼做, 他說只要將修復隨身碟插入 USB 開機時按 F12, 會出現一張選單選擇開機來源為 USB 即可. 總之, 開機時按 F2 進 BIOS, 按 F12 則顯示開機選單.

2019年7月21日 星期日

追蹤項目

以下是我長期追蹤版本變化的程式語言或網站 :
  1. Python (最新 v3.7.4)
  2. Julia language (最新 v1.1)
  3. MicroPython (最新 v1.11)
  4. R langualge (最新 v3.6.1)
  5. Tiobe Index (程式語言排行)
Julia 與 MicroPython 都是很新且快速發展的語言 (韌體), 要經常關注.

臨時追蹤 : 此書有時會有降價優惠 :

# MicroPython Programming with ESP32 and ESP8266 eBook
# http://webpac.ksml.edu.tw/bookDetail.do?id=1605639&Lflag=1

2020-02-12 補充:
  1. Python (最新 v3.8.1)
  2. Julia language (最新 v1.3)
  3. MicroPython (最新 v1.12)
  4. R langualge (最新 v3.6.3)
  5. Tiobe Index (程式語言排行)
哇, Julia 都已經 1.3 了, 我都還沒時間學哩!

2020-05-26 補充 :
  1. Python (最新 v3.8.3)
  2. Julia language (最新 v1.4.2)
  3. MicroPython (最新 v1.12)
  4. R langualge (最新 v4.0.1)
  5. Tiobe Index (程式語言排行 Python #3: 9.12%)
注意, MicroPython on ESP8266/ESP32 的韌體下載網頁已更新, 往下拉到 Espressif 項下, 分別點 ESP8266 或 ESP32 進去點要下載的 bin.