angular-easyfb v1.3.1 released!

https://goo.gl/jKvPnc

  • 為了 adaptive width 的 plugin (目前只有 fb-page),調整 directive parsing 的流程
  • 支援 Embedded Video Player

想不到才隔一天馬上又 release 一個小版… 我也是千百個不願意!

話說昨天那位印度人很快的發現了新支援的 Page plugin 有 bug

fb-page-bug-report.png

我也馬上著手修正,想不到這一下去就是 5 小時…

遠古怪獸 - initial XFBML rendering

其實就是某個一直以來存在但不為我所知的問題在這個 bug 中浮現了。

Facebook JavaScript SDK 會在第一次載入之後主動去掃過整個 DOM (with document.getElementsByTagName('*')),對出符合它支援顯示的 social plugin 之後進行一次 render。

一直以來這個初次的 render 都被我的 directive 忽視了,在我的 directive 生成的時候會透過 FB.XFBML.parse() 也來針對性的 render 一次。往往 SDK 載入的那次都會跑在我的 directive render 之後,總之如果一個 social plugin 在頁面第一次載入的時候是可見的,基本上它就是會被連 call 兩次 FB.XFBML.parse()

是說我在挖 SDK 裡面的程式碼的時候發現,針對正在跑 rendering 流程的 plugin,FB.XFBML.parse() 還是會無情的觸發第二次 render,明明有個參數是「強制 parse」你們永遠代 true 是哪招啊啊啊啊!(另外其實內部有個 function 叫做 parseNew,就是「不強制 parse」正在 render 的 plugin,結果整個 code 都沒有 call 它到底是…)

Adaptive width

遠古怪獸當然是不會隨隨便便就醒過來,需要適當的媒介才能夠喚醒他(X)

還記得上版 release 有說到 adaptive width 需要取得 parent 的 width;而根據 SDK 內程式碼,其實他們使用的方法是很單純的 element.offsetWidth。這時候遠古怪獸就登場了!

Facebook 的 social plugin 一旦進入 parse -> rendered 的過程之後就會主動的被新增一個相當關鍵的 class 叫做 fb_iframe_widget (乍看好像只有 iframe widget 才會套用的 class,但現在每個 plugin 都是 iframe 就是了),來看看它的 CSS style:

.fb_iframe_widget {
  display: inline-block;
  position: relative;
}

發現問題出在哪兒了嗎? 我們先列出一次簡單的 parse 流程:

  1. 找到有 fb-page class 的元素
  2. 透過 element.offsetWidth 取得 container width
  3. 為元素加上 class fb_iframe_widget
  4. 完成主要的畫面產出(需載入 iframe -> 較花時間)

如果把兩次 FB.XFBML.parse() 交錯在一起呢?

  1. (A) 找到有 fb-page class 的元素
  2. (A) 透過 element.offsetWidth 取得 container width (正常值)
  3. (A) 為元素加上 class fb_iframe_widget
  4. (B) 找到有 fb-page class 的元素 (雖然發現已經在 parse 但由前段說明得知會再跑一次)
  5. (B) 透過 element.offsetWidth 取得 container width (由於已經變成 display: inline-block,基本上會是 0)
  6. (B) 為元素加上 class fb_iframe_widget
  7. (A) 完成主要的畫面產出(需載入 iframe -> 較花時間) (container width 正常)
  8. (B) 完成主要的畫面產出(需載入 iframe -> 較花時間) (container width 0)

由於最後完成的 render 的是 (B),我們 adaptive width 吃到的 container width 會是 0,結果來看就是

無論你的 Page plugin 的外層元素寬度是多少,它都會以最小值 180px 產出畫面

最後

本來我想說單純給我們的 directive render 一個 delay 就好了,結果發現不管怎麼量,FB SDK 內的 domReady function 之於 angular 內部的 bootstrap 總是有超過 200ms 的差距… 真心扯到爆啊~~~

最後決定,在 directive compile 階段就先把判斷 plugin widget 的關鍵 class 拔掉(e.g. fb-page),等到 FB SDK 的自動 parse 跑完再把它加回來跑我們的 FB.XFBML.parse()

總之上面整個 debug + 修正的過程花了我 3 小時,而由於這次改動是為了解決遠古怪獸,對現有的 plugin directive 的 render 流程影響滿大的,造成成群的 test failure …

所以把 test 們改到好又花了我 2 小時。

欸,我以為 FB 是去小便都會被天才少年夾擊(天才 | 我 | 天才)的地方,上面那個遠古怪獸到底是 WTFFFFFFFFFFFFFFF