jqLite Orz list (v1.1.5)

jqLite 是 AngularJS 為了方便進行 DOM 操作而又不希望讓 jQuery 成為 dependency 的解決方案,官方文件如是說:

jqLite is a tiny, API-compatible subset of jQuery that allows Angular to manipulate the DOM. jqLite implements only the most commonly needed functionality within a very small footprint, so only a subset of the jQuery API - methods, arguments and invocation styles - are supported.

並且在文件下方給了一串相關 API 文件的連結,連到 jQuery API Documentation 去。

嗯?連到 jQuery API Documentaion 有什麼不對嗎?

前言

爬一爬 jqLite 的 source code 就可以知道,其實它是嘗試照著 jQuery 的部分 API 做 subset-implementation,結果就是雖然它給了 jQuery API Documentation 的連結,它部分 API 的部分功能是與 jQuery 不同的。如果你還是照著使用 jQuery 的用法來使用 jqLite 而沒有做好爬 jqLite source code 的心理準備,應該會有點 happy …

本文整理了 v1.1.5 幾個比較常碰到的地雷,與各位共勉之。

環境設定

  • 不載入 jQuery
  • 由於 AngularJS v1.2.0 於本文撰寫時尚在 RC2,並且我自己主 project 在使用的版本為 v1.1.5,因此本文針對的版本為 v1.1.5

.css() as setter 不會自動加上 px 單位

jQuery 除了部分的 css style,其餘 style 在沒有帶入單位的時候,預設都會加上 px 做為單位,而 jqLite 不會幫你加。

// works
iElm.css('font-size', '18px');

// won't work
iElm.css('font-size', 18);

Demo

與下一段 getter 一併 Demo。

.css() as getter 不會取 computed style

有些 style 是具有繼承性質的(e.g. font-size, color, etc…),而 jqLite 的 .css() 不像 jQuery 會想辦法取得瀏覽器計算後的結果,它只能夠取得該 DOM element 上已設定的 style 內容。

console.log( iElm.css('font-size') );
// output: ""

// jQuery 的取法
iElm[0].ownerDocument.defaultView.getComputedStyle(iElm[0]).fontSize;

Demo

  • 利用 <input type=”text”> 來修改期望的 font-size
  • 用上一段所說的方法實做了兩種 setter 的 directive font-size-successfont-size-error
  • 更新 font-size 的同時,另一個 directive get-font-size 會嘗試從沒有被直接設定 style 的 <span> 上取得 jqLite 直接拿的 font-size 以及計算的 font-size

.bind() 與 .unbind() 對 eventType 參數的理解不同

此問題於 v1.2.0rc1 後已修正。

何謂理解不同呢?在 eventType 代入 "play ended pause" 的時候:

  • .bind() 會視為 play, ended, pause 三個分別處理
  • .unbind() 沒有把它們切開,於是根本就會找不到這個 event 來 unbind
// 一次綁三個: play, ended, pause
iElm.bind('play ended pause', playbackHandler);

// 這邊這樣無法 unbind 且不會跳錯誤
iElm.unbind('play ended pause', playbackHandler);
// 一樣無法 unbind
iElm.unbind('play ended pause');

// 成功 unbind,不過基本上把事件都清光了
iElm.unbind();

// 一個一個來會成功
angular.forEach(['play', 'ended', 'pause'], function (evtName) {
  iElm.unbind(evtName, playbackHandler);
});
// 對,就是一個一個來
iElm.unbind('play', playbackHandler);
iElm.unbind('ended', playbackHandler);
iElm.unbind('pause', playbackHandler);

Demo

  • bind-unbind directive 試著將給定的事件 .bind() 至按鈕上並立刻 .unbind()
  • 實作 .unbind() 的方法是上述不會成功的做法,因此嘗試去按鈕上觸發事件還是可以成功執行 bind-unbind-trigger 的內容
  • 歡迎使用 Edit 功能將 directive 裡面的 .unbind() 替換成會成功的做法看看結果

TL;DR

把 jQuery 擺在 AngularJS 前載入,一勞永逸。

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="http://code.angularjs.org/1.1.5/angular.min.js"></script>

roger relieved