jquery detach on angular directive

在angular中會有基本的jquery-lite,也就是angular.element,當在include angular之前,就先include jquery,此時angular的jquery-lite,就會被自動替換成jquery。

替換angular.element的source code如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function bindJQuery() {
// bind to jQuery if present;
jQuery = window.jQuery;
// reset to jQuery or default to us.
if (jQuery) {
jqLite = jQuery;
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: JQLitePrototype.controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
// Method signature:
// jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
jqLitePatchJQueryRemove('remove', true, true, false);
jqLitePatchJQueryRemove('empty', false, false, false);
jqLitePatchJQueryRemove('html', false, false, true);
} else {
jqLite = JQLite;
}
angular.element = jqLite;
}

另外一個需要注意的地方就是jqLitePatchJQueryRemove,會將jquery原生的function,多經過一層處理,處理完在呼叫jquery的function

jqLitePatchJQueryRemove的source如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {     
var originalJqFn = jQuery.fn[name];
originalJqFn = originalJqFn.$original || originalJqFn;
removePatch.$original = originalJqFn;
jQuery.fn[name] = removePatch;

function removePatch(param) {
// jshint -W040
var list = filterElems && param ? [this.filter(param)] : [this],
fireEvent = dispatchThis,
set, setIndex, setLength,
element, childIndex, childLength, children;

if (!getterIfNoArguments || param != null) {
while(list.length) {
set = list.shift();
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
element = jqLite(set[setIndex]);
if (fireEvent) {
element.triggerHandler('$destroy');
} else {
fireEvent = !fireEvent;
}
for(childIndex = 0, childLength = (children = element.children()).length;
childIndex < childLength;
childIndex++) {
list.push(jQuery(children[childIndex]));
}
}
}
}
return originalJqFn.apply(this, arguments);
}
}

其中element.triggerHandler('$destroy')會告知angular底下的transcludedScope,這個element已經被摧毀了,所以會導致,$element.detach的時候,transcludedScope無法被更新。element.triggerHandler('$destroy')的監聽event可以參考createBoundTranscludeFn這個function,會有下這段code去監聽:

1
clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));

jqLitePatchJQueryRemove只替換removeemptyhtml,而沒有替換detach,使用detach卻會影響scope,真正原因要找jquery的source code。

因為detach間接呼叫了remove:

1
2
3
detach: function( selector ) {
return this.remove( selector, true );
}

Example(1.2.7):

由此可知,只要scope尚未ready時,呼叫detach就不會中斷scope的更新。若要讓directive的scope正常運作,可用以下方式:

  1. 不使用transclude
  2. 不在scope ready中,使用detach
  3. 使用原生api
  4. 最後一種方式,jquery在angular之後include

如果不是這麼需要使用到jquery,建議就不要include了,當然jquery也提供很多方便的功能,依照project需求自己評估吧!

t-count">1
  • November 20151
  • March 20151
  • February 20151
  • October 20141
  • July 20141
  • March 20143
  • February 20144
  • January 20142
  • December 20131
  • November 20131
  • October 20138
  • September 20136
  • June 20131
  • May 20133
  • April 20136
  • March 201310
  • December 20123
  • November 20123
  • October 20121
  • September 20125
  • August 20121
  • July 20122
  • June 20122
  • May 20122
  • April 20123
  • February 20121
  • November 20111
  • July 20113
  • June 20113
  • May 20113
  • April 20116
  • March 201112
  • Recents