1

我希望所有按钮在其正常的 onclick 事件之前和之后执行一个操作。所以我想出了循环遍历所有这些元素并创建一个包装函数的“绝妙”想法。

当我测试它时,这似乎工作得很好,但是当我将它集成到我们的应用程序中时,它就崩溃了。我将其追溯到包装器更改了“this”值。示例代码说明了这一点;在包装事件处理程序之前,每个按钮都会在您单击时显示按钮 ID,但在包装它之后,在此示例中显示的名称为“未定义”,如果您从表单中运行它,则显示名称为“Form1”。

有人知道做同样事情的更好方法吗?还是保持最初预期的“this”值的好方法?

可以想象,我不想修改目标按钮中的任何现有事件处理程序代码。

提前致谢。

PS-目标浏览器是IE6及以上,不需要跨浏览器功能

<!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>
4

3 回答 3

4

您可以使用call方法来解析绑定,例如originalEventHandler.call(btn);

或者,像原型这样的库可以提供帮助 - 它的bind方法允许您构建一个绑定到指定对象的新函数,因此您可以将 originalEventHandler 声明为var originalEventHandler = btn.onclick.bind(btn);

最后,有关绑定问题的良好背景知识,另请参阅摆脱 JavaScript 中的绑定情况

于 2008-12-30T17:59:45.673 回答
3

就像Paul Dixon说的,你可以使用call但我建议你使用apply代替。

但是,我要回答的原因是我发现了一个令人不安的错误:您实际上正在用最后一个按钮的事件处理程序替换所有事件处理程序。我不认为那是你的意图,是吗?(提示:您将在每次迭代中替换 originalEventHandler 的值)

在下面的代码中,您可以找到一个有效的跨浏览器解决方案:

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");
}

首先,我创建一个新的匿名函数并将其存储在变量newOnClick中。由于函数是一个对象,我可以像任何其他对象一样在函数对象上创建属性。我使用它来创建作为原始 onclick-handler 的属性original,以及作为元素的 source ,当调用原始处理程序时将成为this 。

在匿名函数内部,我需要获取对该函数的引用才能获取属性originalsource的值。由于匿名函数没有名称,我使用arguments.callee(自MSIE5.5 起支持)来获取该引用并将其存储在变量src 中。

然后我使用方法apply来执行原始的 onclick 处理程序。apply接受两个参数:第一个是this的值,第二个是参数数组。必须是原始 onclick 处理程序附加到的元素,并且该值保存在source中。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)

于 2008-12-30T22:07:40.697 回答
1

您的问题是闭包在 JavaScript 中的工作方式。老实说,我建议使用框架。他们中的任何一个都应该使事件处理比手动处理要好得多。

于 2008-12-30T17:55:41.983 回答