onclick="" vs 事件处理程序

IT技术 javascript event-handling inline-code
2021-01-11 07:34:46

如果我想执行一个函数,我更喜欢使用内联 js:

<p id="element" onclick="doSomething();">Click me</p>

因为更容易调试。

但是,我听到有人说不要使用内联 js,而是这样做:

document.getElementById('element').onclick = doSomething;

为什么推荐js事件监听器?

6个回答

反对内联事件处理程序的一个重要论点,以及这里其他答案所解决的论点是表示和逻辑的分离

然而,IMO 实际上有一个更大的问题:内联事件处理程序的评估方式以某种方式难以捉摸。

您可能知道,on*属性的内容将用作事件处理函数的主体但是这个函数有什么特点呢?

令人惊讶的一个是一些祖先元素的属性元素本身的属性在内联事件处理程序范围内。

<form>
    <input name="foo" />
    <button type="button" onclick="console.log(foo); console.log(window.foo);">
        Click me
    </button>
    <div onclick="console.log(foo);">Click me as well!</div>
</form>

单击button日志

<input name="foo"></input>
undefined

在控制台中。事实window.fooundefined告诉你没有全局变量foo那么变量foo从何而来?为什么console.log(foo)记录输入元素而不抛出引用错误?
因为form元素的属性在事件处理程序的范围内,并且form元素对于它包含的每个命名表单控件元素都有一个属性。您可以使用console.log(document.querySelector('form').foo).

现在,单击该div元素实际上会引发引用错误:

ReferenceError: foo is not defined

因此显然该form元素仅在表单控件元素的范围内,而不是任何后代。这有多混乱?

同样,对象属性document也在内联事件处理程序的范围内,这可能会导致一些令人惊讶的错误(你知道它document有一个属性plugins吗?)。

HTML5 规范中规定了如何准确评估内联事件处理程序在第 10 步有一个循环,特别是在描述范围链创建的地方。


结论

由于元素和内联事件处理程序之间的这种 隐式联系,错误可能很难跟踪。如果你只是想测试一些东西,使用内联事件处理程序当然很好。但是在生产代码中使用它们会带来更高的维护成本。

quirksmode.org 上的文章很好地解释了绑定事件处理程序的不同方式及其(缺点)优势。

关于范围的相关文章:jibbering.com/faq/names/event_handler.html
2021-03-22 07:34:46
更不用说内联点击处理程序需要在全局范围内注册事件
2021-03-27 07:34:46
古老的问题,我意识到,但它在不久前出现在一个问题中。现在似乎至少有一些现代浏览器不会做那种奇怪with的范围包装器;你知道这是真的吗?
2021-03-27 07:34:46
啊它仅适用于具有表单所有者的元素;显然,某些 WebKit 版本将其赋予<a>元素,或者至少这就是新问题所声称的。没关系。
2021-04-03 07:34:46

基本上它与整体保持一切分开我相信。所以保持 HTML/CSS/JS 全部分开。它使您的 HTML 更整洁,而且我认为没有它更容易导航。

然后,当/如果您需要进行大量更改时,您有足够的空间,无论如何必须将内联 JS 转移到外部文件,或者如果您想将相同的功能应用于多个按钮,那么代码更少。更少的代码是一个更快乐的地方

如果你有你的 JS 文件,并且有完整的文档记录,那么由外部人员导航它们会更容易

同意,如果您以合乎逻辑、易于理解的方式使用 javascript,内联 js 的优势就会变得非常低。尽管如此,它并没有增加外部处理的优势。此外,由于您只是添加了一个函数名称,可能带有参数,您仍然可以将所有内容分开
2021-03-25 07:34:46
我不知道这是否是一个优势,我认为只是一种做法是将所有东西分开。所以我想回答你的问题,他们中的任何一个都没有真正的优势。正如上面有人指出的,如果你有很多动态内容,有时内联会更容易,但如果不是,我个人更喜欢外部文件。我认为这可能只是偏好。如果我错了,请有人纠正我。
2021-03-28 07:34:46

避免内联 JavaScript 的原因有很多,其中最重要的原因之一可能是代码的可维护性。

一个简单的例子(我使用 jQuery 只是为了演示目的)。

<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>

如果您突然收到更改所有段落以执行另一个功能的请求怎么办?在您的示例中,您必须手动更改 HTML 代码中的所有内容。但是,如果您选择将 HTML 与 JavaScript 分开,您可以简单地这样做。

<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>

$('.element').bind('click', doSomethingElse);

HTML 代码也更清晰,这使设计人员可以专注于设计,而不必担心他们在处理涉及其他人的项目时可能会实际破坏某些内容。

编辑:为我下面的评论提供示例。

Project = {
    // All the variables/constants/objects that need to be globally accessible inside the Project object.

    init : function(){
        // Main entry point...
        this.MainMenu.init();

        // Rest of the code which should execute the moment Project is initiated.
    }
}

Project.MainMenu = {
    // All the variables/constants/objects that need to be accessible only to MainMenu.

    init : function(){ // Is run immediatelly by Project.init()
        // Event handlers relevant to the main menu are bound here

        // Rest of the initialization code
    }
}

Project.SlideShow = {
    // All the variables/constants/objects that need to be accessible only to SlideShow.

    init : function(){ // Is run only on pages that really require it.
        // Event handlers for the slideshow.
    }
}
@holodoc,我想到了这一点,但是看到我目前在一个相当大的项目中的工作方式,我们不断遇到因事件被其他事物覆盖而导致的问题。当然,问题不仅仅是事件注册,而且很难判断一个元素是否已经附加了一个事件。特别是在大型项目中,活动注册可能会变得非常复杂
2021-03-12 07:34:46
@Johan 什么参数?您通常从事件对象中获取所有信息,该对象是事件处理程序的默认第一个参数。
2021-03-22 07:34:46
现在我想添加一个参数,并且必须将 DoSomethingElse 包装在不同的函数中,function(){ doSomethingElse(para) } 这通常会使内联时更难阅读,您只需添加参数即可。此外,我还没有经历过在许多不同元素中需要相同的功能,通常只有一个,也许是几个
2021-03-31 07:34:46
同意,这正是我发布这个问题的原因,以听取“另一方”的论点:) 总有特定情况下内联 javascript 更好,而其他情况下事件侦听更好。但对于一般用途?到目前为止,我仍然偏爱内联,但哦,好吧,
2021-03-31 07:34:46
那我能说什么。如果您发现内联样式更有用并且更不容易被队友滥用,那么请务必使用它:) 没有任何规则可以阻止您这样做。请记住,必须对来自这么多人声称不引人注目的 JavaScript 是要走的路的词有一定的重视:)
2021-04-01 07:34:46

尽管其他人可能会这么想,但我认为在标记中内联侦听器是有value的。具体来说,它为您提供了更多修改 DOM 节点的自由。如果您通过 JavaScript 添加侦听器,当您替换任何父节点的 innerHTML 时,侦听器将丢失。如果您在标记中内联侦听器,您可以克隆节点,对其进行修改,然后用您刚刚克隆和修改的节点替换原始节点。

也许最好在特定用例中描述它。我想在不多次触发回流的情况下对文档的多个部分进行更改。因此,在这种情况下,我可以克隆节点,对其进行任何更改(因为它分离后没有回流),然后用修改后的节点替换前一个节点(触发一次回流)。使用内联侦听器,这可以防止任何侦听器在替换过程中丢失。

利用 HTML 的声明形式(以这种方式)允许我添加我自己的 dom 元素检查器,它可以实时编辑与元素关联的功能,基本上是免费的。相反,如果有所有事件侦听器,则所有这些绑定都是不可见的,除非有条不紊地跟踪并在其他地方公开它,这不是免费的。
2021-03-18 07:34:46

我看到有人说需要将有关表示和业务逻辑的关注点分开。

OP 在他的例子中确实显示了这种分离!内联事件处理程序中没有逻辑,而只是一个将在“单击”事件上执行的函数引用/调用……逻辑本身可以在其他地方单独维护。

由于逻辑流的可发现性,我个人更喜欢这种方法。如果我以前从未见过应用程序......我要开始我的代码遍历的第一个地方是在 DOM 中,在那里很清楚哪些事件处理程序在起作用,哪些函数正在为处理程序。使用,比如说“JQuery.On”事件方法,通过浏览 html,您将不知道哪些处理程序实际上已连接并提供功能。

简单地提供函数指针的内联事件处理程序只是简单地连接事件而不是将逻辑泄漏到表示中。

同意,实际上我什至会做出更强有力的声明 - OP 的方法促进了在抽象化实现细节方面的关注点分离。这样,JS 代码就只关心“如何”,而不必关心它会在“哪里”使用。由 DOM 来指定应将什么逻辑应用于特定元素,从而使编辑 DOM 更加灵活并尽可能避免在 JS 中使用 HTML id。
2021-03-18 07:34:46