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使用的,内容全是大同小异,怎么用也不说清楚,复制粘贴来的东西终究是别人的,那这篇博客的产出说到底浪费本身和读者的时间。这也提醒了我本身,对于之后的博客编写,涉及到代码相关的,必定亲自试验,保证可用。
学习不是一天两天,没有捷径,惟有积累。