Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit c42d0a0

Browse files
Peter Deakmatsko
Peter Deak
authored andcommitted
fix(ngAnimate): correctly retain and restore existing styles during and after animation
Closes #4869
1 parent 3fbb25e commit c42d0a0

File tree

2 files changed

+60
-15
lines changed

2 files changed

+60
-15
lines changed

src/ngAnimate/animate.js

+10-15
Original file line numberDiff line numberDiff line change
@@ -876,13 +876,6 @@ angular.module('ngAnimate', ['ng'])
876876
}, 10, false);
877877
}
878878

879-
function applyStyle(node, style) {
880-
var oldStyle = node.getAttribute('style') || '';
881-
var newStyle = (oldStyle.length > 0 ? '; ' : '') + style;
882-
node.setAttribute('style', newStyle);
883-
return oldStyle;
884-
}
885-
886879
function getElementAnimationDetails(element, cacheKey) {
887880
var data = cacheKey ? lookupCache[cacheKey] : null;
888881
if(!data) {
@@ -1057,17 +1050,18 @@ angular.module('ngAnimate', ['ng'])
10571050
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000;
10581051
var startTime = Date.now();
10591052
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
1060-
var formerStyle;
10611053
var ii = data.ii;
10621054

1063-
var applyFallbackStyle, style = '';
1055+
var applyFallbackStyle, style = '', appliedStyles = [];
10641056
if(timings.transitionDuration > 0) {
10651057
var propertyStyle = timings.transitionPropertyStyle;
10661058
if(propertyStyle.indexOf('all') == -1) {
10671059
applyFallbackStyle = true;
10681060
var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'border-spacing';
10691061
style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ';
10701062
style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; ';
1063+
appliedStyles.push(CSS_PREFIX + 'transition-property');
1064+
appliedStyles.push(CSS_PREFIX + 'transition-duration');
10711065
}
10721066
} else {
10731067
unblockKeyframeAnimations(element);
@@ -1082,16 +1076,19 @@ angular.module('ngAnimate', ['ng'])
10821076

10831077
style += CSS_PREFIX + 'transition-delay: ' +
10841078
prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
1079+
appliedStyles.push(CSS_PREFIX + 'transition-delay');
10851080
}
10861081

10871082
if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
10881083
style += CSS_PREFIX + 'animation-delay: ' +
10891084
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
1085+
appliedStyles.push(CSS_PREFIX + 'animation-delay');
10901086
}
10911087
}
10921088

1093-
if(style.length > 0) {
1094-
formerStyle = applyStyle(node, style);
1089+
if(appliedStyles.length > 0) {
1090+
var oldStyle = node.getAttribute('style') || '';
1091+
node.setAttribute('style', oldStyle + ' ' + style);
10951092
}
10961093

10971094
element.on(css3AnimationEvents, onAnimationProgress);
@@ -1104,10 +1101,8 @@ angular.module('ngAnimate', ['ng'])
11041101
element.off(css3AnimationEvents, onAnimationProgress);
11051102
element.removeClass(activeClassName);
11061103
animateClose(element, className);
1107-
if(formerStyle != null) {
1108-
formerStyle.length > 0 ?
1109-
node.setAttribute('style', formerStyle) :
1110-
node.removeAttribute('style');
1104+
for (var i in appliedStyles) {
1105+
node.style.removeProperty(appliedStyles[i]);
11111106
}
11121107
};
11131108

test/ngAnimate/animateSpec.js

+50
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,27 @@ 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+
$rootScope.$digest();
432+
433+
if($sniffer.transitions) {
434+
$timeout.flush();
435+
436+
//this is to verify that the existing style is appended with a semicolon automatically
437+
expect(child.attr('style')).toMatch(/width: 20px;.+?/i);
438+
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
439+
}
440+
441+
expect(child.attr('style')).toMatch(/width: 20px/i);
442+
}));
443+
423444
it("should call the cancel callback when another animation is called on the same element",
424445
inject(function($animate, $rootScope, $sniffer, $timeout) {
425446

@@ -975,6 +996,35 @@ describe("ngAnimate", function() {
975996
expect(element).toBeShown();
976997
}));
977998

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

0 commit comments

Comments
 (0)