1、前言html
最近作了一個圖片懶加載的小插件,功能須要dom渲染完成後,好獲取那些須要懶加載的dom元素。那麼問題來了,若是隻是感知靜態的dom用ready,onload均可以,但項目用的angular,ng-repeat何時循環完,或者說angular自身的生命週期中dom渲染完成怎麼知道,這裏作個解決問題的記錄。api
2、網上流傳的解決方案數組
1.data-ng-init---無效dom
大概意思是,給你須要監聽的dom,好比body添加一個data-ng-init屬性,綁定你須要在body加載完成後執行的方法。異步
<div data-ng-init = "load()"></div> $scope.load = function () { //dosomething };
我查了下資料,在stackoverflow中找到了相關介紹,data-ng-init本質是ng-init,只是在對於H5以前,ng寫法會報錯,爲了解決這個錯誤而添加data前綴達到兼容的寫法,因此本質仍是ng-init。編輯器
在HTML5開始以後,像Visual Studio這樣的代碼編輯器突出顯示'ng-',這是無效的。但實際上它是有效的,因此有一種方法可讓代碼編輯器經過在前面加上'data-ng- *'來理解AngularJS的屬性是有效的。函數
所以,當在任何HTML5代碼編輯器中使用前綴時,它不會強調屬性並將它們視爲有效。學習
這是'data- *'前綴的最初目的。-----點我跳轉原回答測試
那咱們就改成ng-init測試下,當我執行ng-init中的代碼時,是否是連angular自身的動態dom都加載完成了。es5
我將ng-init直接綁在了一個須要ng-repeat的ul上,當斷點已經執行了我load方法
我去看了此時的dom渲染狀況
ul裏面一個li都沒有,空的,說明根本沒解析完成啊,這個方法也就能感知下靜態dom渲染,angular的無效,因此不符合個人要求,排除。
2.$viewContentLoaded事件---無效
大量博客都說了這個方法,那看來是很是的有效啊,去官網查了下api,介紹少之又少
英文很差,大概意思是,須要結合ng-view指令使用,只要ng-view指令範圍的視圖須要從新渲染,經過監聽$viewContentLoaded,就能針對改變作你想作的操做了。
<div ng-view></div>
$scope.$on('$viewContentLoaded', function () { //dosomething });
測試了下,代碼沒執行,又去翻了下資料,懷疑是否是本身用錯了,找到了一個關於使用的的特殊說明
當ngView內容被從新加載時,從ngView做用域上發佈, 經過$emit將事件沿着做用域向上傳播(子做用域到父做用域),也就是說你監聽這個事件必須得在那個View的上層做用域。----點我查看原文
也沒錯啊,將$on換成$watch仍是沒效,先不說有沒有效,這東西只是說感知ng-view變化時執行,沒說dom加載完成後執行,不是我要找的東西。排除在外。
3.自定義指令,$last === true---有bug
由於我作圖片懶加載的要求是,在執行懶加載方法前這些img元素都已經渲染好了,我能抓到它們。而這些圖片說到底就是經過ng-repeat渲染出來的,既然感知angular dom渲染完成無效,換種思路,能不能得知ng-repeat何時渲染完成呢?
經過自定義指令repeatFinish,監聽ng-repeat狀態。
<ul> <li ng-repeat="item in data track by $index" repeat-finish></li> </ul> angular .module("mainApp") .directive('repeatFinish', [function () { //判斷ng-repeat是否渲染完成的自定義指令,暫時沒用到,之後可能會用 return { restrict: 'EA', link: function (scope, element, attr) { if(scope.$last === true) { //dosomething }; }, }; }]);
在ng-repeat過程當中,scope做用域中有一個$last的狀態變量,當循環到最後一個元素時,它就會變成true,而這個方法是寫在link中的,link是爲dom綁定相關指令事件的,趕忙去測試下,打個斷點
出問題了,我須要循環的數組其實有10條數據,理論上來講,一開始索引$index應該從0開始,可是這裏卻直接從1開始了,也就是第二條數據,假設我須要循環的數據一共就1條,link裏面的函數直接就不觸發了。
其次,由於我實際使用是在產品分類頁中,點擊不一樣產品分類,被循環的數據data實際上是在改變的,有趣的是,假設A類產品有4條,B類產品有3條,由4條切換到3條的過程當中,也不會觸發link中的函數。
對於這種作法的問題,大概概括爲兩點:
一是數據只有1條時監聽不到,方法是通用的,誰知道你要遍歷的數據有幾條。
當須要repeat的數據是可變的,由多變少的過程不會觸發,少變多的過程會觸發,說到底仍是有問題,用不了,有興趣的同窗能夠寫demo測試下,我暫時也解釋不了爲何會這樣。
3、靠譜的解決方案
功夫不負有心人,在簡書的一篇文章中,找到了可行靠譜的方法,使用$timeout。
<ul> <li ng-repeat="item in data track by $index"></li> </ul> $timeout(function () { //處理dom加載完成,或者repeat循環完成要作的事情 },0);
原理是什麼呢,你們都知道,js的定時器其實也是異步的,$timeout其實只是angular爲了能自動觸發髒檢測而封裝的方法,一樣也是異步。將你須要執行的方法放在$timeout中,它就會等到全部的dom渲染完成以及同步邏輯跑完最後執行,真的是讓人眼前一亮!
方案出處 實現AngularJS渲染完畢後執行腳本
4、關於寫博客的自我反應
在我查解決方案的過程當中,我確實是被一些博客弄的特別煩躁和惱火,文章內容全靠複製,代碼本身不試驗,好比談到$viewContentLoaded幾乎沒有人提都沒提這個東西是結合ng-view使用的,內容全是大同小異,怎麼用也不說清楚,複製粘貼來的東西終究是別人的,那這篇博客的產出說到底浪費本身和讀者的時間。這也提醒了我本身,對於之後的博客編寫,涉及到代碼相關的,必定親自試驗,保證可用。
學習不是一天兩天,沒有捷徑,惟有積累。