Skip to content

Commit 92ce981

Browse files
author
Peter Deak
committed
fix(ngAnimate): Correctly retain existing styles while animating, and also ensure that outdated styles aren't restored on completion. Fixes angular#4869.
1 parent fa82a31 commit 92ce981

File tree

2 files changed

+58
-15
lines changed

2 files changed

+58
-15
lines changed

src/ngAnimate/animate.js

+15-15
Original file line numberDiff line numberDiff line change
@@ -842,13 +842,6 @@ angular.module('ngAnimate', ['ng'])
842842
}, 10, false);
843843
}
844844

845-
function applyStyle(node, style) {
846-
var oldStyle = node.getAttribute('style') || '';
847-
var newStyle = (oldStyle.length > 0 ? '; ' : '') + style;
848-
node.setAttribute('style', newStyle);
849-
return oldStyle;
850-
}
851-
852845
function getElementAnimationDetails(element, cacheKey) {
853846
var data = cacheKey ? lookupCache[cacheKey] : null;
854847
if(!data) {
@@ -1002,10 +995,9 @@ angular.module('ngAnimate', ['ng'])
1002995
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000;
1003996
var startTime = Date.now();
1004997
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
1005-
var formerStyle;
1006998
var ii = data.ii;
1007999

1008-
var applyFallbackStyle, style = '';
1000+
var applyFallbackStyle, style = '', appliedStyles = [];
10091001
if(timings.transitionDuration > 0) {
10101002
node.style[TRANSITION_PROP + PROPERTY_KEY] = '';
10111003

@@ -1015,6 +1007,8 @@ angular.module('ngAnimate', ['ng'])
10151007
var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'clip';
10161008
style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ';
10171009
style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; ';
1010+
appliedStyles.push(CSS_PREFIX + 'transition-property');
1011+
appliedStyles.push(CSS_PREFIX + 'transition-duration');
10181012
}
10191013
}
10201014

@@ -1027,16 +1021,19 @@ angular.module('ngAnimate', ['ng'])
10271021

10281022
style += CSS_PREFIX + 'transition-delay: ' +
10291023
prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
1024+
appliedStyles.push(CSS_PREFIX + 'transition-delay');
10301025
}
10311026

10321027
if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
10331028
style += CSS_PREFIX + 'animation-delay: ' +
10341029
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
1030+
appliedStyles.push(CSS_PREFIX + 'animation-delay');
10351031
}
10361032
}
10371033

1038-
if(style.length > 0) {
1039-
formerStyle = applyStyle(node, style);
1034+
if(appliedStyles.length > 0) {
1035+
var oldStyle = node.getAttribute('style') || '';
1036+
node.setAttribute('style', oldStyle + '; ' + style);
10401037
}
10411038

10421039
element.on(css3AnimationEvents, onAnimationProgress);
@@ -1049,10 +1046,13 @@ angular.module('ngAnimate', ['ng'])
10491046
element.off(css3AnimationEvents, onAnimationProgress);
10501047
element.removeClass(activeClassName);
10511048
animateClose(element, className);
1052-
if(formerStyle != null) {
1053-
formerStyle.length > 0 ?
1054-
node.setAttribute('style', formerStyle) :
1055-
node.removeAttribute('style');
1049+
for (var i in appliedStyles) {
1050+
1051+
// removeProperty is not suppoerted in IE < 9, and FF
1052+
// doesn't allow clearing style properties via indexing
1053+
node.style.removeProperty ?
1054+
node.style.removeProperty(appliedStyles[i]) :
1055+
node.style[appliedStyles[i]] = "";
10561056
}
10571057
};
10581058

test/ngAnimate/animateSpec.js

+43
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,22 @@ describe("ngAnimate", function() {
420420
expect(element.children().length).toBe(0);
421421
}));
422422

423+
it("should retain existing styles of the animated element",
424+
inject(function($animate, $rootScope, $sniffer, $timeout) {
425+
426+
element.append(child);
427+
child.attr('style', 'width: 20px');
428+
429+
$animate.addClass(child, 'ng-hide');
430+
$animate.leave(child);
431+
if($sniffer.transitions) {
432+
$rootScope.$digest();
433+
$timeout.flush();
434+
}
435+
436+
expect(child.attr('style')).toMatch(/width: 20px/i);
437+
}));
438+
423439
it("should call the cancel callback when another animation is called on the same element",
424440
inject(function($animate, $rootScope, $sniffer, $timeout) {
425441

@@ -975,6 +991,33 @@ describe("ngAnimate", function() {
975991
expect(element).toBeShown();
976992
}));
977993

994+
it("should NOT overwrite styles with outdated values when animation completes",
995+
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
996+
997+
var style = '-webkit-transition-duration: 1s, 2000ms, 1s;' +
998+
'-webkit-transition-property: height, left, opacity;' +
999+
'transition-duration: 1s, 2000ms, 1s;' +
1000+
'transition-property: height, left, opacity;';
1001+
1002+
ss.addRule('.ng-hide-add', style);
1003+
ss.addRule('.ng-hide-remove', style);
1004+
1005+
element = $compile(html('<div style="width: 100px">foo</div>'))($rootScope);
1006+
element.addClass('ng-hide');
1007+
1008+
$animate.removeClass(element, 'ng-hide');
1009+
1010+
if ($sniffer.transitions) {
1011+
$timeout.flush();
1012+
var now = Date.now();
1013+
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
1014+
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
1015+
element.css('width', '200px');
1016+
browserTrigger(element,'transitionend', { timeStamp: now + 2000, elapsedTime: 2 });
1017+
expect(element.css('width')).toBe("200px");
1018+
}
1019+
}));
1020+
9781021
it("should animate for the highest duration",
9791022
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
9801023
var style = '-webkit-transition:1s linear all 2s;' +

0 commit comments

Comments
 (0)