1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Action links are elements that are used to perform an in-page navigation or
6 // action (e.g. showing a dialog).
8 // They look like normal anchor (<a>) tags as their text color is blue. However,
9 // they're subtly different as they're not initially underlined (giving users a
10 // clue that underlined links navigate while action links don't).
12 // Action links look very similar to normal links when hovered (hand cursor,
13 // underlined). This gives the user an idea that clicking this link will do
14 // something similar to navigation but in the same page.
16 // They can be created in JavaScript like this:
18 // var link = document.createElement('a', 'action-link'); // Note second arg.
20 // or with a constructor like this:
22 // var link = new ActionLink();
24 // They can be used easily from HTML as well, like so:
26 // <a is="action-link">Click me!</a>
28 // NOTE: <action-link> and document.createElement('action-link') don't work.
32 * @extends {HTMLAnchorElement}
34 var ActionLink
= document
.registerElement('action-link', {
36 __proto__
: HTMLAnchorElement
.prototype,
38 /** @this {ActionLink} */
39 createdCallback: function() {
40 // Action links can start disabled (e.g. <a is="action-link" disabled>).
41 this.tabIndex
= this.disabled
? -1 : 0;
43 if (!this.hasAttribute('role'))
44 this.setAttribute('role', 'link');
46 this.addEventListener('keydown', function(e
) {
47 if (!this.disabled
&& e
.keyIdentifier
== 'Enter') {
48 // Schedule a click asynchronously because other 'keydown' handlers
49 // may still run later (e.g. document.addEventListener('keydown')).
50 // Specifically options dialogs break when this timeout isn't here.
51 // NOTE: this affects the "trusted" state of the ensuing click. I
52 // haven't found anything that breaks because of this (yet).
53 window
.setTimeout(this.click
.bind(this), 0);
57 function preventDefault(e
) {
61 function removePreventDefault() {
62 document
.removeEventListener('selectstart', preventDefault
);
63 document
.removeEventListener('mouseup', removePreventDefault
);
66 this.addEventListener('mousedown', function() {
67 // This handlers strives to match the behavior of <a href="...">.
69 // While the mouse is down, prevent text selection from dragging.
70 document
.addEventListener('selectstart', preventDefault
);
71 document
.addEventListener('mouseup', removePreventDefault
);
73 // If focus started via mouse press, don't show an outline.
74 if (document
.activeElement
!= this)
75 this.classList
.add('no-outline');
78 this.addEventListener('blur', function() {
79 this.classList
.remove('no-outline');
83 /** @type {boolean} */
84 set disabled(disabled
) {
86 HTMLAnchorElement
.prototype.setAttribute
.call(this, 'disabled', '');
88 HTMLAnchorElement
.prototype.removeAttribute
.call(this, 'disabled');
89 this.tabIndex
= disabled
? -1 : 0;
92 return this.hasAttribute('disabled');
96 setAttribute: function(attr
, val
) {
97 if (attr
.toLowerCase() == 'disabled')
100 HTMLAnchorElement
.prototype.setAttribute
.apply(this, arguments
);
104 removeAttribute: function(attr
) {
105 if (attr
.toLowerCase() == 'disabled')
106 this.disabled
= false;
108 HTMLAnchorElement
.prototype.removeAttribute
.apply(this, arguments
);