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

Commit b146af1

Browse files
committed
fix(htmlAnchorDirective): don't add event listener if replaced, ignore event if target is different 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 not register an event listener at all if the original directive is replaced with a non-anchor, and will ignore events which do not target the linked element. Closes #4262 Closes #10849
1 parent cea8e75 commit b146af1

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

src/ng/directive/a.js

+6
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,16 @@ var htmlAnchorDirective = valueFn({
1818
compile: function(element, attr) {
1919
if (!attr.href && !attr.xlinkHref && !attr.name) {
2020
return function(scope, element) {
21+
// If the linked element is not an anchor tag anymore, do nothing
22+
if (element[0].nodeName.toLowerCase() !== 'a') return;
23+
2124
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
2225
var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
2326
'xlink:href' : 'href';
2427
element.on('click', function(event) {
28+
// If a different element was clicked, ignore it.
29+
if (element[0] !== event.target) return;
30+
2531
// if we have no href url, then don't navigate anywhere.
2632
if (!element.attr(href)) {
2733
event.preventDefault();

test/ng/directive/aSpec.js

+53
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,25 @@
33
describe('a', function() {
44
var element, $compile, $rootScope;
55

6+
beforeEach(module(function($compileProvider) {
7+
$compileProvider.
8+
directive('linkTo', valueFn({
9+
restrict: 'A',
10+
template: '<div class="my-link"><a href="{{destination}}">{{destination}}</a></div>',
11+
replace: true,
12+
scope: {
13+
destination: '@linkTo'
14+
}
15+
})).
16+
directive('linkNot', valueFn({
17+
restrict: 'A',
18+
template: '<div class="my-link"><a href>{{destination}}</a></div>',
19+
replace: true,
20+
scope: {
21+
destination: '@linkNot'
22+
}
23+
}));
24+
}));
625

726
beforeEach(inject(function(_$compile_, _$rootScope_) {
827
$compile = _$compile_;
@@ -76,6 +95,40 @@ describe('a', function() {
7695
});
7796

7897

98+
it('should not preventDefault if anchor element is replaced with href-containing element', function() {
99+
spyOn(jqLite.prototype, 'on').andCallThrough();
100+
element = $compile('<a link-to="https://www.google.com">')($rootScope);
101+
$rootScope.$digest();
102+
103+
var child = element.children('a');
104+
var preventDefault = jasmine.createSpy('preventDefault');
105+
106+
child.triggerHandler({
107+
type: 'click',
108+
preventDefault: preventDefault
109+
});
110+
111+
expect(preventDefault).not.toHaveBeenCalled();
112+
});
113+
114+
115+
it('should preventDefault if anchor element is replaced with element without href attribute', function() {
116+
spyOn(jqLite.prototype, 'on').andCallThrough();
117+
element = $compile('<a link-not="https://www.google.com">')($rootScope);
118+
$rootScope.$digest();
119+
120+
var child = element.children('a');
121+
var preventDefault = jasmine.createSpy('preventDefault');
122+
123+
child.triggerHandler({
124+
type: 'click',
125+
preventDefault: preventDefault
126+
});
127+
128+
expect(preventDefault).toHaveBeenCalled();
129+
});
130+
131+
79132
if (isDefined(window.SVGElement)) {
80133
describe('SVGAElement', function() {
81134
it('should prevent default action to be executed when href is empty', function() {

0 commit comments

Comments
 (0)