例 5.7.
zoomtextarea.user.js
// ==UserScript==
// @name Zoom Textarea
// @namespace http://diveintogreasemonkey.org/download/
// @description add controls to zoom textareas
// @include *
// ==/UserScript==
var textareas, textarea;
textareas = document.getElementsByTagName('textarea');
if (!textareas.length) { return; }
function textarea_zoom_in(event) {
var link, textarea, s;
link = event.currentTarget;
textarea = link._target;
s = getComputedStyle(textarea, "");
textarea.style.width = (parseFloat(s.width) * 1.5) + "px";
textarea.style.height = (parseFloat(s.height) * 1.5) + "px";
textarea.style.fontSize = (parseFloat(s.fontSize) + 7.0) + 'px';
event.preventDefault();
}
function textarea_zoom_out(event) {
var link, textarea, s;
link = event.currentTarget;
textarea = link._target;
s = getComputedStyle(textarea, "");
textarea.style.width = (parseFloat(s.width) * 2.0 / 3.0) + "px";
textarea.style.height = (parseFloat(s.height) * 2.0 / 3.0) + "px";
textarea.style.fontSize = (parseFloat(s.fontSize) - 7.0) + "px";
event.preventDefault();
}
function createButton(target, func, title, width, height, src) {
var img, button;
img = document.createElement('img');
img.width = width;
img.height = height;
img.style.borderTop = img.style.borderLeft = "1px solid #ccc";
img.style.borderRight = img.style.borderBottom = "1px solid #888";
img.style.marginRight = "2px";
img.src = src;
button = document.createElement('a');
button._target = target;
button.title = title;
button.href = '#';
button.onclick = func;
button.appendChild(img);
return button;
}
for (var i = 0; i < textareas.length; i++) {
textarea = textareas[i];
textarea.parentNode.insertBefore(
createButton(
textarea,
textarea_zoom_in,
'Increase textarea size',
20,
20,
'data:image/gif;base64,'+
'R0lGODlhFAAUAOYAANPS1tva3uTj52NjY2JiY7KxtPf3%2BLOys6WkpmJiYvDw8fX19vb'+
'296Wlpre3uEZFR%2B%2Fv8aqpq9va3a6tr6Kho%2Bjo6bKytZqZml5eYMLBxNra21JSU3'+
'Jxc3RzdXl4emJhZOvq7KamppGQkr29vba2uGBgYdLR1dLS0lBPUVRTVYB%2Fgvj4%2BYK'+
'Bg6SjptrZ3cPDxb69wG1tbsXFxsrJy29vccDAwfT09VJRU6uqrFlZW6moqo2Mj4yLjLKy'+
's%2Fj4%2BK%2Busu7t783Nz3l4e19fX7u6vaalqNPS1MjHylZVV318ftfW2UhHSG9uccv'+
'KzfHw8qqqrNPS1eXk5tvb3K%2BvsHNydeLi40pKS2JhY2hnalpZWlVVVtDQ0URDRJmZm5'+
'mYm11dXp2cnm9vcFxcXaOjo0pJSsC%2FwuXk6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'+
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC'+
'H5BAAAAAAALAAAAAAUABQAAAeagGaCg4SFhoeIiYqKTSQUFwgwi4JlB0pOCkEiRQKKRxM'+
'gKwMGDFEqBYpPRj4GAwwLCkQsijwQBAQJCUNSW1mKSUALNiVVJzIvSIo7GRUaGzUOPTpC'+
'igUeMyNTIWMHGC2KAl5hCBENYDlcWC7gOB1LDzRdWlZMAZOEJl83VPb3ggAfUnDo5w%2F'+
'AFRQxJPj7J4aMhYWCoPyASFFRIAA7'),
textarea);
textarea.parentNode.insertBefore(
createButton(
textarea,
textarea_zoom_out,
'Decrease textarea size',
20,
20,
'data:image/gif;base64,'+
'R0lGODlhFAAUAOYAANPS1uTj59va3vDw8bKxtGJiYrOys6Wkpvj4%2BPb29%2FX19mJiY'+
'%2Ff3%2BKqqrLe3uLKytURDRFpZWqmoqllZW9va3aOjo6Kho4KBg729vWJhZK%2BuskZF'+
'R4B%2FgsLBxHNydY2Mj%2Ff396amptLS0l9fX9fW2dDQ0W1tbpmZm8DAwfT09fHw8n18f'+
'uLi49LR1V5eYOjo6VBPUa6tr769wEhHSNra20pJStPS1KuqrNPS1ZmYm%2B7t77Kys8rJ'+
'y%2Fj4%2BaSjpm9uca%2BvsMjHyqalqHRzdVJRU8PDxVRTVcvKzc3Nz0pKS9rZ3evq7MC'+
'%2FwsXFxp2cnnl4e1VVVu%2Fv8ba2uM7Oz29vcbu6vZqZmnJxc9vb3PHx8uXk5mhnamJh'+
'Y1xcXZGQklZVV29vcHl4eoyLjKqpq6Wlpl1dXuXk6AAAAAAAAAAAAAAAAAAAAAAAAAAAA'+
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'+
'AAACH5BAAAAAAALAAAAAAUABQAAAeZgGaCg4SFhoeIiYqKR1IWVgcyi4JMBiQqA0heQgG'+
'KQTFLPQgMCVocBIoNNqMgCQoDVReKYlELCwUFI1glEYorOgopWSwiTUVfih8dLzRTKA47'+
'Ek%2BKBGE8GEAhFQYuPooBOWAHY2ROExBbSt83QzMbVCdQST8Ck4QtZUQe9faCABlGrvD'+
'rB4ALDBMU%2BvnrUuOBQkE4NDycqCgQADs%3D'),
textarea);
textarea.parentNode.insertBefore(
document.createElement('br'),
textarea);
}这段代码看起来很复杂,而且它确实复杂,但是有特殊原因的。看起来很复杂是因为它中间有大段的乱码似的字符串。这些是的 data: URIs,它们看起来像地狱却很容易生成。真正复杂的地方还在别处。
首先,我获取了页面上所有的 <textarea> 元素集。此模式的详情,请阅读操作特定 HTML 元素的所有实例。如果没有,那就没必要继续了,所以 return(返回)。
textareas = document.getElementsByTagName('textarea');
if (!textareas.length) { return; }现在,我们跑下题。我们的“工具栏”看起来像是一行按钮,但是每个按钮就是看起来可以点的某些图片,外面包上一个能执行我们的 Javascript 函数的链接。
由于我们要创建多个按钮(尽管这个脚本只有两个按钮,但是您可以便利地扩展出更多功能),我创建了一个函数来封装所有的制造按钮的逻辑。
function createButton(target, func, title, width, height, src) {The createButton function takes 6 arguments:
-
target - 元素对象,按钮控制的
<textarea>元素 -
func - 函数对象,当用户用鼠标点击或键盘激活这个按钮时,调用的 Javascript 函数
-
title - 字符串,鼠标移动到按钮上显示的提示文字
-
width - 整数,按钮的宽度。它应该与
src参数提供的图形实际宽度一致。 -
height - 整数,按钮的高度。它应该与
src参数提供的图形实际高度一致。 -
src - 字符串,URL、路径或者按钮图形的
data:URI
创建一个按钮分为两个步骤:创建 <img> 元素,然后创建其外的 <a> 元素。
我调用 document.createElement 来创建 <img> 元素,并且设置了一小部分属性,包含样式的属性。关于此模式的更多信息,请阅读设置元素样式。
img = document.createElement('img');
img._target = target;
img.width = width;
img.height = height;
img.style.borderTop = img.style.borderLeft = "1px solid #ccc";
img.style.borderRight = img.style.borderBottom = "1px solid #888";
img.style.marginRight = "2px";
img.src = src;这里有个很酷的技巧,您可以使用下面的语法同时为两个属性赋上相同的值:
img.style.borderTop = img.style.borderLeft = "1px solid #ccc";
OK, 继续前进。创建按钮的第二部分是创建链接(<a> 元素),然后把 <img> 元素放在里面。
button = document.createElement('a');
button._target = target;
button.title = title;
button.href = '#';
button.onclick = func;
button.appendChild(img);我想指出两点。第一,我需要赋给链接伪造的 href 属性,否则 Firefox 会把它当作命名锚并且不会把它加到 tab 索引中(也就是您无法通过 tab 移动它上面,也就无法通过键盘使用)。第二,我设置了 _target 属性来保存目标 <textarea> 的引用。这在 Javascript 中是完全合法的;您只需要给新属性赋值,就可以给对象创建新属性。稍后,在 onclick 的事件句柄中,我会访问这个自定义的 _target 属性。
现在,让我们跳回到 onclick 句柄。每个句柄都是一个函数,有一个参数:event。
function textarea_zoom_in(event)
event 对象有许多属性,现在我感兴趣的只有一个:currentTarget。
link = event.currentTarget;
如果您阅读过关于 Event 对象的文档,您可以读到很多目标相关的属性,包含一个简单的:target。您可能很想使用 event.target 去获取已访问的链接,但是它表现(依我看来)不一致。当用户 tab 到按钮上然后敲回车键,event.target 是这个链接,但是当用户用鼠标点击按钮时,event.target 是链接中的图片!我想这肯定有个合理的解释,但是它超过了我对 DOM 事件模型的理解层次。无论如何,event.currentTarget 在所有情况下都返回链接,所以我使用它。
下一步我取回了目前打算缩放的 <textarea> 的引用,通过自定义的 _target 属性,我在创建按钮时设置了这个属性。
textarea = link._target;
现在好戏刚刚开始。(您认为您已经找到乐趣了吗!)我需要获得 <textarea> 当前的长宽和字体大小,这样我就可以把它们放大。简单的从textarea.style (textarea.style.width, textarea.style.height 和 textarea.style.fontSize)中获得适合的属性并不管用,因为只有在 <textarea> 的 style 属性中定义它们的时候才能得到值。这不是我想要的;我要得到真实的当前样式。我需要的是 getComputedStyle. 关于此函数的更多信息,请阅读获取元素样式。
s = getComputedStyle(textarea, ""); textarea.style.width = (parseFloat(s.width) * 1.5) + "px"; textarea.style.height = (parseFloat(s.height) * 1.5) + "px"; textarea.style.fontSize = (parseFloat(s.fontSize) + 7.0) + 'px';
最后,您还记得按钮链接上为了键盘易用新而加的伪造的 href 值吗?嗯,它已经变成了烦恼,因为 Firefox 执行完 onclick 句柄后,它就会尝试跳转到这个链接。因为它指向了不存在的锚,无论按钮在什么地方,Firefox 都会跳到页面的顶端。这是件恼人的事,要阻止它,我需要在完成 onclick 句柄前调用 event.preventDefault()。
event.preventDefault();
脚本的其余部分很简单。我遍历了所有的 <textarea> 元素,为他们各自创建缩放按钮(每个都有它自己的 onclick 句柄和按钮图片),然后在 <textarea> 前插入缩放按钮。对于每张图片,我使用 data: URI 来创建内嵌的图片,所以用户不需要访问远端服务器去获取按钮图片。关于此模式的更多信息,请阅读在元素前插入内容和在没有服务器的情况下添加图片。
for (var i = 0; i < textareas.length; i++) {
textarea = textareas[i];
textarea.parentNode.insertBefore(
createButton(
textarea,
textarea_zoom_in,
'Increase textarea size',
20,
20,
'data:image/gif;base64,'+
'R0lGODlhFAAUAOYAANPS1tva3uTj52NjY2JiY7KxtPf3%2BLOys6WkpmJiYvDw8fX19vb'+
'296Wlpre3uEZFR%2B%2Fv8aqpq9va3a6tr6Kho%2Bjo6bKytZqZml5eYMLBxNra21JSU3'+
'Jxc3RzdXl4emJhZOvq7KamppGQkr29vba2uGBgYdLR1dLS0lBPUVRTVYB%2Fgvj4%2BYK'+
'Bg6SjptrZ3cPDxb69wG1tbsXFxsrJy29vccDAwfT09VJRU6uqrFlZW6moqo2Mj4yLjLKy'+
's%2Fj4%2BK%2Busu7t783Nz3l4e19fX7u6vaalqNPS1MjHylZVV318ftfW2UhHSG9uccv'+
'KzfHw8qqqrNPS1eXk5tvb3K%2BvsHNydeLi40pKS2JhY2hnalpZWlVVVtDQ0URDRJmZm5'+
'mYm11dXp2cnm9vcFxcXaOjo0pJSsC%2FwuXk6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'+
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC'+
'H5BAAAAAAALAAAAAAUABQAAAeagGaCg4SFhoeIiYqKTSQUFwgwi4JlB0pOCkEiRQKKRxM'+
'gKwMGDFEqBYpPRj4GAwwLCkQsijwQBAQJCUNSW1mKSUALNiVVJzIvSIo7GRUaGzUOPTpC'+
'igUeMyNTIWMHGC2KAl5hCBENYDlcWC7gOB1LDzRdWlZMAZOEJl83VPb3ggAfUnDo5w%2F'+
'AFRQxJPj7J4aMhYWCoPyASFFRIAA7'),
textarea);
textarea.parentNode.insertBefore(
createButton(
textarea,
textarea_zoom_out,
'Decrease textarea size',
20,
20,
'data:image/gif;base64,'+
'R0lGODlhFAAUAOYAANPS1uTj59va3vDw8bKxtGJiYrOys6Wkpvj4%2BPb29%2FX19mJiY'+
'%2Ff3%2BKqqrLe3uLKytURDRFpZWqmoqllZW9va3aOjo6Kho4KBg729vWJhZK%2BuskZF'+
'R4B%2FgsLBxHNydY2Mj%2Ff396amptLS0l9fX9fW2dDQ0W1tbpmZm8DAwfT09fHw8n18f'+
'uLi49LR1V5eYOjo6VBPUa6tr769wEhHSNra20pJStPS1KuqrNPS1ZmYm%2B7t77Kys8rJ'+
'y%2Fj4%2BaSjpm9uca%2BvsMjHyqalqHRzdVJRU8PDxVRTVcvKzc3Nz0pKS9rZ3evq7MC'+
'%2FwsXFxp2cnnl4e1VVVu%2Fv8ba2uM7Oz29vcbu6vZqZmnJxc9vb3PHx8uXk5mhnamJh'+
'Y1xcXZGQklZVV29vcHl4eoyLjKqpq6Wlpl1dXuXk6AAAAAAAAAAAAAAAAAAAAAAAAAAAA'+
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'+
'AAACH5BAAAAAAALAAAAAAUABQAAAeZgGaCg4SFhoeIiYqKR1IWVgcyi4JMBiQqA0heQgG'+
'KQTFLPQgMCVocBIoNNqMgCQoDVReKYlELCwUFI1glEYorOgopWSwiTUVfih8dLzRTKA47'+
'Ek%2BKBGE8GEAhFQYuPooBOWAHY2ROExBbSt83QzMbVCdQST8Ck4QtZUQe9faCABlGrvD'+
'rB4ALDBMU%2BvnrUuOBQkE4NDycqCgQADs%3D'),
textarea);
textarea.parentNode.insertBefore(
document.createElement('br'),
textarea);
}