如果我想执行一个函数,我更喜欢做内联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.
}
}
尽管其他人可能会怎么想,我认为在标记中内联侦听器具有赎回价值。具体来说,它为您提供了更多修改 DOM 节点的自由。如果您通过 JavaScript 添加侦听器,则当您替换任何父节点的 innerHTML 时,侦听器将丢失。如果您在标记中内联侦听器,您可以克隆节点,对其进行修改,然后将原始节点替换为您刚刚克隆和修改的节点。
也许最好在特定的用例中描述它。我想在不触发多次重排的情况下对文档的多个部分进行更改。所以在这种情况下,我可以克隆节点,对其进行任何更改(因为它分离后没有回流),然后用修改后的节点替换前一个节点(触发一个回流)。使用内联侦听器,这可以防止任何侦听器在替换过程中丢失。
我看到有人说需要分离关于表示和业务逻辑的关注点。
OP 在他的例子中确实显示了这种分离!内联事件处理程序中没有逻辑,而只是将在“单击”事件上执行的函数引用/调用......逻辑本身可以在其他地方单独维护。
由于逻辑流可发现性,我个人更喜欢这种方法。如果我以前从未见过应用程序...我要开始我的代码遍历的第一个地方是在 DOM 中,并且在那里将清楚哪些事件处理程序正在运行以及哪些函数正在为处理程序。使用“JQuery.On”事件方法,只需浏览 html,您将不知道哪些处理程序实际上已连接并提供功能。
简单地提供函数指针的内联事件处理程序只是简单地连接事件而不是将逻辑泄漏到表示中。
你可以对 CSS 和包括内联样式说同样的话。不必费力地通过 css 文件来查找当前的类/id 或您正在使用的父/子元素会很容易。
绑定点击说 10 个不同的内联项目真的更好吗?或者只是一个针对一个类的事件处理程序?即使您不需要 10 个单击处理程序,设置代码以实现可维护性并在以后进行升级总是一个更好的主意。
我不知道使用内联处理程序与稍后附加处理程序的优势。根据我的经验,当我必须处理动态元素时,我更喜欢使用内联方法,例如,如果我想为每个图像添加一个处理程序,并且图像的数量会根据其他数据(例如数据库中的图像)而变化。在这种情况下,使用内联允许我为每个图像添加一个处理程序,否则我必须重新计算 javascript 文件中的图像数量以附加和处理每个图像。
当然,当您可以使用像 jQuery 这样可以轻松获取内联元素列表的库时,这些库是没有用的
除了编码标准的观点
使用属性:
<p id="element" onclick="doSomething();">Click me</p>
如果您再次添加相同的属性(见下文) - 考虑最新的属性。
<p id="element" onclick="doSomethingMore();">Click me</p>
使用事件处理程序:
document.getElementById('element').onclick = doSomething;
假设您添加了以下行
document.getElementById('element').onclick = doSomethingMore;
两个处理程序都被调用。
我很惊讶没有人提到内容安全策略 (CSP) 作为使用事件属性代替事件属性的理由。我认为这胜过迄今为止给出的任何个人偏好。
如果实施得当(即不添加“unsafe-inline”,恕我直言,这完全是胡说八道),CSP1.0 基本上会阻止所有形式的内联脚本。使内联事件属性成为不可能。虽然 CSP2.0 试图通过为内联脚本提供“nonce”来解决这个问题,但它没有为内联事件属性提供这样的东西。CSP3.0 为内联事件添加了“不安全哈希”,但仍然没有随机数的迹象。
因此,在没有内联事件的随机数的情况下,从 CSP 的角度来看,事件属性(或事件侦听器)非常适合。