onclick="";与事件处理程序
如果我想要执行一个函数,我更喜欢使用内联js:
<p id="element" onclick="doSomething();">Click me</p>
因为它更容易调试。
但是,我听到人们说不要使用内联js,并且这样做:
document.getElementById('element').onclick = doSomething;
为什么推荐使用js事件监听器?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
反对内联事件处理程序的一个重要论点,以及这里其他答案所解决的论点是 分离表示和逻辑。
然而,在我看来,实际上存在一个更大的问题:内联事件处理程序的评估方式有些难以捉摸。
您可能知道,
on*
属性的内容将用作事件处理函数的body。但这个功能有什么特点呢?令人惊讶的事情之一是某些祖先元素的属性和元素本身在内联事件处理程序的范围中。
单击
按钮
登录控制台。
window.foo
为undefined
的事实告诉您不存在全局变量foo
。那么变量foo
从哪里来呢?为什么console.log(foo)
记录输入元素而不引发引用错误?因为
form
元素的属性位于事件处理程序的范围内,并且form
元素对其包含的每个命名表单控件元素都有一个属性。您可以使用console.log(document.querySelector('form').foo)轻松测试这一点。现在,单击
div
元素实际上会引发引用错误:所以显然
form
元素仅在表单控制元素的范围内,而不是任何后代。这有多令人困惑?同样,
文档的属性
对象也在内联事件处理程序的范围内,这可能会导致一些令人惊讶的错误(您知道吗?文档
有属性插件
?)。HTML5 规范中明确了如何评估内联事件处理程序。在步骤 10 处有一个循环,特别是在其中描述了作用域链的创建。
结论:
由于元素和内联事件处理程序之间的这种隐式连接,错误可能很难跟踪。如果您只想测试某些内容,那么使用内联事件处理程序当然没问题。但在生产代码中使用它们会带来更高的维护成本。
quirksmode.org 上的文章解释了绑定事件处理程序及其(dis)的不同方法优点很好。
One big argument against inline event handlers, and the argument that is addressed by the other answers here is the separation of presentation and logic.
However, there is actually a bigger problem IMO: The somehow elusive way how inline event handlers are evaluated.
As you may know, the content of the
on*
attributes will be used as body of the event handler function. But what characteristics does this function have?One of the surprising ones is that properties of some ancestor elements and of the element itself are in the scope of the inline event handler.
Clicking the
button
logsin the console. The fact that
window.foo
isundefined
tells you that there is no global variablefoo
. So where does the variablefoo
come from? Why doesconsole.log(foo)
log the input element and not throw a reference error?Because the properties of the
form
element are in the scope of the event handler and theform
element has a property for each named form control element it contains. You can easily test this withconsole.log(document.querySelector('form').foo)
.Now, clicking the
div
element actually throws a reference error:So apparently the
form
element is only in scope of form control elements, not any descendant. How confusing is that?Similarly, the properties of the
document
object are also in the scope of inline event handlers, which can lead to some surprising bugs (did you know thatdocument
has a propertyplugins
?).How exactly inline event handlers are evaluated is formalized in the HTML5 spec. Have a loop at step 10 in particular where he scope chain creation is described.
Conclusion:
Because of this implicit connection between elements and inline event handlers, bugs can be really hard to track. It's of course fine to use inline event handlers if you just want to test something. But using them in production code comes with a higher maintenance cost.
The articles at quirksmode.org explain the different ways of binding event handlers and their (dis)advantages very well.
基本上,我相信这与整体保持一切分离有关。因此,请将 HTML/CSS/JS 全部分开。它使您的 HTML 更加整洁,而且我认为,如果没有它,导航也更容易。
然后,当/如果您需要进行较大的更改时,您有足够的空间,无论如何都必须将内联 JS 转移到外部文件,或者如果您想将相同的功能应用于多个按钮,那么代码就更少了。更少的代码是一个更快乐的地方
如果你有正确的 JS 文件,并且有完整的文档,那么由外部人员导航它们就会变得更容易
Basically it has to do with the whole keep everything separate I believe. So keep HTML/CSS/JS all separate. It makes your HTML tidier and, I think, easier to navigate without.
Then when/if you need to make large changes, you have ample space with having to shift the inline JS to an external file anyway OR if you want to apply the same function to more than one button, then it's less code. And less code is a happier place
If you have your JS files properly, and thoroughly documented then navigating them by an outside person is made eaiser
避免使用内联 JavaScript 的原因有很多,其中最重要的原因之一可能是代码的可维护性。
一个简单的例子(我使用 jQuery 只是为了演示目的)。
如果您突然收到更改所有段落以执行另一个功能的请求怎么办?在您的示例中,您必须手动更改 HTML 代码中的所有内容。但是,如果您选择将 HTML 与 JavaScript 分开,您可以简单地这样做。
HTML 代码也更加清晰,这使得设计人员能够专注于设计,而不必担心他们在处理涉及其他人的项目时可能会真正破坏某些东西。
编辑:为我的评论提供示例。
There are a lot of reasons to avoid inline JavaScript and one of the perhaps most important is code maintainability.
A quick example (I am using jQuery simply for demonstration purposes).
What if suddenly you get a request to change all your paragraphs to execute another function? In your example you would have to change everything manually in your HTML code. However if you choose to separate HTML from JavaScript you could simply do it like this.
The HTML code is also cleaner which allows the designers to focus exclusively on design without fear that they might actually break something while working on a project which also involves other people.
EDIT: Providing example for my comment bellow.
不管其他人怎么想,我认为在标记中内联侦听器具有可取的价值。具体来说,它为您提供了更多修改 DOM 节点的自由。如果您通过 JavaScript 添加侦听器,则当您替换任何父节点的innerHTML 时,侦听器将会丢失。如果您在标记中内联侦听器,则可以克隆该节点,对其进行修改,然后将原始节点替换为您刚刚克隆和修改的节点。
也许最好在特定的用例中描述它。我想对文档的多个部分进行更改而不多次触发回流。因此,在这种情况下,我可以克隆该节点,对其进行任何更改(自分离以来没有回流),然后用修改后的节点替换前一个节点(触发一次回流)。对于内联侦听器,这可以防止任何侦听器在替换过程中丢失。
Despite what other people may think, I think there are redeeming value to inlining listeners in markup. Specifically, it gives you a lot more freedom to modifying the DOM nodes. If you're adding listeners though JavaScript, the listeners are lost when you replace the innerHTML of any parent nodes. If you're inlining the listeners in markup, you can clone the node, make modifications to it, then replace the original node with the node you just cloned and modified.
Perhaps it's better to describe it in a specific use case. I want to make changes to multiple part of the document without triggering reflow multiple times. So in this case, I can clone the node, make any change to it (no reflow since its detached), then replace the previous node with the modified one (triggering one reflow). With inlined listeners, this prevents any listeners from getting lost during the replacement.
我看到有人说需要将表示和业务逻辑的关注点分开。
OP 在他的例子中确实显示了这种分离!内联事件处理程序中没有逻辑,而只是在“单击”事件上执行的函数引用/调用...逻辑本身可以在其他地方单独维护。
由于逻辑流的可发现性,我个人更喜欢这种方法。如果我以前从未见过应用程序...我要开始遍历代码的第一个地方是在 DOM 中,就在那里,可以清楚哪些事件处理程序正在发挥作用以及哪些函数正在为应用程序提供逻辑。处理程序。通过使用“JQuery.On”事件方法,只需浏览 html,您将不知道哪些处理程序实际上已连接并提供功能。
仅提供函数指针的内联事件处理程序只是简单地连接事件,而不会将逻辑泄漏到表示中。
I see some saying there needs to be a separation of concerns regarding Presentation and Business Logic.
OP in his example does indeed show this separation! There is no logic in the inline event handler, but merely a function reference/call that will get executed on the "click" event... the logic itself can be separately maintained elsewhere.
I personally prefer this approach due to logical flow discoverability. If I have never seen an application before... the first place I'm going to begin my code traversal is in the DOM, and right there it will be clear which event handlers are in play and which functions are providing the logic to the handlers. With using, say a "JQuery.On" eventing approach, by just glancing through the html you would have no idea which handlers are actually wired up and providing functionality.
Inline event handlers that simply provide pointers to functions is simply wiring up the events and not leaking logic into presentation.
对于 CSS 和内联样式,您也可以这么说。不必费力地通过 css 文件来查找当前的类/id 或您使用的父/子元素,这将是很容易的。
绑定一次点击来内联说出 10 个不同的项目真的更好吗?或者只是一个针对某个类的事件处理程序?即使您不需要 10 个单击处理程序,设置代码以实现可维护性并在以后进行升级始终是一个更好的主意。
You could say the same about CSS and including inline styles. It would be easy to not have to wade through a css file to find the current class/id, or parent/child elements your working with.
Is it really better to bind a click to say 10 different items inline? Or just one event handler targeting a class? Even if you don't need 10 click handlers its always a better idea to set your code up for maintainability, and upgrades down the road.
除了编码标准角度之外
使用属性:
,请点击我
如果您添加 再次相同的属性(见下文)- 考虑最新的属性。
点击我
使用事件处理程序:
document.getElementById(' element').onclick = doSomething;
假设您添加了以下行
document.getElementById('element').onclick = doSomethingMore;
两个处理程序都被调用。
Apart from the coding standards perspective
Using Attributes:
<p id="element" onclick="doSomething();">Click me</p>
if you add the same attribute again (see below) -the latest one is considered.
<p id="element" onclick="doSomethingMore();">Click me</p>
Using Event Handler:
document.getElementById('element').onclick = doSomething;
and lets say you added the below line
document.getElementById('element').onclick = doSomethingMore;
Both the handlers are invoked.
我不知道使用内联处理程序与稍后附加处理程序的优势。根据我的经验,当我必须处理动态元素时,我更喜欢使用内联方法,例如,如果我想向每个图像添加一个处理程序,并且图像数量根据其他数据(例如数据库中的图像)而变化。在这种情况下,使用内联允许我向每个图像添加一个处理程序,否则我必须重新计算 javascript 文件中要附加到每个图像的图像数量和处理程序。
当然,当您可以使用像 jQuery 这样可以轻松获取内联元素列表的库时,这并没有什么用处
I'm not aware of and advantage of using inline handler vs attach the handler later. In my experience I prefer to use the inline method when I have to deal with dynamic elements, for example if I want to add an handler to each image and the number of images changes according to other data (ex images in db). In this case using inline allow me to add an handler to each image, otherwise i have to recalculate the number of images in the javascript file to attach and handler to each image.
Of course, when you can use libraries like jQuery where you can easily get the list of elements inline aren't useful
令我惊讶的是,没有人提到内容安全策略 (CSP) 作为使用事件属性代替事件属性的原因。我认为这胜过迄今为止给出的任何个人偏好。
当正确实现时(即不添加“不安全内联”,恕我直言,这完全是无稽之谈)CSP1.0 基本上会阻止所有形式的内联脚本。使内联事件属性变得不可能。虽然 CSP2.0 尝试通过为内联脚本提供“随机数”来解决此问题,但它没有为内联事件属性提供这样的东西。 CSP3.0 为内联事件添加了“不安全哈希”,但仍然没有随机数的迹象。
因此,在缺乏内联事件随机数的情况下,从 CSP 的角度来看,事件属性(或事件侦听器)是非常好的方法。
I'm surprised that nobody has mentioned content security policy (CSP) as a reason to use event properties in place of event attributes. I think that trumps any of the personal preferences given so far.
When properly implemented (i.e. not adding "unsafe-inline", which is utter nonsense imho) CSP1.0 basically blocks all forms of inline script. Making inline event attributes impossible. While CSP2.0 attempts to remedy this by providing a "nonce" for inline script, it does not provide such a thing for inline event attributes. CSP3.0 adds "unsafe-hashes" for inline events, but still no sign of a nonce.
So, in the absence of a nonce for inline events, event properties (or event listeners) is very much the way to go from a CSP standpoint.