Skip to content

Commit cb34300

Browse files
committed
fix(ngAnimate): ensure that all jqLite elements are deconstructed properly
Prior to this fix if a form DOM element was fed into parts of the ngAnimate queuing code it would attempt to detect if it is a jqLite object in an unstable way which would allow a form element to return an inner input element by index. This patch ensures that jqLite instances are properly detected using a helper method. Closes angular#11658
1 parent d83bddc commit cb34300

File tree

6 files changed

+50
-20
lines changed

6 files changed

+50
-20
lines changed

src/ngAnimate/animateCss.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
472472
return stagger || {};
473473
}
474474

475-
var bod = $document[0].body;
475+
var bod = getNode($document).body;
476476
var cancelLastRAFRequest;
477477
var rafWaitQueue = [];
478478
function waitUntilQuiet(callback) {
@@ -521,7 +521,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
521521
}
522522

523523
function init(element, options) {
524-
var node = element[0];
524+
var node = getNode(element);
525525
options = prepareAnimationOptions(options);
526526

527527
var temporaryStyles = [];

src/ngAnimate/animateCssDriver.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
1616
// only browsers that support these properties can render animations
1717
if (!$sniffer.animations && !$sniffer.transitions) return noop;
1818

19-
var bodyNode = $document[0].body;
20-
var rootNode = $rootElement[0];
19+
var bodyNode = getNode($document).body;
20+
var rootNode = getNode($rootElement);
2121

2222
var rootBodyElement = jqLite(bodyNode.parentNode === rootNode ? bodyNode : rootNode);
2323

@@ -44,7 +44,7 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
4444
}
4545

4646
function prepareAnchoredAnimation(classes, outAnchor, inAnchor) {
47-
var clone = jqLite(outAnchor[0].cloneNode(true));
47+
var clone = jqLite(getNode(outAnchor).cloneNode(true));
4848
var startingClasses = filterCssClasses(clone.attr('class') || '');
4949
var anchorClasses = pendClasses(classes, NG_ANIMATE_ANCHOR_SUFFIX);
5050

@@ -113,7 +113,7 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
113113
function calculateAnchorStyles(anchor) {
114114
var styles = {};
115115

116-
var coords = anchor[0].getBoundingClientRect();
116+
var coords = getNode(anchor).getBoundingClientRect();
117117

118118
// we iterate directly since safari messes up and doesn't return
119119
// all the keys for the coods object when iterated

src/ngAnimate/animateQueue.js

+13-13
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
117117
}
118118

119119
function findCallbacks(element, event) {
120-
var targetNode = element[0];
120+
var targetNode = getNode(element);
121121

122122
var matches = [];
123123
var entries = callbackRegistry[event];
@@ -198,7 +198,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
198198
// (bool) - Global setter
199199
bool = animationsEnabled = !!element;
200200
} else {
201-
var node = element.length ? element[0] : element;
201+
var node = getNode(element);
202202
var recordExists = disabledElementsLookup.get(node);
203203

204204
if (argCount === 1) {
@@ -222,7 +222,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
222222

223223
function queueAnimation(element, event, options) {
224224
element = stripCommentsFromElement(element);
225-
var node = element[0];
225+
var node = getNode(element);
226226

227227
options = prepareAnimationOptions(options);
228228
var parent = element.parent();
@@ -408,7 +408,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
408408
close(!status);
409409
var animationDetails = activeAnimationsLookup.get(node);
410410
if (animationDetails && animationDetails.counter === counter) {
411-
clearElementAnimationState(element);
411+
clearElementAnimationState(getNode(element));
412412
}
413413
notifyProgress(runner, event, 'close', {});
414414
});
@@ -435,7 +435,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
435435
}
436436

437437
function closeChildAnimations(element) {
438-
var node = element[0];
438+
var node = getNode(element);
439439
var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
440440
forEach(children, function(child) {
441441
var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));
@@ -454,9 +454,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
454454
}
455455

456456
function clearElementAnimationState(element) {
457-
element = element.length ? element[0] : element;
458-
element.removeAttribute(NG_ANIMATE_ATTR_NAME);
459-
activeAnimationsLookup.remove(element);
457+
var node = getNode(element);
458+
node.removeAttribute(NG_ANIMATE_ATTR_NAME);
459+
activeAnimationsLookup.remove(node);
460460
}
461461

462462
function isMatchingElement(a,b) {
@@ -466,7 +466,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
466466
}
467467

468468
function closeParentClassBasedAnimations(startingElement) {
469-
var parentNode = startingElement[0];
469+
var parentNode = getNode(startingElement);
470470
do {
471471
if (!parentNode || parentNode.nodeType !== ELEMENT_NODE) break;
472472

@@ -563,14 +563,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
563563
details = details || {};
564564
details.state = state;
565565

566-
element = element.length ? element[0] : element;
567-
element.setAttribute(NG_ANIMATE_ATTR_NAME, state);
566+
var node = getNode(element);
567+
node.setAttribute(NG_ANIMATE_ATTR_NAME, state);
568568

569-
var oldValue = activeAnimationsLookup.get(element);
569+
var oldValue = activeAnimationsLookup.get(node);
570570
var newValue = oldValue
571571
? extend(oldValue, details)
572572
: details;
573-
activeAnimationsLookup.put(element, newValue);
573+
activeAnimationsLookup.put(node, newValue);
574574
}
575575
}];
576576
}];

src/ngAnimate/animation.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
128128
var refLookup = {};
129129
forEach(animations, function(animation, index) {
130130
var element = animation.element;
131-
var node = element[0];
131+
var node = getNode(element);
132132
var event = animation.event;
133133
var enterOrMove = ['enter', 'move'].indexOf(event) >= 0;
134134
var anchorNodes = animation.structural ? getAnchorNodes(node) : [];

src/ngAnimate/shared.js

+4
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,7 @@ function resolveElementClasses(existing, toAdd, toRemove) {
234234

235235
return classes;
236236
}
237+
238+
function getNode(element) {
239+
return (element instanceof angular.element) ? element[0] : element;
240+
}

test/ngAnimate/animateSpec.js

+26
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,32 @@ describe("animations", function() {
530530
expect(itsOver).toBe(true);
531531
}));
532532

533+
it('should immediately end a parent class-based form animation if a structural child is active',
534+
inject(function($rootScope, $animate, $rootElement, $$rAF, $$AnimateRunner) {
535+
536+
parent.remove();
537+
element.remove();
538+
539+
parent = jqLite('<form></form>');
540+
$rootElement.append(parent);
541+
542+
element = jqLite('<input type="text" name="myInput" />');
543+
544+
$animate.addClass(parent, 'abc');
545+
$rootScope.$digest();
546+
547+
// we do this since the old runner was already closed
548+
overriddenAnimationRunner = new $$AnimateRunner();
549+
550+
$animate.enter(element, parent);
551+
$rootScope.$digest();
552+
553+
$$rAF.flush();
554+
555+
expect(parent.attr('data-ng-animate')).toBeFalsy();
556+
expect(element.attr('data-ng-animate')).toBeTruthy();
557+
}));
558+
533559
it('should not end a pre-digest parent animation if it does not have any classes to add/remove',
534560
inject(function($rootScope, $animate, $$rAF) {
535561

0 commit comments

Comments
 (0)