如果我想执行一个函数,我更喜欢使用内联 js:
<p id="element" onclick="doSomething();">Click me</p>
因为更容易调试。
但是,我听到有人说不要使用内联 js,而是这样做:
document.getElementById('element').onclick = doSomething;
为什么推荐js事件监听器?
如果我想执行一个函数,我更喜欢使用内联 js:
<p id="element" onclick="doSomething();">Click me</p>
因为更容易调试。
但是,我听到有人说不要使用内联 js,而是这样做:
document.getElementById('element').onclick = doSomething;
为什么推荐js事件监听器?
反对内联事件处理程序的一个重要论点,以及这里其他答案所解决的论点是表示和逻辑的分离。
然而,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.foo
是undefined
告诉你没有全局变量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 上的文章很好地解释了绑定事件处理程序的不同方式及其(缺点)优势。
基本上它与整体保持一切分开我相信。所以保持 HTML/CSS/JS 全部分开。它使您的 HTML 更整洁,而且我认为没有它更容易导航。
然后,当/如果您需要进行大量更改时,您有足够的空间,无论如何必须将内联 JS 转移到外部文件,或者如果您想将相同的功能应用于多个按钮,那么代码更少。更少的代码是一个更快乐的地方
如果你有你的 JS 文件,并且有完整的文档记录,那么由外部人员导航它们会更容易
避免内联 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.
}
}
尽管其他人可能会这么想,但我认为在标记中内联侦听器是有value的。具体来说,它为您提供了更多修改 DOM 节点的自由。如果您通过 JavaScript 添加侦听器,当您替换任何父节点的 innerHTML 时,侦听器将丢失。如果您在标记中内联侦听器,您可以克隆节点,对其进行修改,然后用您刚刚克隆和修改的节点替换原始节点。
也许最好在特定用例中描述它。我想在不多次触发回流的情况下对文档的多个部分进行更改。因此,在这种情况下,我可以克隆节点,对其进行任何更改(因为它分离后没有回流),然后用修改后的节点替换前一个节点(触发一次回流)。使用内联侦听器,这可以防止任何侦听器在替换过程中丢失。
我看到有人说需要将有关表示和业务逻辑的关注点分开。
OP 在他的例子中确实显示了这种分离!内联事件处理程序中没有逻辑,而只是一个将在“单击”事件上执行的函数引用/调用……逻辑本身可以在其他地方单独维护。
由于逻辑流的可发现性,我个人更喜欢这种方法。如果我以前从未见过应用程序......我要开始我的代码遍历的第一个地方是在 DOM 中,在那里很清楚哪些事件处理程序在起作用,哪些函数正在为处理程序。使用,比如说“JQuery.On”事件方法,通过浏览 html,您将不知道哪些处理程序实际上已连接并提供功能。
简单地提供函数指针的内联事件处理程序只是简单地连接事件而不是将逻辑泄漏到表示中。