在固定状态栏中显示键盘快捷键
Access Bar 可以显示出页面中已定义的快捷键。快捷键是由网页作者定义的,用来增强页面易用性的键盘快捷方式,让您可以通过键盘快速跳转到特定的链接或表单。(了解快捷键。)
Firefox 支持使用快捷键,但是它不会显示页面中已定义的快捷键。由此,Access Bar 诞生了。
例 5.8.
accessbar.user.js
// ==UserScript==
// @name Access Bar
// @namespace http://diveintogreasemonkey.org/download/
// @description show accesskeys defined on page
// @include *
// ==/UserScript==
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}
var akeys, descriptions, a, desc, label, div;
akeys = document.evaluate(
"//*[@accesskey]",
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null);
if (!akeys.snapshotLength) { return; }
descriptions = new Array();
desc = '';
for (var i = 0; i < akeys.snapshotLength; i++) {
a = akeys.snapshotItem(i);
desctext = '';
if (a.nodeName == 'INPUT') {
label = document.evaluate("//label[@for='" + a.name + "']",
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null).singleNodeValue;
if (label) {
desctext = label.title;
if (!desctext) { desctext = label.textContent; }
}
}
if (!desctext) { desctext = a.textContent; }
if (!desctext) { desctext = a.title; }
if (!desctext) { desctext = a.name; }
if (!desctext) { desctext = a.id; }
if (!desctext) { desctext = a.href; }
if (!desctext) { desctext = a.value; }
desc = '<strong>[' +
a.getAttribute('accesskey').toUpperCase() + ']</strong> ';
if (a.href) {
desc += '<a href="' + a.href + '">' + desctext + '</a>';
} else {
desc += desctext;
}
descriptions.push(desc);
}
descriptions.sort();
div = document.createElement('div');
div.id = 'accessbar-div-0';
desc = '<div><ul><li class="first">' + descriptions[0] + '</li>';
for (var i = 1; i < descriptions.length; i++) {
desc = desc + '<li>' + descriptions[i] + '</li>';
}
desc = desc + '</ul></div>';
div.innerHTML = desc;
document.body.style.paddingBottom = "4em";
window.addEventListener(
"load",
function() {
document.body.appendChild(div);
},
true);
addGlobalStyle(
'#accessbar-div-0 {'+
' position: fixed;' +
' left: 0;' +
' right: 0;' +
' bottom: 0;' +
' top: auto;' +
' border-top: 1px solid silver;' +
' background: black;' +
' color: white;' +
' margin: 1em 0 0 0;' +
' padding: 5px 0 0.4em 0;' +
' width: 100%;' +
' font-family: Verdana, sans-serif;' +
' font-size: small;' +
' line-height: 160%;' +
'}' +
'#accessbar-div-0 a,' +
'#accessbar-div-0 li,' +
'#accessbar-div-0 span,' +
'#accessbar-div-0 strong {' +
' background-color: transparent;' +
' color: white;' +
'}' +
'#accessbar-div-0 div {' +
' margin: 0 1em 0 1em;' +
'}' +
'#accessbar-div-0 div ul {' +
' margin-left: 0;' +
' margin-bottom: 5px;' +
' padding-left: 0;' +
' display: inline;' +
'}' +
'#accessbar-div-0 div ul li {' +
' margin-left: 0;' +
' padding: 3px 15px;' +
' border-left: 1px solid silver;' +
' list-style: none;' +
' display: inline;' +
'}' +
'#accessbar-div-0 div ul li.first {' +
' border-left: none;' +
' padding-left: 0;' +
'}');这段代码分为六个步骤:
- 定义一个辅助函数,
addGlobalStyle - 获取包含有
accesskey熟悉的页面元素 - 遍历这些元属,然后为每个元素决定最恰当的文本描述
- 为已启用
accesskey的元素,构建一个有序的链接列表。 - 把这个有序列表加到页面中,使用标准的
ul和li元素 - 样式化此列表,让它看起来像一个固定在视图底部的伪状态栏
首先,我需要一个辅助函数 addGlobalStyle,注入我自己的 CSS 样式(在第六步中会用到)。关于此模式的更多信息,请阅读添加 CSS 样式。
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}第二步,找出页面中有 accesskey 属性的全部元素。在 Firefox 的 XPath 支持下很容易办到。注释:如果 XPath 查询返回空集,我可以保证,那是因为这里没有什么需要显示的。此模式的更多信息,请阅读操作所有有特定属性的元素。
var akeys, descriptions, a, i, desc, label, div;
akeys = document.evaluate(
"//*[@accesskey]",
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null);
if (!akeys.snapshotLength) { return; }第三步,构造每个启用 accesskey 元素的适当描述的清单列表,这是脚本中最复杂的一段。问题是 accesskey 属性可以出现在许多不同的 HTML 元素中。
表单中的 input 元素可以定义 accesskey。input 元素可有或可无相关联的 label,它包含与 input 域有关联的文字标签。如果有,label 可能包含 title 属性,提供关于 input 域的更多详细信息。或者 label 属性只包含文本。或者 input 元素可能没有关联的标签,这中情况下,我最好使用 input 元素的 value 属性。
另一方面,label 自己也可以定义 accesskey,代替标签描述的 input 元素。再者,我会查找 label 元素的 title 属性作为描述,如果 title 不存在,就退而使用标签的文本。
链接同样可以定义 accesskey 属性。如果有,选择链接的文本是理所当然的。但是如果链接不包含文本(例如,它只包含一张图片),那么下个地方就是找链接的 title 属性。如果链接既没有文本,又没有 title,我就退而使用链接的 name 属性,要是还是不成,就用链接的 id 属性。
Ain't heuristics a bitch? 这是完整的算法。记着,akeys 是一个 XPathResult 对象,所以我要调用 akeys.snapshotItem(i) 来得到每个结果。
descriptions = new Array();
desc = '';
for (var i = 0; i < akeys.snapshotLength; i++) {
a = akeys.snapshotItem(i);
desctext = '';
if (a.nodeName == 'INPUT') {
label = document.evaluate("//label[@for='" + a.name + "']",
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null).singleNodeValue;
if (label) {
desctext = label.title;
if (!desctext) { desctext = label.textContent; }
}
}
if (!desctext) { desctext = a.textContent; }
if (!desctext) { desctext = a.title; }
if (!desctext) { desctext = a.name; }
if (!desctext) { desctext = a.id; }
if (!desctext) { desctext = a.href; }
if (!desctext) { desctext = a.value; }
desc = '<strong>[' +
a.getAttribute('accesskey').toUpperCase() + ']</strong> ';
if (a.href) {
desc += '<a href="' + a.href + '">' + desctext + '</a>';
} else {
desc += desctext;
}
descriptions.push(desc);
}第四步比较简单,因为 Javascript 数组有个 sort 方法,可以给数组在原地排序。
descriptions.sort();
第五步创建了 HTML 代码,用来渲染启用 accesskey 的元素列表。我创建一个外包装
<div>,为字符串形式的快捷键列表构造 HTML,设置外包装 <div> 的 innerHTML 属性,最后把它加到页面的末尾。因为当用户脚本执行时,对页面的复杂改动应该在页面加载完成以后,所以我使用 window.addEventListener 加入 onload 事件,来向页面中加入 <div>。
有关 innerHTML 的用法,请阅读快速插入复杂的 HTML, 有关 window.addEventListener 的用法,请阅读处理已渲染的页面。
div = document.createElement('div');
div.id = 'accessbar-div-0';
desc = '<div><ul><li class="first">' + descriptions[0] + '</li>';
for (var i = 1; i < descriptions.length; i++) {
desc = desc + '<li>' + descriptions[i] + '</li>';
}
desc = desc + '</ul></div>';
div.innerHTML = desc;
document.body.style.paddingBottom = "4em";
window.addEventListener(
"load",
function() {
document.body.appendChild(div);
},
true);最后,第六步,为了使插入的 HTML 看起来更协调,我在页面中加入了自己的 CSS 声明。(很明显,它将作为固定的黑色工具栏出现在页面的底部。滚动页面的时候,它仍然在那个位置,这是因为 Firefox 支持 position:fixed显示类型。)更多的信息,请阅读添加 CSS 样式。
addGlobalStyle(
'#accessbar-div-0 {'+
' position: fixed;' +
' left: 0;' +
' right: 0;' +
' bottom: 0;' +
' top: auto;' +
' border-top: 1px solid silver;' +
' background: black;' +
' color: white;' +
' margin: 1em 0 0 0;' +
' padding: 5px 0 0.4em 0;' +
' width: 100%;' +
' font-family: Verdana, sans-serif;' +
' font-size: small;' +
' line-height: 160%;' +
'}' +
'#accessbar-div-0 a,' +
'#accessbar-div-0 li,' +
'#accessbar-div-0 span,' +
'#accessbar-div-0 strong {' +
' background-color: transparent;' +
' color: white;' +
'}' +
'#accessbar-div-0 div {' +
' margin: 0 1em 0 1em;' +
'}' +
'#accessbar-div-0 div ul {' +
' margin-left: 0;' +
' margin-bottom: 5px;' +
' padding-left: 0;' +
' display: inline;' +
'}' +
'#accessbar-div-0 div ul li {' +
' margin-left: 0;' +
' padding: 3px 15px;' +
' border-left: 1px solid silver;' +
' list-style: none;' +
' display: inline;' +
'}' +
'#accessbar-div-0 div ul li.first {' +
' border-left: none;' +
' padding-left: 0;' +
'}');