2020年8月30日 星期日

2020 年第 35 周記事

本周大部分空閒時間在整理 jQuery 筆記, 因為去年底從市圖借來的幾本書也該清一清, 趕快還掉 (雖然半年來都沒人借~~~). 整理筆記很花時間很累, 但這是一個反芻的過程, 可以更透徹檢驗是否真的會用, 而且以後要複習也很快. 九月份要接新增的工作, jQuery 可以幫上一些忙.

阿蘭因尿路感染住院近兩周, 週四醫院通知可出院, 由機構接回, 費用約兩萬. 今年已是第三次, 之前醫生說過對於身心障礙人來說衰退比一般人快, 五十歲相當於老人了. 近期國泰業務員詢問月配息 3.8% 的儲蓄險, 考慮將到期定存轉購此商品, 用以支付部分機構照料費用.

為了避免一時忘記繳大樓管理費被列入欠繳名單, 我去年底改成預繳下個月, 但還是覺得每個月都要準備零錢很麻煩, 所以前天就將今年剩下的管理費都預繳了, 以後年底就預繳次年的全部管理費, 管委會有預收款好辦事, 而且我也省時省事, 畢竟時間比甚麼都寶貴.

上回向公司樓下擺攤的買的兩個藍芽音箱我覺得是很久的庫存貨, 開始用沒多久電池就沒電, 今天拆開機箱發現裡面的鋰電池居然沒編號 (盒裝標示電池容量 520mAh), 只好上露天隨便找個容量大一點又塞得下去的來換 :




本周看完韓劇便利店新星, 這部兼具搞笑與感人效果 (娛樂紓壓還不錯), 改編自網路漫畫 (韓劇越來越多這種), 崔大賢 (池昌旭飾) 最終辭掉總公司代表職務回來當加盟店店長, 丁新星 (金裕貞) 也從農場回來當店經理了, 我覺得崔大賢的網路漫畫家朋友韓達植應該就是作者自己吧!

下周菁菁開學, 我又要開始早睡早起了.

2020年8月29日 星期六

老師的退休餐會

前天接到玉蓉學姊訊息說鍾老師今年退休了, 阿正邀集以前高師大研究所同學歡送老師, 地點在巨蛋六樓喫茶趣, 還好是周六中午, 要是晚上我就沒辦法參加了. 上一次與老師見面應該是兩年前碩班同學南下高雄, 今天則大部分是博班學姐與學弟妹, 還有老師以前的助理也帶著女兒來了, 好久沒見面, 真的非常開心.

2020年8月28日 星期五

市圖還書 3 本

本周還書 3 本 :
  1. 7檔特別股養我一輩子 :Miss Q寫給退休族、定存族、小資族的私房三賺股
  2. 為你自己學Git
  3. 巨量資料的第一步 :R語言與商業應用 (總館 ONLY)
這本 R 語言與商業應用寫得不錯, 精簡又有料. Miss Q 這本特別股的書很紅, 我預約了好久才借到, 但我覺得特別股中大概中鋼特我比較中意 (有保證配息), 金融特別股都有期限.

2020年8月25日 星期二

已被廢棄的 jQuery 物件方法

最近在整理 jQuery 語法時注意到 bind() 事件綁定方法已被廢棄 (deprecated), 於是到 jQuery 官網查詢, 發現自 v1.3 版後還有蠻多選擇器, 物件方法, 以及工具函數 (頂層命名空間的 utilities) 陸續被廢棄, 例如 :

選擇器 :
  • :first
  • :last 
  • :eq() 
  • :lt() 
  • :gt() 
  • :even 
  • :odd 
物件方法 :
  • load()
  • unload()
  • toggle()
  • bind()
  • unbind()
  • delegate()
  • undelegate()
  • error()
  • size()
  • die()
  • live()
公用函數與屬性 :
  • jQuery.now()
  • jQuery.type()
  • jQuery.proxy()
  • jQuery.sub()
  • jQuery.holdReady()
  • jQuery.isNumeric()
  • jQuery.isWindow()
  • jQuery.isFunction
  • jQuery.isArray
  • jQuery.unique()
  • jQuery.parseJSON()
  • jQuery.fx.interval
  • jQuery.support
  • jQuery.browser
  • jQuery.boxModel
這些屬性, 函數與方法在最新版 v3.5.1 都不要再用了 (若只是廢棄但未移除則最新版還可以用). 不過我覺得其中有些工具函數例如 jQuery.trim(), jQuery.parseJSON() 等還蠻好用的, 不知為何要廢棄.

參考 :

https://api.jquery.com/category/deprecated/
https://api.jquery.com/category/removed/

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年8月23日 星期日

2020 年第 34 周記事

週五水某與菁菁作自強號上台北找姊姊 (安心旅遊專案), 雖然巴威颱風來襲, 但只到花東, 還好只是帶來南部的雨水.

暑假已近尾聲, 二哥的打工生涯也即將在 8/26 結束, 下班要直接回高雄, 因為週三要赴座談會提出報告. 本來是應徵課輔, 後來被建議改為企劃, 其實就是總務啦, 包括點貨, 搬米, 裝箱, 招待, 維持學生秩序 ... 相信近兩個月下來體驗應該很多, 收穫是得到自己的第一個資產 ~~~ 電動機車. 月底菁菁開學我又要開始早起的生活了.

過去一周為了工作上的需要回頭改寫 jQuery UI 為基礎的作業自動化系統, 所以 R 語言暫停, 希望用兩周的時間把這搞定, 順便把 jQuery 的書整理完畢還圖書館, 因為母校借的書 R 語言書籍有兩本被 recall, 還有三周要還, 所以弄完 jQuery 要努力看 R 的書了.

阿蘭仍住院中, 因護理師通知我安素已快用完, 故週五去永茂買了一箱 (1050 元) 提前於晚上回鄉下順路拿去, 聽護理師說一天約 4~6 罐, 下周四若出院就還夠, 但不確定何時可出院, 故今早又去鎮上藥局買一箱 (1350 元), 下午 13:30 探病截止前送過去.

表弟安安的小孩帶回家了, 今日滿 15 天, 傳來的照片看來頭好壯壯, 據小舅說第一天兩夫妻不知如何為寶寶洗澡, 故小舅媽專程回去示範. 想到我家三隻小狐狸回家第一天開始都是我在幫她們洗澡, 覺得自己還是非常自豪.

上週在 Line TV 看完韓劇 "365:逆轉命運的一年", 劇情改編自日本推理小說, 描述警官池亨柱因為前輩朴善浩被出獄的嫌犯殺害而灰心失志, 半年後接到一位精神科醫師李信的電話邀約, 說她找到一個回到過去的方法 (但只能到一年前), 池亨柱為了回到一年前改變歷史便答應參加了. 但一起回到一年前的團員卻陸續被殺害, 誰是幕後的黑手呢? 池亨柱與也是團員的漫畫家申佳賢 (南志鉉飾) 在死亡威脅中一步步揭開內幕.

https://www.linetv.tw/drama/11420/eps/1 (Line TV)

目前正在愛奇藝看的 "優雅的朋友們" 今天也將播出最後一集, 好期待.

市圖還書 1 本

本周市圖還書 1 本 :
  1. Make :國際中文版 .28 .無人機革命
馥林出的 Make 系列是 Maker 靈感的來源, 此書除了無人機外, 還介紹了其他許多有趣的自作項目, 例如自製電動坦克車等等.

2020年8月22日 星期六

jQuery UI SPA 應用程式 (五) : 字串處理函式庫

今天進入 jQuery UI 應用程式重頭戲 : 字串處理函式庫, 因為從遠端機台擷取到的純文字資料有其特定格式, 必須利用文字處理函數進行清洗, 再餵給 DataTables 表格呈現與篩選, 或者用來產生要下給遠端機台的指令, 所以字串處理可說是整個自動化專案中的關鍵工具.

這個函式庫的來源有兩個, 一是以前用 PHP 寫爬蟲程式時找到的一本好書 :

# 網路機器人, 網路蜘蛛與網路爬蟲-PHP/CURL 程式設計指南 (第二版, 碁峰出版)


Source : 金石堂


其範例程式下載網址如下 :

http://webbotsspidersscreenscrapers.com/
http://webbotsspidersscreenscrapers.com/DSP_download.php

我參考了其中的字串剖析函式庫 LIB_parse.php 進行改寫, 裡面的兩個函數 return_between() 與 parse_array() 是此函式庫的主角, 也是進行文本剖析的利器. 另外一個參考來源是網路上的 Python 字串處理函式庫:

http://docs.python.org/release/2.5.2/lib/string-methods.html

我將這些 PHP 與 Python 字串處理函式改寫為如下之 Javascript 版, 每一個函數都指派為 String 物件的 prototype 屬性以擴充 String 物件的功能, 這樣用法就與 Javascript 字串物件的函數完全一樣了 :

//parse.js
String.prototype.split_string=split_string;
String.prototype.return_between=return_between;
String.prototype.parse_array=parse_array;
String.prototype.remove=remove;
String.prototype.get_attribute=get_attribute;
String.prototype.trim=trim;
String.prototype.ltrim=ltrim;
String.prototype.rtrim=rtrim;
String.prototype.isdigit=isdigit;
String.prototype.startswith=startswith;
String.prototype.endswith=endswith;
String.prototype.title=title;
String.prototype.swapcase=swapcase;
String.prototype.zfill=zfill;
String.prototype.ljust=ljust;
String.prototype.rjust=rjust;
String.prototype.encode=encode;
String.prototype.decode=decode;
/*-----------------------------------------------------------------------------
split_string(delineator, desired, type)
功能 :
  此函數會在一個待剖析字串中搜尋指定之界定字串 delineator, 依據所需要的位置
  desired, 傳回在該界定字串之前或之後的子字串, 型態 type 用來設定傳回值中是否
  要包含界定字串. 此函數所處理之字串與大小寫無關 (包括參數).
參數 :                                   
  delineator : 界定字串 
  desired    : "before" : 傳回界定字串前之子字串
               "after"  : 傳回界定字串後之子字串     
  type       : "incl"   : 包含界定字串 
               "excl"   : 不包含界定字串
-----------------------------------------------------------------------------*/
function split_string(delineator, desired, type) {
  //處理預設值
  var desired = String(desired).toLowerCase() || "before";
  var type = String(type).toLowerCase() || "excl";
  //為與大小寫無關, 全部轉為小寫處理
  var lc_str = String(this).toLowerCase();
  var marker = delineator.toLowerCase();
  if (lc_str.indexOf(marker, 0) == -1) { //沒找到分割字串:傳回 ""
     return "";
     }
  else { //有找到分割字串
     if (desired == "before") {  //傳回分割字串前的部分
        if (type == "excl") { //不包含分割字串:不必加分割字串本身     
           var split_here = lc_str.indexOf(marker, 0);
           }
        else { //包含分割字串:要加計分割字串本身
           var split_here = lc_str.indexOf(marker, 0) + marker.length;
           } 
        var parsed_string = this.substring(0, split_here);
        }
     else { //傳回界定字串後的部分 "after"
        if (type == "excl") { //不包含分割字串:要扣除分割字串本身
           var split_here = lc_str.indexOf(marker, 0) + marker.length;
           }
        else { //包含分割字串:要加計分割字串本身
           var split_here = lc_str.indexOf(marker, 0);
           }     
        var parsed_string = this.substring(split_here, lc_str.length + 1);
        }
     }
  return parsed_string;
  }
/*-----------------------------------------------------------------------------
return_between(start, stop, type)
功能 :
  此函數會在待剖析字串字串中搜尋指定之起始字串 start 與結束字串 stop, 傳回在該組
  界定字串之間的子字串, 型態 type 用來設定傳回值中是否要包含組界定字串.
  此函數所處理之字串與大小寫無關 (包括參數).
參數 :                                 
  start : 起始字串 
  stop  : 結束字串     
  type  : "incl"   : 包含界定字串 
          "excl"   : 不包含界定字串
-----------------------------------------------------------------------------*/
function return_between(start, stop, type) {
  //傳回起始字串後面的部分
  var temp = this.split_string(start, "AFTER", type);
  //傳回結束字串前面的部分
  return temp.split_string(stop, "BEFORE", type);
  }
/*-----------------------------------------------------------------------------
parse_array(start, stop)
功能 :
  此函數會在待剖析字串字串中重複搜尋指定之起始字串 start 與結束字串 stop 間的
  子字串, 並把每次符合的子字串放入陣列中傳回. 傳回值為陣列, 元素中包含界定字串.
  此函數所處理之字串與大小寫無關 (包括參數).
參數 :                                 
  start : 起始字串 
  stop  : 結束字串
-----------------------------------------------------------------------------*/
function parse_array(start, stop) {
  var reg=new RegExp("(" + start + "([\\s\\S]*?)" + stop + ")","ig");
  return this.match(reg);  //傳回符合之陣列
  }
/*-----------------------------------------------------------------------------
remove(start, stop)
功能 :
  此函數會在待剖析字串中重複搜尋指定之起始字串 start 與結束字串 stop 間的
  子字串, 並把每次符合的子字串從待剖析字串中刪除. 傳回值為刪除後之字串.
  注意, 刪除子字串時會連同界定字串一起刪除.
  此函數所處理之字串與大小寫無關 (包括參數).
參數 :                                 
  start : 起始字串 
  stop  : 結束字串
-----------------------------------------------------------------------------*/
function remove(start, stop) {
  var remove_array = this.parse_array(start, stop);  //傳回要移除的子字串陣列
  var string = this;
  if (remove_array != null) { //有找到子字串:執行刪除
    for (var i = 0; i < remove_array.length; i++) {    //依序移除
      var reg = new RegExp(remove_array[i],"ig");    //要刪除之子字串 reg 物件
      var string = string.replace(reg, "");
      }
    return string;
    }
  else {return this;}  //沒有找到子字串:傳回待剖析字串本身
  }
/*-----------------------------------------------------------------------------
get_attribute(attribute)
功能 :
  此函數會在待剖析 HTML 字串中搜尋指定之屬性值.
參數 :       
  attribute : HTML 元素之屬性, 例如 style, class 等
-----------------------------------------------------------------------------*/
function get_attribute(attribute) {
  var cleaned_html = this.tidy_html();             //整理 tag 元素
  cleaned_html = cleaned_html.replace("\r", "");   //去除 CR
  cleaned_html = cleaned_html.replace("\n", "");   //去除 LF
  var start=attribute.toLowerCase() + "=\"";       //屬性開頭 (全小寫)
  return cleaned_html.return_between(start, "\"", "EXCL");   //回傳屬性值
  }
//=====以下函數來自 VBscript=====//
/*-----------------------------------------------------------------------------
trim()
  此函數會刪除字串開頭與結尾處之空白字元後傳回 (與 VBscript 之 Trim 同).
  空白字元, 包括空格(space),水平定位(tab),跳頁(form-feed)與換列(linefeed),
  相當於 [ \f\n\r\t\v\u00A0\u2028\u2029]
參數 :                                   
  無
-----------------------------------------------------------------------------*/
function trim() {
  return this.replace(/(^\s*)|(\s*$)/g,"");
  }
/*-----------------------------------------------------------------------------
ltrim()
  此函數會刪除字串開頭處之空白字元後傳回 (與 VBscript 之 Ltrim 同).
參數 :                                   
  無
-----------------------------------------------------------------------------*/
function ltrim() {
  return this.replace(/(^\s*)/g, "");
  }
/*-----------------------------------------------------------------------------
rtrim()
  此函數會刪除字串結尾處之空白字元後傳回 (與 VBscript 之 Rtrim 同).
參數 :                                   
  無
-----------------------------------------------------------------------------*/
function rtrim() {
  return this.replace(/(\s*$)/g, "");
  }
//======以下函數改寫自 Python=====//
//參考 : http://docs.python.org/release/2.5.2/lib/string-methods.html
/*-----------------------------------------------------------------------------
isdigit()
  此函數會判斷字串內容是否全為數字 (與 Python 之 isdigit 同).
參數 :                                   
  無
-----------------------------------------------------------------------------*/
function isdigit() {
  return str.match(/D/)==null;
  }
/*-----------------------------------------------------------------------------
startswith(prefix[,start[, end]])
  此函數會判斷字串內容是否以 prefix 開頭 (與 Python 之 startwith 同).
  備選參數 start 與 end 可指定比對之起訖位置, 預設為從頭 (0) 比到尾 (length-1).
參數 :                                   
  無
-----------------------------------------------------------------------------*/
function startswith(prefix, start, end) {
  var start=start || 0;          //預設值
  var end=end || this.length-1;  //預設值
  var haystack=this.substring(start,end+1);
  var needle=new RegExp("^" + prefix,"ig");  //是否以 prefix 結尾 (不分大小寫)
  WScript.Echo(haystack);
  return needle.test(haystack);
  }
/*-----------------------------------------------------------------------------
endswith(prefix[,start[, end]])
  此函數會判斷字串內容是否以 prefix 開頭 (與 Python 之 startwith 同).
  備選參數 start 與 end 可指定比對之起訖位置, 預設為從頭 (0) 比到尾 (length-1).
參數 :                                   
  無
-----------------------------------------------------------------------------*/
function endswith(prefix, start, end) {
  var start=start || 0;          //預設值
  var end=end || this.length-1;  //預設值
  var haystack=this.substring(start,end+1);
  var needle=new RegExp(prefix + "$","ig");  //是否以 prefix 結尾 (不分大小寫)
  WScript.Echo(haystack);
  return needle.test(haystack);
  }
/*-----------------------------------------------------------------------------
title()
  此函數會將字串 (英文) 中每一個字的第一個字元改成大寫, 其餘字元改為小寫後傳回
  (與 Python 之 title 同).
參數 :                                   
  無
-----------------------------------------------------------------------------*/
function title() {
  var words=this.replace(/[ ]{2,}/g," ").split(" ");  //兩個以上連續空格改為1個
  for (var i=0; i<words.length; i++) {
    var first=words[i].charAt(0).toUpperCase();  //每字首字元大寫
    var others=words[i].substr(1).toLowerCase();  //第2字元至最後小寫
    words[i]=first + others;  //重組回字
    }
  return words.join(" ");  //重組回字串傳回
  };
/*-----------------------------------------------------------------------------
swapcase(prefix[,start[, end]])
  此函數會將字串 (英文) 中每一個小寫字元改成大寫, 大寫字元改成小寫後傳回
  (與 Python 之 swapcase 同).
參數 :                                   
  無
-----------------------------------------------------------------------------*/
function swapcase() {
  var arr=[];  //暫存字元用
  var lower=/[a-z]/;   //小寫字元
  var upper=/[A-Z]/;  //大寫字元
  for (var i=0; i<this.length; i++) {  //走訪每一個字元
    if (lower.test(this.charAt(i))) {  //小寫字母->改成大寫
      arr.push(this.charAt(i).toUpperCase());
      }
    else if (upper.test(this.charAt(i))) {  //大寫字母->改成小寫
      arr.push(this.charAt(i).toLowerCase());
      }
    else {arr.push(this.charAt(i));}
    }
  return arr.join(""); //串接全部字元
  };
/*-----------------------------------------------------------------------------
zfill(width)
  此函數會將數字字串前面填滿 0 使得總長為 width 字元 (與 Python 之 isalnum 同).
  若字串長度小於 width, 則無法填 0 而傳回字串本身.
參數 :                                   
  width : 填補之後字串總長度
傳回值 :
  字串
-----------------------------------------------------------------------------*/
function zfill(width) {
  var zeros="";  //補0字串初始值
  if (width < this.length) {return this;} //width小於字串長度:不用補,傳回本身
  else {  //需要補滿 0
     for (var i=0; i<width-this.length; i++) {zeros += "0";}  //製作補0字串
     return zeros + this;  //前面冠上補0字串
     }
  }
/*-----------------------------------------------------------------------------
ljust(width[, fillchar])
  此函數會在字串前面以 fillchar 字元填滿使得總長為 width 字元 (與 Python 之
  ljust 同). 若字串長度小於 width, 則無法填滿而傳回字串本身. fillchar 預設為
  空格 space=" ".
參數 :                                   
  width    : 填補之後字串總長度
  fillchar : 填補字元
傳回值 :
  字串
-----------------------------------------------------------------------------*/
function ljust(width, fillchar) {
  var fillchar=fillchar || " ";  //填補字元預設值為 " "
  var filler="";                 //填補字串初始值
  if (width < this.length) {return this;} //width小於字串長度:不用補,傳回本身
  else {  //需要補滿
     for (var i=0; i<width-this.length; i++) {filler += fillchar;} //填補字串
     return filler + this;  //前面冠上填補字串
     }
  }
/*-----------------------------------------------------------------------------
rjust(width[, fillchar])
  此函數會在字串後面以 fillchar 字元填滿使得總長為 width 字元 (與 Python 之
  ljust 同). 若字串長度小於 width, 則無法填滿而傳回字串本身. fillchar 預設為
  空格 space=" ".
參數 :                                   
  width    : 填補之後字串總長度
  fillchar : 填補字元
傳回值 :
  字串
-----------------------------------------------------------------------------*/
function rjust(width, fillchar) {
  var fillchar=fillchar || " ";  //填補字元預設值為 " "
  var filler="";                 //填補字串初始值
  if (width < this.length) {return this;} //width小於字串長度:不用補,傳回本身
  else {  //需要補滿
     for (var i=0; i<width-this.length; i++) {filler += fillchar;} //填補字串
     return this + filler;  //後面冠上填補字串
     }
  }
/*-----------------------------------------------------------------------------
encode(key)
  此函數會依據傳入之 key 對字串進行特定編碼. 其相對之解碼函式為 decode(), 必須
  傳入相同 key 才會解出原始字串.
參數 :                                   
  key : 編碼所用之鍵字串.
傳回值 :
  由 key 字串中之字元編碼所組成之加密字串
源碼 : http://www.360doc.com/content/13/0806/13/1073512_305112816.shtml
-----------------------------------------------------------------------------*/
function encode(key) {
  if (!key) {
    var key="8ABC7DLO5MN6Z9EFGdeJfghijkHIVrstuvwWSTUXYabclmnopqKPQRxyz01234";
    }
  var nl=this.length;
  var t=[];
  var a,b,c,x,m=function(y){t[t.length]=key.charAt(y)};
  var N=key.length;
  var N2=N*N,N5=N*5;
  for (x=0;x<nl;x++) {
    a=this.charCodeAt(x);
    if (a<N5) m(Math.floor(a/N)),m(a%N);
    else m(Math.floor(a/N2)+5),m(Math.floor(a/N)%N),m(a%N);
    }
  var s=t.join("");
  return String(s.length).length+String(s.length)+s;
  };
/*-----------------------------------------------------------------------------
decode(key)
  此函數會依據傳入之 key 對字串進行特定解碼. 其相對之編碼函式為 encode(), 必須
  傳入編碼所用之相同 key 才會解出原始字串.
參數 :                                   
  key : 解碼所用之鍵字串.
傳回值 :
  由 key 字串中之字元解碼所組成之還原字串
源碼 : http://www.360doc.com/content/13/0806/13/1073512_305112816.shtml
-----------------------------------------------------------------------------*/
function decode(key) {
  if (!key) {
    var key="8ABC7DLO5MN6Z9EFGdeJfghijkHIVrstuvwWSTUXYabclmnopqKPQRxyz01234";
    }
  var c=this.charAt(0)*1;
  if (isNaN(c)) return "";
  c=this.substr(1,c)*1;
  if (isNaN(c)) return "";
  var nl=this.length;
  var t=[];
  var a,f,b,x=String(c).length+1;
  var enc=this;
  var m=function(y){return key.indexOf(enc.charAt(y))};
  var N=key.length;
  if (nl!=x+c) return "";
  while (x<nl) {
    a=m(x++)
    if (a<5)f=a*N+m(x);
    else f=(a-5)*N*N+m(x)*N+m(x+=1);
    t[t.length]=String.fromCharCode(f);
    x++;
    }
  return t.join("");
  };

其中最重要的函數是 return_between(start, end, type), 它使用正規表達式來取得目標字串, 第一個參數 start 是要擷取之資料前面之起始標記; 第二個參數 end 為結束標記, type 用來指定傳回結果是否要包含起始與結束標記, "incl" 為包含, "excl" 為不包含.

將此函式庫放在專案的 /lib 資料夾下 :




下面是這些函數的測試範例 :


範例 10 : 用 return_between() 擷取子字串

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery UI Single Page Application</title>
  <script src="jquery/jquery.js"></script>
  <script src="jquery/jquery-ui.js"></script>
  <script src="jquery/datatables.js"></script>
  <script src="lib/file.js"></script>
  <script src="lib/parse.js"></script>
  <link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <link href="jquery/datatables.css" rel="stylesheet">
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      font-size:10px;
      }
  </style>
</head>
<body>
  <!-- 頁嵌面板 -->
  <div id="tabs">
    <ul>
      <li><a href="#tabs-1">tab1</a></li>
      <li><a href="#tabs-2">tab2</a></li>
      <li><a href="#tabs-3">tab3</a></li>
    </ul>
    <div id="tabs-1">
      <div id="data" class="ui-state-highlight ui-corner-all" style="margin-top: 10px; margin-bottom: 10px;padding: 5px;">align="center" height="25"</div>
      <button id="get_align" class="ui-button ui-widget ui-corner-all">擷取 align</button>
      <button id="get_height" class="ui-button ui-widget ui-corner-all">擷取 height</button>
      <script>
        $(function(){
          //監聽按鈕之 click 事件
          $("#get_align").click(function(e) {
            e.preventDefault();
            var data=$("#data").html();
            var str=data.return_between('align="', '"', 'excl');
            $("#msgbox").html(str);
            $("#msgbox").dialog("open");
            });
          //監聽按鈕之 click 事件
          $("#get_height").click(function(e) {
            e.preventDefault();
            var data=$("#data").html();
            var str=data.return_between('height="', '"', 'excl');
            $("#msgbox").html(str);
            $("#msgbox").dialog("open");
            });
          });
      </script>
    </div>
    <div id="tabs-2">
    </div>
    <div id="tabs-3">
    </div>
  </div>
  <script>
    $(function(){
      $("#tabs" ).tabs();
      }); 
  </script>
  <!-- 訊息盒 -->
  <div id="msgbox" title="訊息"></div>
  <script>
    $(function(){
      //建立訊息盒
      $("#msgbox").dialog({
        title: "訊息",
        autoOpen: false,
        buttons: {
          "確定": function() {$(this).dialog("close");}
          }
        });
      });
  </script>
</body>
</html>

此網頁在 id=data 的 div 元素中放置字串內容 align="center" height="25", 按下按鈕後會呼叫 return_between() 分別擷取 align 與 height 之屬性值, 結果如下 : 










可見 return_between() 可以擷取起始與結束標記之間的子字串.

下面的範例則是測試 parse_array() 函數, 此函數會將字串中多組由起始與結束標記包起來子字串都擷取出來, 放在陣列中傳回 :


範例 11 : 用 parse_array() 擷取子字串陣列

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery UI Single Page Application</title>
  <script src="jquery/jquery.js"></script>
  <script src="jquery/jquery-ui.js"></script>
  <script src="jquery/datatables.js"></script>
  <script src="lib/file.js"></script>
  <script src="lib/parse.js"></script>
  <link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <link href="jquery/datatables.css" rel="stylesheet">
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      font-size:10px;
      }
  </style>
</head>
<body>
  <!-- 頁嵌面板 -->
  <div id="tabs">
    <ul>
      <li><a href="#tabs-1">tab1</a></li>
      <li><a href="#tabs-2">tab2</a></li>
      <li><a href="#tabs-3">tab3</a></li>
    </ul>
    <div id="tabs-1">
      <input id="data" style="width: 100%; margin-top: 10px; margin-bottom: 10px" value="<td>1</td><td>2</td><td>3</td><td>4</td><td>5</td>"><br>
      <input id="out" style="width: 100%; margin-top: 10px; margin-bottom: 10px"<br>
      <button id="get_cell_content" class="ui-button ui-widget ui-corner-all">擷取儲存格中的內容</button>
      <script>
        $(function(){
          //監聽按鈕之 click 事件
          $("#get_cell_content").click(function(e) {
            var data=$("#data").val();
            var arr=data.parse_array('<td>', '</td>');
            $("#out").val(arr.join());
            });
          });
      </script>
    </div>
    <div id="tabs-2">
    </div>
    <div id="tabs-3">
    </div>
  </div>
  <script>
    $(function(){
      $("#tabs" ).tabs();
      }); 
  </script>
</body>
</html>

此例中有兩個 input 文字欄位, id=data 的欄位存放待剖析之字串; 而 id=out 的欄位則用來顯示 parse_array() 傳回來的陣列 (用 join 方法轉成字串), 結果如下 :




可見 parse_array() 在擷取子字串時不會刪除起始與結束標記, 這可以呼叫 replace() 方法處理.

Win10 的全形半形切換 (Shift + Space)

今天又遇到一個長久以來使用 Win10 常遇到的問題, 就是在飛快打字時突然變成全形模式, 輸入的英文字變成粗體且間隔變大的字, 不知道是錯按了甚麼鍵造成, 只好在輸入英文時點右下角語言按鈕, 將中文改為英文, 切來切去很不方便. 今天又遇到了, 就上網查了一下, 原來是誤按了 Shift + Space 所致, 只要再按一次就會切換回半形了, 參考 :

Win10教學︰win10切換全形半形教學

2020年8月21日 星期五

jQuery UI SPA 應用程式 (四) : 檔案處理函式庫

今天繼續幹活, 目標是整理很久之前寫好的 Javascript 函式庫, 我將其中伺服端部分的程式碼刪除以便用在本次專案上. 由於資安限制不能安裝與使用資料庫, 只好用檔案處理方式來協助作業自動化, 因此檔案處理函式庫在整個專案中扮演關鍵角色.

刪減後的函式庫檔案 file.js 如下所示, 此函式庫使用微軟 ActiveX 技術, 因此只能在 IE 瀏覽器上運作 : 


1. 匯入函數 :   

此函數用來讀取 js 程式 (函式) 並執行其中的頂層程式碼.

/*-----------------------------------------------------------------------------
include(filename)
功能 : 
  此函數用來匯入其他外部 Javascript 檔. 僅限於 Client 端之 WSH 應用中.
參數 :                                   
  filename  : 外部 Javascript 檔名 (字串)   
傳回值 :
  無/null
範例 : 
  include("parse.js");    
-----------------------------------------------------------------------------*/
function include(filename) {
  try {
       var FSO=new ActiveXObject ("Scripting.FileSystemObject");
       var TXO=FSO.openTextFile(filename,1);
       var fileData=TXO.readAll();
       eval(fileData);  //執行 JS 檔內容
       TXO.Close();
       TXO = null;
       FSO = null;
      }
  catch (e) {return e.description;}
  }


2. 讀取文字檔案 :     

函式 read_file() 用來讀取文字檔, 傳回整個檔案內容 (字串); read_lines() 則逐行讀取文字檔, 傳回值為檔案中各行依序組成之陣列.

/*-----------------------------------------------------------------------------
read_file(filename)
功能 : 
  此函數用來讀取外部文字檔, 結果以字串傳回.
參數 :                                   
  filename  : 文字檔檔名 (含路徑)   
傳回值 :
  成功傳回檔案內容字串, 失敗傳回原因字串
範例 : 
  var fileData=read_file("test.log");    
-----------------------------------------------------------------------------*/
function read_file(filename) {
  try {
      var FSO=new ActiveXObject("Scripting.FileSystemObject");
      var TXO=FSO.openTextFile(filename,1,false);
      var fileData=TXO.readAll();
      TXO.Close();
      TXO=null;
      FSO=null;
      return fileData;
      }
  catch (e) {return e.description;}
  }
/*-----------------------------------------------------------------------------
read_lines(filename)
功能 : 
  此函數用來讀取外部文字檔, 並以分行符號拆分每列後儲存在陣列中傳回.
參數 :                                   
  filename  : 文字檔檔名 (含路徑)   
傳回值 :
  成功傳回陣列, 失敗傳回原因
範例 : 
  var array_lines=read_lines("test.log");    
-----------------------------------------------------------------------------*/
function read_lines(filename) {
  try {
      var FSO=new ActiveXObject("Scripting.FileSystemObject");
      var TXO=FSO.openTextFile(filename,1,false);
      var array_lines=new Array();
      while (!TXO.atEndOfStream) {
            array_lines.push(TXO.ReadLine());  //讀取每行放入陣列中
            }
      TXO.Close();
      TXO=null;
      FSO=null;
      return array_lines;
      }
  catch (e) {return e.description;}
  }


3. 寫入文字檔 :  

函式 write_file() 將字串以覆蓋或新建方式寫入外部文字檔; append() 是將字串貼附到檔尾; 而prepend() 則是將字串貼附到檔頭. 

/*-----------------------------------------------------------------------------
write_file(filename,text)
功能 : 
  此函數用來將純文字寫入外部文字檔 (覆蓋或新建).
參數 :                                   
  filename  : 文字檔檔名 (含路徑)   
  text      : 檔案內容   
傳回值 :
  成功傳回 1, 失敗傳回原因
範例 : 
  var result=write_file("test.log", "This is a book");    
-----------------------------------------------------------------------------*/  
function write_file(filename,text) {
  try {
      var FSO=new ActiveXObject("Scripting.FileSystemObject");
      var TXO=FSO.openTextFile (filename,2,true); //2=寫入,true=新建
      TXO.Write(text); //寫入檔案
      TXO.Close();
      FSO=null;
      return 1;
      }
  catch(e) {return e.description;}
  }
/*-----------------------------------------------------------------------------
append(filename,text)
功能 : 
  此函數用來將純文字寫入外部文字檔後面(不存在就新建).
參數 :                                   
  filename  : 文字檔檔名 (含路徑)   
  text      : 檔案內容   
傳回值 :
  成功傳回 1, 失敗傳回原因字串
範例 : 
  var result=append("test.log", "This is a book");    
-----------------------------------------------------------------------------*/  
function append(filename,text) {
  try {
      var FSO=new ActiveXObject("Scripting.FileSystemObject");
      var TXO=FSO.openTextFile(filename,8,true); //8=附加,true=新建
      TXO.Write(text); //寫入檔案
      TXO.Close();
      FSO=null;
      return 1;    
      }
  catch(e) {return e.description;}
  }
/*-----------------------------------------------------------------------------
prepend(filename,text)
功能 : 
  此函數用來將純文字寫入外部文字檔前面(不存在就新建).
參數 :                                   
  filename  : 文字檔檔名 (含路徑)   
  text      : 檔案內容   
傳回值 :
  成功傳回 1, 失敗傳回原因字串
範例 : 
  var result=prepend("test.log", "This is a book");    
-----------------------------------------------------------------------------*/  
function prepend(filename,text) {
  var new_text=text + read_file(filename);  //串接
  return write_file(filename,new_text); 
  }


4. 刪除文字檔部分內容 : 

函式 remove_lines() 會刪除文字檔中指定之行數 (0 起始); remove_prelines() 用來刪除文字檔指定之前面幾行; 而 emove_lastlines() 則刪除文字檔指定之最後幾行. 

/*-----------------------------------------------------------------------------
remove_lines(filename,lines_array)
功能 : 
  此函數用來移除文字檔指定之行.
參數 :                                   
  filename     : 文字檔檔名 (含路徑)   
  lines_array  : 要移除之行數陣列 (0 起算)  
傳回值 :
  成功傳回 1, 失敗傳回原因字串
範例 : 
  var result=remove_lines("test.log", [0]);    
  var result=remove_lines("test.log", [2,4,6]);    
-----------------------------------------------------------------------------*/  
function remove_lines(filename,lines_array) {
  var lines_array=lines_array.sort();  //行號排序,以便取出要刪的最後一行行數
  var text=read_lines(filename);  //讀取檔案成陣列
  var break_point=lines_array[lines_array.length-1] + 1;  //斷點
  var pre=text.slice(0, break_point); //前半部 (要處理刪除作業)
  //剩下的後半部陣列不須處理刪除作業
  var left=text.slice(break_point); //選取斷點至陣列尾
  var tmp=new Array();
  var line_str=lines_array.join();  //把欲刪除行號組成字串
  for (var i = 0; i < pre.length; i++) {  //沒在刪除名單的放入 tmp 陣列
      if (line_str.indexOf(i.toString()) == -1 ) {tmp.push(pre[i]);} //不刪除
      else {continue;}  //有在刪除名單的不存入 tmp : 刪除
      }
  tmp=tmp.concat(left);  //串接刪除後的前半部與後半部串接
  return write_file(filename,tmp.join("\r\n")); 
  }
/*-----------------------------------------------------------------------------
remove_prelines(filename,numbers)
功能 : 
  此函數用來移除文字檔最前面指定之行數.
參數 :                                   
  filename  : 文字檔檔名 (含路徑)   
  numbers   : 移除之行數   
傳回值 :
  成功傳回 1, 失敗傳回原因字串
範例 : 
  var result=remove_prelines("test.log", 2);    
-----------------------------------------------------------------------------*/  
function remove_prelines(filename,numbers) {
  var lines=read_lines(filename);  //
  for (var i = 0; i < numbers; i++) {lines.shift();}
  return write_file(filename,lines.join("\r\n")); 
  }
/*-----------------------------------------------------------------------------
remove_lastlines(filename,numbers)
功能 : 
  此函數用來移除文字檔最後面指定之行數.
參數 :                                   
  filename  : 文字檔檔名 (含路徑)   
  numbers   : 移除之行數   
傳回值 :
  成功傳回 1, 失敗傳回原因字串
範例 : 
  var result=remove_lastlines("test.log", 2);    
-----------------------------------------------------------------------------*/  
function remove_lastlines(filename,numbers) {
  var lines=read_lines(filename);  //讀進檔案為陣列
  lines.reverse();  //陣列倒序
  for (var i = 0; i < numbers; i++) {lines.shift();}  //刪除前面幾列
  lines.reverse();  //陣列倒序回去
  return write_file(filename,lines.join("\r\n")); 
  }


5. 檔案目錄資訊 : 

函式 get_files() 會以陣列傳回指定目錄下之檔案列表; file_exist() 用來檢查指定之檔案是否存在 (存在傳回 1); delete_file() 用來刪除指定之檔案 (成功傳回 1), get_folder() 則傳回網頁檔所在之本機路徑. 

/*-----------------------------------------------------------------------------
get_files(folder)
功能 : 
  此函數用來取得指定目錄下的檔案列表, 以陣列傳回.
參數 :                                   
  folder  : 檔案路徑 (注意, 目錄需用兩個倒斜線分隔) 
傳回值 :
  陣列或失敗原因
範例 : 
  var files=get_files("my_doc");    
  var files=get_files("c:\\test\\my_doc");    
-----------------------------------------------------------------------------*/  
function get_files(folder) {
  try {
      var FSO=new ActiveXObject("Scripting.FileSystemObject");
      var folderObj=FSO.GetFolder(folder); 
      var enumObj=new Enumerator(folderObj.Files); 
      var filesArray=new Array();
      for (;!enumObj.atEnd();enumObj.moveNext()) {      
          filesArray.push(enumObj.item().Name);
          }
      folderObj=null;
      enumObj=null;
      FSO=null;
      return filesArray;   
      }
  catch(e) {return e.description;}
  }
/*-----------------------------------------------------------------------------
file_exist(file_name)
功能 : 
  此函數用來檢查檔案是否存在.
參數 :                                   
  file_name : 檔案名稱與路徑 (注意, 目錄需用兩個倒斜線分隔) 
傳回值 :
  成功傳回 1, 失敗傳回原因
範例 :    
  var result=file_exist("c:\\test\\my_doc.txt");    
-----------------------------------------------------------------------------*/  
function file_exist(file_name) {
  try {
      var FSO=new ActiveXObject("Scripting.FileSystemObject");
      if (FSO.FileExists(file_name)) {return 1;}
      else {return 0;}
      FSO=null;    
      }
  catch(e) {return e.description;}
  }
/*-----------------------------------------------------------------------------
delete_file(file_name)
功能 : 
  此函數用來刪除檔案.
參數 :                                   
  file_name : 檔案名稱與路徑 (注意, 目錄需用兩個倒斜線分隔) 
傳回值 :
  成功傳回 1, 失敗傳回原因
範例 :    
  var result=delete_file("c:\\test\\my_doc.txt");    
-----------------------------------------------------------------------------*/  
function delete_file(file_name) {
  try {
      var FSO=new ActiveXObject("Scripting.FileSystemObject");
      if (FSO.FileExists(file_name)) {FSO.DeleteFile(file_name);}
      FSO=null;
      return 1;
      }
  catch(e) {return e.description;}
  }
/*-----------------------------------------------------------------------------
get_folder()
功能 : 
  此函數會傳回網頁檔所在本機路徑, 例如 D:\\project\\pitch\\
參數 :  
  無
傳回值 : 字串 (雙倒斜線格式 D:\\project\\pitch\\)
-----------------------------------------------------------------------------*/
function get_folder() { //傳回目前網頁所在之目錄字串
  //取得目前網頁所在目錄,例如 /D:\project\pitch\pitch.htm
  var browser=window.navigator.appName;
  var b_version=window.navigator.appVersion;
  var version=b_version.split(";"); 
  var trim_Version=version[1].replace(/[ ]/g,""); 
  //取得目前網頁所在目錄,/D:/project/pitch/pitch.htm
  var sys_path=unescape(window.location.pathname);
  //去除檔名,從右邊開始找第一個右斜線 / 索引
  var pos=sys_path.lastIndexOf("\/"); 
  //須去除第一個右斜線,故從 1 開始直到右側第一個右斜線(含)
  var path=sys_path.substring(1,pos+1); 
  //工作目錄路徑演算結果例如 path=D:/project/pitch/
  //製作 FSO 物件運作所需之系統路徑字串 (由單一倒斜線轉成兩個倒斜線)
  var path_arr=path.split("\/"); //先拆成陣列
  var folder=path_arr.join("\\\\"); //再組合為字串 D:\\project\\pitch\\
  return folder;
  }

先在專案根目錄下新增一個 lib 資料夾, 將此 file.js 函式庫檔案存放於 /lib 下 :




完整的 files.js 參考 GitHub :

https://github.com/tony1966/test/blob/master/jqueryui-app/lib/file.js

下面逐一測試這些函數的用法. 



<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery UI Single Page Application</title>
  <script src="jquery/jquery.js"></script>
  <script src="jquery/jquery-ui.js"></script>
  <script src="jquery/datatables.js"></script>
  <script src="lib/file.js"></script>
  <link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
  <link href="jquery/datatables.css" rel="stylesheet">
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      font-size:10px;
      }
  </style>
</head>
<body>
  <!-- 主題佈景選擇器 -->
  <select id="themes">
    <option value="base">base</option>
    <option value="black-tie">black-tie</option>
    <option value="blitzer">blitzer</option>
    <option value="cupertino">cupertino</option>
    <option value="dark-hive">dark-hive</option>
    <option value="dot-luv">dot-luv</option>
    <option value="eggplant">eggplant</option>
    <option value="excite-bike">excite-bike</option>
    <option value="flick">flick</option>
    <option value="hot-sneaks">hot-sneaks</option>
    <option value="humanity">humanity</option>
    <option value="le-frog">le-frog</option>
    <option value="mint-choc">mint-choc</option>
    <option value="overcast">overcast</option>
    <option value="pepper-grinder">pepper-grinder</option>
    <option value="redmond">redmond</option>
    <option value="smoothness">smoothness</option>
    <option value="south-street">south-street</option>
    <option value="start">start</option>
    <option value="sunny">sunny</option>
    <option value="swanky-purse">swanky-purse</option>
    <option value="trontastic">trontastic</option>
    <option value="ui-darkness">ui-darkness</option>
    <option value="ui-lightness">ui-lightness</option>
    <option value="vader">vader</option>
  </select>
  <script>
    $(function(){
      $("#themes").selectmenu();
      $("#themes").val("hot-sneaks");
      $("#themes").selectmenu("refresh");
      $('#themes').on('selectmenuchange', function() {
        var theme=$(this).val();
        var href="jquery/themes/" + theme + "/jquery-ui.min.css";
        $("#theme").attr("href", href);
        });
      });
  </script>
  <!-- 頁嵌面板 -->
  <div id="tabs">
    <ul>
      <li><a href="#tabs-1">tab1</a></li>
      <li><a href="#tabs-2">tab2</a></li>
      <li><a href="#tabs-3">tab3</a></li>
    </ul>
    <div id="tabs-1">
      <button id="read_file" class="ui-button ui-widget ui-corner-all">讀取檔案</button>
      <div id="out1" class="ui-state-highlight ui-corner-all" style="margin-top: 10px; padding: 5px;"></div>
      <script>
        $(function(){
          //監聽按鈕之 click 事件
          $("#read_file").click(function(e) {
            e.preventDefault();
            var folder=get_folder();     //取得此網頁的本機路徑
            var txt=read_file(folder + "hello-world.txt");      //讀取 .txt 檔
            var htm=read_file(folder + "hello-world.htm");   //讀取 .htm 檔
            $("#out1").html(folder + "<br>" + txt + "<br>" + htm);
            $("#out1").show();
            });
          });
      </script>
    </div>
    <div id="tabs-2">
    </div>
    <div id="tabs-3">
    </div>
  </div>
  <script>
    $(function(){
      $("#tabs" ).tabs();
      $("#out1").hide();
      }); 
  </script>
</body>
</html>

此網頁中的 tab1 頁籤中放置了一個按鈕與預設隱藏的 div 元素 (套用 jQuery UI 的 highlight 樣式類別), 當按下按鈕時會先呼叫 files.js 函式庫中的 get_folder() 函數來取得此網頁在本機中的路徑, 然後呼叫 read_file() 分別讀取專案根目錄下的 hello-world.txt 與 hello-world.htm 這兩個存文字檔案, hello-world.txt 內容為 :

Hello!
World! (text)

而 hello-world.htm 內容為 :

<b>Hello!</b>
<b>World! (html)</b>

將檔案路徑與讀取之檔案內容用跳行元素 br 串接後呼叫 html() 輸出到隱藏的 div 高亮度元件, 最後呼叫 show() 顯示此高亮度元素, 結果如下 :




注意, 由於使用了 Active X 技術, 此範例網頁只能在 IE 執行 (Edge 不支援), 且按下按鈕時會出現如下警告, 必須選 '是' 才會正常執行 :





參考 :

[Jquery]讓div能自由的show、hide