I want all buttons to perform an action before and after their normal onclick event. So I came up with the "brilliant" idea of looping through all those elements and creating a wrapper function.
我希望所有按钮在正常的onclick事件之前和之后执行操作。所以我提出了循环遍历所有这些元素并创建包装函数的“精彩”想法。
This appeared to work pretty well when I tested it, but when I integrated it into our app, it fell apart. I traced it down to the 'this' value was changed by my wrapper. The sample code illustrates this; before you wrap the event handlers, each button displays the button id when you click, but after wrapping it the displayed name is 'undefined' in this example, or 'Form1' if you run it from within a form.
当我测试它时,这看起来效果很好,但是当我将它集成到我们的应用程序中时,它就崩溃了。我追溯到'this'值由我的包装器改变了。示例代码说明了这一点;在包装事件处理程序之前,每个按钮在您单击时显示按钮ID,但在包装后,显示的名称在此示例中为“undefined”,如果您在表单中运行它,则为“Form1”。
Does anybody know either a better way to do the same thing? Or a good way to maintain the originally intended 'this' values?
有没有人知道更好的方法做同样的事情?或者是保持原先预期的“这个”价值观的好方法?
As you can imagine, I don't want to modify any of the existing event handler code in the target buttons.
可以想象,我不想修改目标按钮中的任何现有事件处理程序代码。
Thanks in advance.
提前致谢。
PS-The target browser is IE6 & up, crossbrowser functionality not required
PS-目标浏览器是IE6及以上,不需要crossbrowser功能
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<script language="javascript" type="text/javascript">
function btnWrap_onClick()
{
var btns = document.getElementsByTagName("button");
for( var i = 0; i < btns.length; i++)
{
var btn = btns[i];
// handle wrap button differerntly
if( "btnWrap" == btn.id)
{
btn.disabled = true;
continue; // skip this button
}
// wrap it
var originalEventHandler = btn.onclick;
btn.onclick = function()
{
alert("Starting event handler");
originalEventHandler();
alert("Finished event handler");
}
}
alert("Buttons wrapped successfully");
}
</script>
<body>
<p>
<button id="TestButton1" onclick="alert(this.id);">TestButton1</button>
<button id="TestButton2" onclick="alert(this.id);">TestButton2</button>
</p>
<button id="btnWrap" onclick="btnWrap_onClick();">Wrap Event Handlers</button>
</body>
</html>
3 个解决方案
#1
Like Paul Dixon said, you could use call but I suggest you use apply instead.
就像Paul Dixon所说,你可以使用电话,但我建议你使用apply。
However, the reason I am answering is that I found a disturbing bug: You are actually replacing all your event handlers with the event handler of the last button. I don't think that was what you intended, was it? (Hint: You are replacing the value for originalEventHandler in each iteration)
但是,我回答的原因是我发现了一个令人不安的错误:您实际上是用最后一个按钮的事件处理程序替换所有事件处理程序。我不认为这是你的意图,是吗? (提示:您正在每次迭代中替换originalEventHandler的值)
In the code below you find a working cross-browser solution:
在下面的代码中,您可以找到有效的跨浏览器解决方案:
function btnWrap_onClick()
{
var btns = document.getElementsByTagName("button");
for( var i = 0; i < btns.length; i++)
{
var btn = btns[i];
// handle wrap button differerntly
if( "btnWrap" == btn.id)
{
btn.disabled = true;
continue; // skip this button
}
// wrap it
var newOnClick = function()
{
alert("Starting event handler");
var src=arguments.callee;
src.original.apply(src.source,arguments);
alert("Finished event handler");
}
newOnClick.original = btn.onclick; // Save original onClick
newOnClick.source = btn; // Save source for "this"
btn.onclick = newOnClick; //Assign new handler
}
alert("Buttons wrapped successfully");
}
First I create a new anonymous function and store that in the variable newOnClick. Since a function is an object I can create properties on the function object like any other object. I use this to create the property original that is the original onclick-handler, and source that is the source element that will be the this when the original handler is called.
首先,我创建一个新的匿名函数并将其存储在变量newOnClick中。由于函数是一个对象,我可以像任何其他对象一样在函数对象上创建属性。我使用它来创建原始onclick处理程序的属性原始,以及在调用原始处理程序时将成为此元素的源元素。
Inside the anonymous function I need to get a reference to the function to be able to get the value of the properties original and source. Since the anonymous function don't have a name I use use arguments.callee (that has been supported since MSIE5.5) to get that reference and store it in variable src.
在匿名函数内部,我需要获取对函数的引用,以便能够获取属性original和source的值。由于匿名函数没有名称,我使用arguments.callee(自MSIE5.5以来一直支持)来获取该引用并将其存储在变量src中。
Then I use the method apply to execute the original onclick handler. apply takes two parameters: the first is going to be the value of this, and the second is an array of arguments. this has to be the element where the original onclick handler was attached to, and that value was saved in source. arguments is an internal property of all functions and hold all the arguments the function was called with (notice that the anonymous function don't have any parameters specified, but if it is called with some parameters anyway, they will be found in the arguments property).
然后我使用方法apply来执行原始的onclick处理程序。 apply有两个参数:第一个是this的值,第二个是参数数组。这必须是附加原始onclick处理程序的元素,并且该值保存在源代码中。 arguments是所有函数的内部属性,并保存调用函数的所有参数(请注意,匿名函数没有指定任何参数,但如果使用某些参数调用它们,它们将在arguments属性中找到)。
The reason I use apply is that I can forward all the arguments that the anonymous function was called with, and this makes this function transparent and cross-browser. (Microsoft put the event in window.event but the other browsers supplies it in the first parameter of the handler call)
我使用apply的原因是我可以转发调用匿名函数的所有参数,这使得此函数透明且跨浏览器。 (Microsoft将事件放在window.event中,但其他浏览器在处理程序调用的第一个参数中提供它)
#2
You can use the call method to resolve the binding, e.g. originalEventHandler.call(btn);
您可以使用调用方法来解析绑定,例如originalEventHandler.call(BTN);
Alternatively, a library like prototype can help - its bind method lets you build a new function bound to a specified object, so you'd have declared originalEventHandler as var originalEventHandler = btn.onclick.bind(btn);
或者,像prototype这样的库可以提供帮助 - 它的bind方法允许你构建一个绑定到指定对象的新函数,所以你已经将originalEventHandler声明为var originalEventHandler = btn.onclick.bind(btn);
Finally, for a good backgrounder on binding issues, see also Getting Out of Binding Situations in JavaScript
最后,有关绑定问题的良好背景知识,请参阅JavaScript中的“退出绑定情境”
#3
Your problem is the way closures work in JavaScript. Honestly, I'd recommend using a framework. Any of them should make event-handling far nicer than doing it by hand.
你的问题是关闭JavaScript的工作方式。老实说,我建议使用框架。他们中的任何一个都应该使事件处理比手工操作更好。
#1
Like Paul Dixon said, you could use call but I suggest you use apply instead.
就像Paul Dixon所说,你可以使用电话,但我建议你使用apply。
However, the reason I am answering is that I found a disturbing bug: You are actually replacing all your event handlers with the event handler of the last button. I don't think that was what you intended, was it? (Hint: You are replacing the value for originalEventHandler in each iteration)
但是,我回答的原因是我发现了一个令人不安的错误:您实际上是用最后一个按钮的事件处理程序替换所有事件处理程序。我不认为这是你的意图,是吗? (提示:您正在每次迭代中替换originalEventHandler的值)
In the code below you find a working cross-browser solution:
在下面的代码中,您可以找到有效的跨浏览器解决方案:
function btnWrap_onClick()
{
var btns = document.getElementsByTagName("button");
for( var i = 0; i < btns.length; i++)
{
var btn = btns[i];
// handle wrap button differerntly
if( "btnWrap" == btn.id)
{
btn.disabled = true;
continue; // skip this button
}
// wrap it
var newOnClick = function()
{
alert("Starting event handler");
var src=arguments.callee;
src.original.apply(src.source,arguments);
alert("Finished event handler");
}
newOnClick.original = btn.onclick; // Save original onClick
newOnClick.source = btn; // Save source for "this"
btn.onclick = newOnClick; //Assign new handler
}
alert("Buttons wrapped successfully");
}
First I create a new anonymous function and store that in the variable newOnClick. Since a function is an object I can create properties on the function object like any other object. I use this to create the property original that is the original onclick-handler, and source that is the source element that will be the this when the original handler is called.
首先,我创建一个新的匿名函数并将其存储在变量newOnClick中。由于函数是一个对象,我可以像任何其他对象一样在函数对象上创建属性。我使用它来创建原始onclick处理程序的属性原始,以及在调用原始处理程序时将成为此元素的源元素。
Inside the anonymous function I need to get a reference to the function to be able to get the value of the properties original and source. Since the anonymous function don't have a name I use use arguments.callee (that has been supported since MSIE5.5) to get that reference and store it in variable src.
在匿名函数内部,我需要获取对函数的引用,以便能够获取属性original和source的值。由于匿名函数没有名称,我使用arguments.callee(自MSIE5.5以来一直支持)来获取该引用并将其存储在变量src中。
Then I use the method apply to execute the original onclick handler. apply takes two parameters: the first is going to be the value of this, and the second is an array of arguments. this has to be the element where the original onclick handler was attached to, and that value was saved in source. arguments is an internal property of all functions and hold all the arguments the function was called with (notice that the anonymous function don't have any parameters specified, but if it is called with some parameters anyway, they will be found in the arguments property).
然后我使用方法apply来执行原始的onclick处理程序。 apply有两个参数:第一个是this的值,第二个是参数数组。这必须是附加原始onclick处理程序的元素,并且该值保存在源代码中。 arguments是所有函数的内部属性,并保存调用函数的所有参数(请注意,匿名函数没有指定任何参数,但如果使用某些参数调用它们,它们将在arguments属性中找到)。
The reason I use apply is that I can forward all the arguments that the anonymous function was called with, and this makes this function transparent and cross-browser. (Microsoft put the event in window.event but the other browsers supplies it in the first parameter of the handler call)
我使用apply的原因是我可以转发调用匿名函数的所有参数,这使得此函数透明且跨浏览器。 (Microsoft将事件放在window.event中,但其他浏览器在处理程序调用的第一个参数中提供它)
#2
You can use the call method to resolve the binding, e.g. originalEventHandler.call(btn);
您可以使用调用方法来解析绑定,例如originalEventHandler.call(BTN);
Alternatively, a library like prototype can help - its bind method lets you build a new function bound to a specified object, so you'd have declared originalEventHandler as var originalEventHandler = btn.onclick.bind(btn);
或者,像prototype这样的库可以提供帮助 - 它的bind方法允许你构建一个绑定到指定对象的新函数,所以你已经将originalEventHandler声明为var originalEventHandler = btn.onclick.bind(btn);
Finally, for a good backgrounder on binding issues, see also Getting Out of Binding Situations in JavaScript
最后,有关绑定问题的良好背景知识,请参阅JavaScript中的“退出绑定情境”
#3
Your problem is the way closures work in JavaScript. Honestly, I'd recommend using a framework. Any of them should make event-handling far nicer than doing it by hand.
你的问题是关闭JavaScript的工作方式。老实说,我建议使用框架。他们中的任何一个都应该使事件处理比手工操作更好。