highlight.js

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

星期五, 9月 27, 2019

使用 id 名稱直接存取 DOM 物件

同事問了我一個問題, 為什麼我們總是要叫用 document.getElementById() 來取得 HTML 網頁上的物件?不是明明就可以直接用 id 名稱嗎?由於我的知識停留在古時候, 一查 HTML 標準才發現原來可以直接用 id 或是 name 屬性定義的名稱直接存取, 不需要叫用 document.getElementById(), 例如:

See the Pen
TestID
by 黃昕暐 (@meebox)
on CodePen.



不過在文件中寫到, 到底是哪些名稱會對應到 window 物件的屬性在未來可能會變化, 因此並不建議這樣用, 還是請乖乖的用 document.gettElementById(), 既然如此, 那幹嘛提供這個功能呢?

星期日, 12月 24, 2017

使用 minimify code 網站簡化 HTML 網頁的注意事項

在之前的〈ESP8266 全域變數佔用太多導致不斷重置〉中提到需要將 HTML 內容縮減, 才能放入 ESP8266 可用的記憶體中, 試用了許多提供縮減網頁內容的網站後, 發現許多網站縮減出來的內容都有大小不同的問題, 像是:
  • 無法同時縮減同時含有 HTML/JavaScript/CSS 的單一網頁內容, 必須自己分開處理。
  • 無法正確處理註解, 縮減成單一行後可能因為註解導致原來在註解之後的內容都變成註解了。
  • 任意將單引號換成雙引號, 導致 JavaScript 程式碼出錯。
最後最能符合我需求的是 minify code 網站, 符合我需要縮減內含 HTML/JavaScript/CSS 的單一 HTML 檔需求, 唯一的問題是它在處理 JavaScript 字串中的 "\n" 時會轉成 "n", 例如原始內容為:
Image
縮減後變成:
Image
要自己手動找出來修改, 這是美中不足的地方。

星期三, 9月 28, 2016

在 Visual Studio Code 為新語言增加語法標示 (Syntax Highlighting)

Visual Studio Code 寫程式很好用, 但如果遇到特殊或是很新的語言, 就不見得有前人製作好的套件可以幫程式碼標示語法, 這時候就只好自己動手做了。這得自行設計好對應該語言的語法規則套件, 相關說明可參考 Visual Studio Code 的文件。大致步驟如下:
  1. 撰寫該語言的語法規則檔
  2. 以語法規則檔為基礎建立該語言的擴充功能 (extension)
  3. 將建立好的擴充功能加入 Visual Studio Code 後重新啟動 Visual Studio Code
  4. 開啟該語言的檔案或手動選用該語言擴充功能, 就可以看到語法標示發揮效用
首先, Visual Studio Code 沿用了 Mac 上知名的文字編輯器 TextMate 的語法規則檔格式,名稱為 tmLanguage, 可參考 TextMate 網站上的說明。它其實是依循 Properties List Format 格式的 XML 檔, TextMate 本身有提供工具可以編修 tmLanguage 檔, 如果要手工編輯 XML 檔, 我想大部分的人都會卻步, 還好有善心人士設計好工具可以讓我們用 JSON 或是 YAML 格式來編寫語法規則。由於 TextMate 工具與網頁上的說明範例跟 JSON 較相近, 因此我建議採用 JSON, 大家應該可以很容易將 TextMate 網頁上的範例轉換成 JSON 格式。 使用 JSON 寫好語法規格後, 請先安裝 Visual Studio Code 上的 TextMate Language 擴充功能 (extension),它提供有從 JSON 轉成 tmLanguage 格式的功能:
Image
轉換結果會建立一個與你的 JSON 檔同名的文檔, 但附檔名是 tmLanguage, 這個檔案才是符合 TextMate 的語法規則檔, 存檔後就可以進行下一步。 Visual Studio Code 中的語法功能必須建立成擴充功能, 微軟已經幫大家準備好工具, 可以從 tmLanguage 檔自動設計好擴充功能, 這個工具叫做 'code'-Yeoman Generator, 請依照網頁上的說明安裝 (它需要 node.js 以及 npm 套件管理程式)。安裝完成後即可以下達 yo code 指令建立語言擴充套件:
Image
選取『New Language Support』就會進入選項, 你必須依序回答 tmLanguage 檔的位置、此種語言的副檔名、在 Visual Studio Code 中該語言的識別名稱等等,完成後會以你指定的識別名稱建立一個新資料夾, 裡頭就是它幫你設計好的語言擴充功能。 接著, 只要把該資料夾複製到你的 Visual Studio Code 擴充功能資料夾下即可, 一般就是 C:\Users\使用者名稱\.vscode\extensions, 重新開啟 Visual Studio Code 就可以讓語言擴充套件生效了。像是以下就是我建立的新語言擴充功能, 叫做 FLAG-TXT, 專門用來顯示我服務的公司制式文字檔稿件的格式:
Image
在右下角可以看到目前套用的語言名稱為 FLAG-TXT,畫面中也可以看到小節、中標、圖片標示處以及內文中用全行括號標示的加粗文字都會以不同顏色及樣式顯示。 完成了語言法標示後, 你可能還需要為該種語言加上 auto completion 以及 code snippets, 使用起來就會更便利。

附註:TextMate 的語法規則檔不只 Visual Studio Code 使用,鼎鼎大名的 Sublime Text 以及 Atom 也都是使用同一格式來描述語法的規則, 儼然成為文字編輯器界的標準了。

星期一, 3月 07, 2016

ESP8266 韌體燒錄

ESP8266 是創客界的實用玩具, 許多人都客製了不同的韌體, 以下蒐集了幾種不同的韌體供大家切換使用:
另外, 為了將韌體燒錄到 ESP8266 上, 你會需要燒錄工具, 我自己覺得最方便使用的是 NodeMCU Flasher,  只要在 Config 頁次選擇要燒錄的韌體, 並在 Operation 頁次選取正確的序列埠後, 按下 Flash 按鈕即可燒錄。

星期日, 3月 09, 2014

HTML5 deviceorientation 事件與 CSS3 rotate3D 的方向

由於處理一本 HTML5 翻譯書的關係, 被 HTML5 中 deviceorientation 事件與 CSS3 rotate3D 的方向弄得暈頭轉向, 網路上看起來講得最清楚的就是 This End Up: Using Device Orientation 這篇文章了, 再透過其中的 demo 範例, 可以了解基本的座標軸都是 (圖片取自 Mozilla 網站):
Image
但是 deviceorientation 事件的角度是以右手系統為基準 (亦即右手拇指為座標軸), 所以手機螢幕朝右邊翻時, DeviceOrientationEvent.gamma 會由 0 度漸增至 90 度, 三軸的方向如下圖所示 (圖片來源:http://msdn.microsoft.com/library/ie/dn433240.aspx):

Image
Internet Explorer 的 developer 網站上『裝置方向事件』 一文中的方向說明也是如此。

但是 CSS3的 rotate3D 方向 y 軸是相反的, 而且是以左手系統為基準, 這是很容易搞混的方式, 程式很容易寫錯 (圖片來源:DEV.OPERA):
Image
 補充:更慘的是, Mozilla 目前的方向與 webkit 為基礎的瀏覽器並不一致,參考 Orientation and motion data explained 一文即可發現其中說明的方向與上述略有差異,如果在 Firefox for Android 版本上執行本文一開頭所提到的 HTML5Rocks 網站上的範例,更是會發現完全是以左手系統為基礎, 要特別留意。

星期三, 12月 04, 2013

Webkit 要命的修改--支援 DOM4 的 remove()

Webkit 在 2012 年 9 月做了一項變動, 支援 DOM4 的 remove(), 會在執行此方法的元素有父元素時, 刪除掉自己。如果你用 Opera 18 測試以下的 HTML 檔:
<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <script type="text/javascript">
   function remove() {
    alert("remove is called.");
   }
  </script>
 </head>
 <body>
  <button onclick="remove()">刪除</button>
 </body>
</html>
按一下『刪除』鈕, 你可能預期會執行程式中的 remove(), 顯示訊息窗, 但實際的結果卻是『刪除』鈕不見了, 這是因為按下『刪除』後執行的是內建的 remove(), 而不是程式中所撰寫的 remove(), 而內建的 remove() 就把『刪除』鈕的 button 元素刪除了。

要解決這個問題很容易, 就是不要將 function 取名為 "remove", 不過我想應該有許多舊的網頁上都有以 remove 為名的 function, 如果剛好又是用在類似以上案例的情境下, 就會產生悲劇了!

星期三, 8月 28, 2013

超簡短的行動裝置瀏覽器 JavaScript 偵測技巧

剛剛看到 stackoverflow 上一篇超讚的回覆, 使用以下的技巧偵測是否行動裝置的瀏覽器:
if(typeof window.orientation !== 'undefined'){...}
由於一般來說, PC 上的瀏覽器都沒有提供 window.orientation 屬性, 因此可以用來快速判別是否為行動裝置的瀏覽器。

當然, 隨著往後 PC 與平版、手機界線的混淆, 也許這個方法會失效, 不過目前應該還堪用, 先學起來!

星期六, 8月 24, 2013

使用 JavaScript 縮放網頁內容

同事開出了一個需求, 希望專為手機製作的網頁若是在桌機上的瀏覽器觀看時, 能夠自動縮小尺寸,不然以現在手機至少 800X400 點數以上的設計, 在手機上看起來大小適中的圖片, 到了桌機上的瀏覽器都會過大。

為了解決這個問題, 第一步是先能判別是手機還是桌機, 這在 detect mobile browser 網站已經提供有現成的 JavaScript 碼可用。接著就是希望能夠縮放網頁內容, 查了各種解法, 看起來要想透過 JavaScript 直接控制瀏覽器的縮放比例是不可行的, 只能透過 CSS 直接縮放網頁內容, 根據 stackoverflow 上這篇問答, 根據瀏覽器的種類, 可分別處理:
  • Firefox 提供有 MozTransform 屬性可以用 scale(1.0) 的格式設定元素的縮放比例, 1,0 就是原尺寸不縮放。
  • Chrome 或是 IE 則可用 zoom 屬性, 以 100% 的格式設定縮放比例。
只要將上述屬性套用在body 元素的 CSS 上, 就可以將整個網頁內容縮放成指定的比例。

不過對於 Firefox, 縮放後網頁的上下左右都會留下大塊的空白區域, 使得縮放後的內容不識顯示在瀏覽器的中央上方處, 要解決這個問題, 必須再使用 MozTransformOrigin 屬性設定為 "center top" 才行。

利用上述方式寫成的 JavaScript 碼如下:

window.onload = function(){
    var a = navigator.userAgent||navigator.vendor||window.opera;
    var level = 60;
    if (!(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))) {
     if (navigator.userAgent.indexOf('Firefox') != -1 && parseFloat(navigator.userAgent.substring(navigator.userAgent.indexOf('Firefox') + 8)) >= 3.6){//Firefox)
         document.body.style.MozTransform = "scale(" + level/100 + ")";
         document.body.style.MozTransformOrigin = "center top";
     }
     else {
      document.body.style.zoom = "" + level + "%";
      document.body.style.msTransformOrigin = "center top";
     }
 }   
};

這個解法在 IE 上也有和 Firefox 類似的問題, 就是網頁內容會偏向瀏覽器視窗的左邊, 而不是在中間上方, 目前還沒有找到解決的方法, 很可惜。

星期日, 3月 24, 2013

JavaScript 的 var

今天有同事問我 JavaScript 變數有加 var 宣告跟沒有有什麼差?我聽了也是一愣, 後來去查了文件才發現, 原來 var 不 var 可有差了, 主要大概可以歸納為這兩點:
  1. JavaScript 是以 function 來建立新的變數 scope, 只要是在 function 之外宣告的變數都是全域變數, 也就是全域物件的屬性, 而只有全域變數可以不用加 var 宣告, 就可以直接使用。
  2. 有加 var 宣告的全域變數是全域物件 (在瀏覽器中就是 window) 的 non-configurable 屬性, 比如說你就無法使用 delete 刪除以 var 宣告的全域變數。
舉例來說, 以這個 html 檔為例:
<!DOCTYPE html>
<html>
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>
  <div id="da"></div>
  <div id="db"></div>
  <div id="dc"></div>
  <div id="dd"></div>
<script>
i = 10;
var j = 20; 
this.i = 100;
this.j = 200;

$("#da").text(i);
$("#db").text(j);
$("#dc").text(this.i);
$("#dd").text(window.j);
</script>
 
</body>
</html>
實際輸出的結果就是:
100
200
100
200
你可以看到不論是用 i 或是 this.i 都是同一份資料, 這是因為 this 指向全域物件, 所以也可以用 window.j 以存取 j, 因為在瀏覽器中, window 就是全域物件。

如果你在剛剛的 JavaScript 程式中插入兩航程式, 嘗試用 delete 刪除 i 與 j:
i = 10;
var j = 20; 
this.i = 100;
this.j = 200;

delete this.i;
delete this.j;

$("#da").text(i);
$("#db").text(j);
$("#dc").text(this.i);
$("#dd").text(window.j);
在瀏覽器的 JavaScript console 中就會看到以下的錯誤訊息:
Uncaught ReferenceError: i is not defined
這是因為 i 並沒有使用 var 宣告, 是全域物件的 configurable 屬性, 因此可以用 delete 刪除, 所以當執行到底下存取變數 i 的值時, i 變數就已經不存在了。

事情還沒完, 如果你是使用 node.js, 那麼又複雜了一點, 使用 var 宣告的全域變數實際上不是真的全域, 而只能在模組 (可簡單看成是檔案) 內存取, 出了模組, 就無法存取了。以底下的程式為例:
i = 10;
var j =20;

console.log(global.i);
console.log(global.j);
console.log(i);
console.log(j);
執行結果如下:
10
undefined
10
20
透過 node.js 的全域物件 global 可存取到沒有使用 var 宣告的 i 變數, 這是真正全域的變數。可是透過 global 物件嘗試存取 j 變數時, 就會看到 global.j 是未定義的屬性, 這表示 j 並不是真正全域的變數。

如果另外寫個程式把剛剛的測試程式當成模組載入:
var tv = require("./testvar.js");

console.log("global==============");
console.log(i);
//console.log(j);
輸出結果如下:
10
undefined
10
20
global==============
10
可以看到在程式中可存取到在另一個檔案中宣告的變數 i, 的確是全域變數無誤。若是把程式中最後一行的註解拿掉, 嘗試存取 j, 就會看到:
10

D:\mee\My Box Files\practice\jswebapp\testvar2.js:5
console.log(j);
^
ReferenceError: j is not defined
at Object. (D:\mee\My Box Files\practice\jswebapp\testvar2.js:5:13)
可以看到 j 並未定義, 這是因為 j 是用 var 在其他檔案中宣告的變數, 不是全域變數, 所以無法存取。

星期六, 9月 22, 2012

讓 jQuery Mobile 頁面上的固定式頁首與頁尾工具列不會因碰觸畫面而隱藏

有朋友今天傳了一個 jQuery Mobile 的頁面給我, 程式如下:
<!DOCTYPE html> 
<html>
<head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1"> 
 <title>jQuery Mobile Demo</title> 
 <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.1/jquery.mobile-1.1.1.min.css" />
 <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
 <script type="text/javascript">
 $(document).bind("mobileinit", function(){
  $("[data-role=header],[data-role=footer]").fixedtoolbar({ tapToggle:false });
 });
 </script>
 <script src="http://code.jquery.com/mobile/1.1.1/jquery.mobile-1.1.1.min.js"></script>
</head> 

<body> 
<div data-role="page">
 <div data-role="header" data-position="fixed" data-fullscreen="true">
  <h1>全螢幕標頭</h1>
 </div>
 <div data-role="content">
  <img src="mycat.jpg" /> 
 </div>
</div>
</body>
</html>
這個程式是希望能在初始化的階段透過第 11 行的程式設定固定在頁首與頁尾的工具列不會因為使用者碰觸畫面而自動隱藏或重現, 不過實際執行之後, 發現設定失效, 工具列還是會以預設的方式自動隱藏或重現。經過查詢 jQuery Mobile 文件以及 Google 爬文後, 發現 mobileinit 事件發生時, 其實後續的 HTML 內容並未載入, 因此事件處理方法中的 $("[data-role=header],[data-role=footer]") 根本就抓不到元素, 自然無法讓期望的設定生效。為了解決這個問題, 只要改成處理 pageinit 事件, 也就是在後續的 HTML 內容生成後再進行設定, 就可以正確執行了。修改的程式如下:
$(document).bind("pageinit", function(){
 $("[data-role=header],[data-role=footer]").fixedtoolbar({ tapToggle:false });
});
你也可以在事件處理的程式中多加上一行 alert() 呼叫, 就可以觀察實際抓取到的元素內容了:
$(document).bind("pageinit", function(){
 $("[data-role=header],[data-role=footer]").fixedtoolbar({ tapToggle:false });
 alert($("[data-role=header],[data-role=footer]").text());
});

星期日, 6月 17, 2012

jQuery 與 jsonp

jsonp 簡單的來說, 就是使用 ajax 存取跨網域 json 格式資料的技術, 這主要是因為瀏覽器通常都會因為安全的理由, 限制 ajax 機制存取非來源網站的資料, 也就是所謂的 "origin policy"。換句話說, 只要資料不是和發出 ajax 請求的網頁位於同一個網域, 瀏覽器就不會讓 ajax 存取該資料。最簡單的例子就是如果你直接瀏覽本機檔案系統上的 HTML 檔, 從這個 HTML 檔發出 ajax 存取網路上任何網站提供的 json 資料, 就會因為這份 HTML 檔式是位於本機的檔案系統上, 和網路上的任何網站都不在同一個網域而無法存取。

以下我們以一個簡單的例子來說明, 這裡我們要存取的是台北市資料公開平台中的停水資訊, 你可以透過以下的 URL 取得 json 格式的停水資訊:

http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo?format=json

如果我們使用以下的 HTML 網頁 (此程式修改自此網頁) 去存取停水資訊, 會發現瀏覽器根本不會送出請求:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>JQuery JSONP</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
    $(document).ready(function(){
 
        $("#useJSONP").click(function(){
            $.ajax({
                url: 'http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo',
                data: {format: 'json'},
                dataType: 'json',
                success: jsonpCallback
            });
        });
 
    });
     
    function jsonpCallback(data){
    $('#jsonpResult').text(data.d[0].stitle);
    }
    </script>
  </head> 
  
  <body>
    <input type="button" id="useJSONP" value="Use JSONP"></input>
    <span id="jsonpResult"></span>
  </body>
</html>

如果使用 Chrome 的開發人員工具,在 Console 頁次中會看到錯誤訊息 "XMLHttpRequest cannot load .... Origin null is not allowed by Access-Control-Allow-Origin.", 表示因為只限來源網域才能進行 ajax, 所以無法載入所要的資料。

為了解決這個問題, 就有人想出了 jsonp 的方法。由於瀏覽器在處理 <script src="...">
的時候, 並沒有來源網站的限制, 因此只要能動態的產生一個 <script>
元素, 指定 src 屬性, 就可以動態載入並執行一段 JavaScript 碼, 若是 server 端配合, 產生類似以下的程式碼:

jsonCallnack(原本的 json 資料)

那麼當瀏覽器為動態產生的 <script> 元素載入此段程式碼執行, 就會呼叫 jsonCallback 函數處理 json 資料, 達到跨網域存取資料的目的。

為了完成上述動作, 一般就是在 <script> 的 src 屬性值指定的 URL 添加 "callback=jsonCallback" 參數, server 端收到這個請求時, 就會檢查 callback 參數的值, 將此參數搭配 json 資料產生一段如同前述內容的 JavaScript 碼。

你可以發現 jsonp 要能成功, 必須仰賴 server 端配合, 而我們所存取的台北市資料公開平台就支援 jsonp, 會在發現查詢字串中有 callback 參數時, 改用 jsonp 的方式運作, 傳回一段 JavaScript 碼, 若是沒有 callback 參數, 就直接傳回 json 格式的資料。

在 jQuery 中, 只要在呼叫 ajax() 時把 dataType 選項指定為 "jsonp", ajax() 就會改用 jsonp 的方式, 動態產生 <script>, 並且自動依據 success 選項指定的 callback 函數, 在 url 選項的內容加入 "callback=XXX" 的參數, 其中 XXX 就是 success 選項所指定函數的名稱。

如果我們把之前的範例改為這樣:

$("#useJSONP").click(function(){
  $.ajax({
    url: 'http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo',
    data: {format: 'json'},
    dataType: 'jsonp',
    success: jsonpCallback
   });
});

就可以看到瀏覽器上顯示傳回的停水資訊中的第一筆資料的 stitle 欄位內容, 若是觀察開發人員工具中的 Network 頁次, 會看到發出的請求為:

http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo?callback=jQuery1720631575214676559_1339854122194&format=json&_=1339854129075

你可能會覺得很奇怪, 明明我們指定的 callback 函數是 jsonpCallback, 為什麼 URL 中的 callback 參數內容卻不一樣?這是因為 jQuery 會在中間介接一個 jQurey 自動產生的函數, 在這個函數中再去呼叫我們指定的 callback 函數。

如果你要存取的 server 端不是以 "callback" 作為識別名稱, 而且你沒有辦法修改 server 端的程式, jQuery 的 ajax() 函數也提供你透過 jsonp 選項自訂參數名稱的功能, 在這種情況下, 你可以透過 jsonpCallback 選項指定 callback 函數, 此外, 還可以再透過 success 選項指定成功執行時要呼叫的 callback 函數。我們可以再把之前的範例改為這樣:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>JQuery JSONP</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
    $(document).ready(function(){
 
        $("#useJSONP").click(function(){
            $.ajax({
                url: 'http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo',
                data: {format: 'json'},
                dataType: 'jsonp',
                jsonp: 'callback',
                jsonpCallback: 'jsonpCallback',
                success: function(){
                    alert("success");
                }
            });
        });
 
    });
     
    function jsonpCallback(data){
    $('#jsonpResult').text(data.d[0].stitle);
    }
    </script>
  </head> 
  
  <body>
    <input type="button" id="useJSONP" value="Use JSONP"></input>
    <span id="jsonpResult"></span>
  </body>
</html>

執行後除了可以看到取回的停水資訊外, 也會因為 success 選項指定的 callback 函數, 而顯示訊息視窗。如果觀察開發人員工具列中 Network 頁次, 可以看到發出的請求其 URL 為:

http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo?callback=jsonpCallback&format=json&_=1339854333659

可以確認 jQuery 完全依照 jsonp 與 jsonpCallback 選項的值改寫了 url 選項指定的內容。透過上述的說明, 就可以利用 jQuery 進行跨網域的 ajax 存取了。

星期五, 2月 10, 2012

node.js 的 ServerResponse 回應 binary data 的問題

這兩天在測試時一直被一個問題困擾, 我是用 node.js 寫一個簡單的 HTTP Server 接收 GET request, 這個 server 會把傳入的 GET 參數 (都是表示整數的字串) 轉成整數後加總傳回, 一開始都用文字的方式傳回, 測試也都正常, 可是因為我的 client 要處理字串有點麻煩, 所以當我嘗試把加總值改成用 binary data 傳回一個 16 位元的整數值時, client 這邊一直收不到答案, 經過一番測試, 發現 node.js 這邊會把 HTTP respnse header 與 binary data 分開成兩次傳回, client 這邊沒有考慮到這個問, 於是就收不到答案了。

後來直接看了一下 node.js 裡頭 http.js 原始碼, 發現果然如果使用 ServerResponse.end() 或是 ServerResponse.Write() 送出回應時, 如果 header 尚未送出, 而要回應的資料是字串的話, 就會直接將 header 與回應內容串在一起後送出, 但若是回應的是 binary data, 就會將 header 與 binary data 分開送, 事實上, 只要傳送的資料的編碼不同, 就會分開傳送。

如果您也使用 node.js 的話, 希望這個經驗能夠避免一些問題。

星期三, 11月 30, 2011

免費的 JavaScript 檔案存放空間:Free Javascript Hosting

因為想把 JavaScript library 放在網路上, 以便能在 blogger 的網誌上使用, 順手找了一下, 果然有免費的 JavaScript 檔案存放空間, 而且上載程序超簡單, 只要填入 email 就可以上載, 之後就可以在任一網頁中引用了, 真不賴!

Free Javascript Hosting