From b1601c3957ecf6d080ca1f176bf1d09a11fba6f5 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Fri, 23 Jan 2015 15:08:03 -0500 Subject: [PATCH] fix(htmlAnchorDirective): remove event listener if target is not element Previously, when an `a` tag element used a directive with a replacing template, and did not include an `href` or `name` attribute before linkage, the anchor directive would always prevent default. Now, the anchor directive will cancel its event listener if the linked element is not the same as the target element of the event. Closes #4262 --- src/ng/directive/a.js | 6 +++++ test/ng/directive/aSpec.js | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/ng/directive/a.js b/src/ng/directive/a.js index 23096147145f..581f2fec5b12 100644 --- a/src/ng/directive/a.js +++ b/src/ng/directive/a.js @@ -18,10 +18,16 @@ var htmlAnchorDirective = valueFn({ compile: function(element, attr) { if (!attr.href && !attr.xlinkHref && !attr.name) { return function(scope, element) { + // If the linked element is not an anchor tag anymore, do nothing + if (element[0].nodeName.toLowerCase() !== 'a') return; + // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? 'xlink:href' : 'href'; element.on('click', function(event) { + // If a different element was clicked, ignore it. + if (element[0] !== event.target) return; + // if we have no href url, then don't navigate anywhere. if (!element.attr(href)) { event.preventDefault(); diff --git a/test/ng/directive/aSpec.js b/test/ng/directive/aSpec.js index 05f3c8b2eba7..460ea88bc2ff 100644 --- a/test/ng/directive/aSpec.js +++ b/test/ng/directive/aSpec.js @@ -3,6 +3,25 @@ describe('a', function() { var element, $compile, $rootScope; + beforeEach(module(function($compileProvider) { + $compileProvider. + directive('linkTo', valueFn({ + restrict: 'A', + template: '', + replace: true, + scope: { + destination: '@linkTo' + } + })). + directive('linkNot', valueFn({ + restrict: 'A', + template: '', + replace: true, + scope: { + destination: '@linkNot' + } + })); + })); beforeEach(inject(function(_$compile_, _$rootScope_) { $compile = _$compile_; @@ -76,6 +95,40 @@ describe('a', function() { }); + it('should not preventDefault if anchor element is replaced with href-containing element', function() { + spyOn(jqLite.prototype, 'on').andCallThrough(); + element = $compile('')($rootScope); + $rootScope.$digest(); + + var child = element.children('a'); + var preventDefault = jasmine.createSpy('preventDefault'); + + child.triggerHandler({ + type: 'click', + preventDefault: preventDefault + }); + + expect(preventDefault).not.toHaveBeenCalled(); + }); + + + it('should preventDefault if anchor element is replaced with element without href attribute', function() { + spyOn(jqLite.prototype, 'on').andCallThrough(); + element = $compile('')($rootScope); + $rootScope.$digest(); + + var child = element.children('a'); + var preventDefault = jasmine.createSpy('preventDefault'); + + child.triggerHandler({ + type: 'click', + preventDefault: preventDefault + }); + + expect(preventDefault).toHaveBeenCalled(); + }); + + if (isDefined(window.SVGElement)) { describe('SVGAElement', function() { it('should prevent default action to be executed when href is empty', function() {