Skip to content

Commit 6fa75b7

Browse files
committed
fix(viewSwitcher): do not finish transition from bubbled transitionend events
Closes #3006. Closes #3063.
1 parent 83cc181 commit 6fa75b7

File tree

4 files changed

+73
-5
lines changed

4 files changed

+73
-5
lines changed

Diff for: js/angular/controller/navViewController.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
2929
var transitionDuration, transitionTiming;
3030

3131
self.scope = $scope;
32+
self.element = $element;
3233

3334
self.init = function() {
3435
var navViewName = $attrs.name || '';
@@ -399,7 +400,7 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
399400
ionic.offGesture(deregDragStart, 'dragstart', onDragStart);
400401
ionic.offGesture(deregDrag, 'drag', onDrag);
401402
ionic.offGesture(deregRelease, 'release', onRelease);
402-
viewTransition = associatedNavBarCtrl = null;
403+
self.element = viewTransition = associatedNavBarCtrl = null;
403404
});
404405
};
405406

Diff for: js/angular/service/viewSwitcher.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
170170

171171
if (viewTransition.shouldAnimate) {
172172
// attach transitionend events (and fallback timer)
173-
enteringEle.on(TRANSITIONEND_EVENT, transitionComplete);
173+
enteringEle.on(TRANSITIONEND_EVENT, completeOnTransitionEnd);
174174
enteringEle.data(DATA_FALLBACK_TIMER, $timeout(transitionComplete, defaultTimeout));
175175
$ionicClickBlock.show(defaultTimeout);
176176
}
@@ -209,7 +209,7 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
209209
run: viewTransition.run,
210210
cancel: function(shouldAnimate) {
211211
if (shouldAnimate) {
212-
enteringEle.on(TRANSITIONEND_EVENT, cancelTransition);
212+
enteringEle.on(TRANSITIONEND_EVENT, cancelOnTransitionEnd);
213213
enteringEle.data(DATA_FALLBACK_TIMER, $timeout(cancelTransition, defaultTimeout));
214214
$ionicClickBlock.show(defaultTimeout);
215215
} else {
@@ -248,11 +248,17 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
248248
}
249249
}
250250

251+
// Make sure that transitionend events bubbling up from children won't fire
252+
// transitionComplete. Will only go forward if ev.target == the element listening.
253+
function completeOnTransitionEnd(ev) {
254+
if (ev.target !== this) return;
255+
transitionComplete();
256+
}
251257
function transitionComplete() {
252258
if (transitionComplete.x) return;
253259
transitionComplete.x = true;
254260

255-
enteringEle.off(TRANSITIONEND_EVENT, transitionComplete);
261+
enteringEle.off(TRANSITIONEND_EVENT, completeOnTransitionEnd);
256262
$timeout.cancel(enteringEle.data(DATA_FALLBACK_TIMER));
257263
leavingEle && $timeout.cancel(leavingEle.data(DATA_FALLBACK_TIMER));
258264

@@ -279,10 +285,16 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
279285
nextTransition = nextDirection = enteringView = leavingView = enteringEle = leavingEle = null;
280286
}
281287

288+
// Make sure that transitionend events bubbling up from children won't fire
289+
// transitionComplete. Will only go forward if ev.target == the element listening.
290+
function cancelOnTransitionEnd(ev) {
291+
if (ev.target !== this) return;
292+
cancelTransition();
293+
}
282294
function cancelTransition() {
283295
navViewAttr(enteringEle, VIEW_STATUS_CACHED);
284296
navViewAttr(leavingEle, VIEW_STATUS_ACTIVE);
285-
enteringEle.off(TRANSITIONEND_EVENT, cancelTransition);
297+
enteringEle.off(TRANSITIONEND_EVENT, cancelOnTransitionEnd);
286298
$timeout.cancel(enteringEle.data(DATA_FALLBACK_TIMER));
287299
ionicViewSwitcher.transitionEnd([navViewCtrl]);
288300
}

Diff for: js/utils/dom.js

+1
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@
297297
}
298298
}
299299
}
300+
300301
};
301302

302303
//Shortcuts

Diff for: test/unit/angular/service/viewSwitcher.unit.js

+54
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,58 @@ describe('Ionic View Switcher', function() {
225225
expect(switcher.enteringEle().data('$accessed')).toBeDefined();
226226
}));
227227

228+
it('should end the transition on transitionend', inject(function($ionicViewSwitcher, $rootScope, $ionicConfig, $compile) {
229+
$ionicConfig.views.transition('test');
230+
$ionicConfig.transitions.views.test = function() {
231+
return { run: angular.noop, shouldAnimate: true };
232+
};
233+
var afterEnterSpy = jasmine.createSpy('afterEnter');
234+
var navViewCtrl = setup();
235+
var enteringEle = $compile('<enter>')($rootScope);
236+
navViewCtrl.element.append(enteringEle);
237+
238+
enteringEle.data('$eleId', 'foo');
239+
var switcher = $ionicViewSwitcher.create(navViewCtrl, {}, {
240+
viewId: 'foo'
241+
});
242+
243+
switcher.loadViewElements({});
244+
$rootScope.$on('$ionicView.afterEnter', afterEnterSpy);
245+
switcher.transition('forward');
246+
247+
expect(afterEnterSpy).not.toHaveBeenCalled();
248+
enteringEle.triggerHandler('transitionend');
249+
expect(afterEnterSpy).toHaveBeenCalled();
250+
}));
251+
252+
it('should not end the transition on bubbled transitionend', inject(function($ionicViewSwitcher, $rootScope, $ionicConfig, $compile) {
253+
$ionicConfig.views.transition('test');
254+
$ionicConfig.transitions.views.test = function() {
255+
return { run: angular.noop, shouldAnimate: true };
256+
};
257+
var afterEnterSpy = jasmine.createSpy('afterEnter');
258+
var navViewCtrl = setup();
259+
var enteringEle = $compile('<enter>')($rootScope);
260+
navViewCtrl.element.append(enteringEle);
261+
262+
var enteringEleChild = angular.element('<child>');
263+
enteringEle.append(enteringEleChild);
264+
265+
enteringEle.data('$eleId', 'foo');
266+
var switcher = $ionicViewSwitcher.create(navViewCtrl, {}, {
267+
viewId: 'foo'
268+
});
269+
270+
switcher.loadViewElements({});
271+
$rootScope.$on('$ionicView.afterEnter', afterEnterSpy);
272+
switcher.transition('forward');
273+
274+
expect(afterEnterSpy).not.toHaveBeenCalled();
275+
enteringEle.triggerHandler({
276+
type: 'transitionend',
277+
target: enteringEleChild[0]
278+
});
279+
expect(afterEnterSpy).not.toHaveBeenCalled();
280+
}));
281+
228282
});

0 commit comments

Comments
 (0)