Javascript 为什么在 HTML 中使用 onClick() 是不好的做法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5871640/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is using onClick() in HTML a bad practice?
提问by NiLL
I have heard many times that using JavaScript events, such as onClick()
, in HTML is a bad practice, because it's not good for semantics. I would like to know what the downsides are and how to fix the following code?
我多次听说onClick()
在 HTML中使用 JavaScript 事件(例如 )是一种不好的做法,因为它不利于语义。我想知道缺点是什么以及如何修复以下代码?
<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
回答by Michael Borgwardt
You're probably talking about unobtrusive Javascript, which would look like this:
您可能在谈论不显眼的 Javascript,它看起来像这样:
<a href="#" id="someLink">link</a>
with the logic in a central javascript file looking something like this:
中央 javascript 文件中的逻辑如下所示:
$('#someLink').click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
The advantages are
优点是
- behaviour (Javascript) is separated from presentation (HTML)
- no mixing of languages
- you're using a javascript framework like jQuery that can handle most cross-browser issues for you
- You can add behaviour to a lot of HTML elements at once without code duplication
- 行为(Javascript)与表示(HTML)分离
- 没有语言混合
- 您正在使用像 jQuery 这样的 javascript 框架,它可以为您处理大多数跨浏览器问题
- 您可以一次向大量 HTML 元素添加行为而无需重复代码
回答by Rich Bradshaw
If you are using jQuery then:
如果您使用的是 jQuery,那么:
HTML:
HTML:
<a id="openMap" href="/map/">link</a>
JS:
JS:
$(document).ready(function() {
$("#openMap").click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
});
This has the benefit of still working without JS, or if the user middle clicks the link.
这具有在没有 JS 的情况下仍然工作的好处,或者如果用户中间单击链接。
It also means that I could handle generic popups by rewriting again to:
这也意味着我可以通过再次重写来处理通用弹出窗口:
HTML:
HTML:
<a class="popup" href="/map/">link</a>
JS:
JS:
$(document).ready(function() {
$(".popup").click(function(){
popup($(this).attr("href"), 300, 300, 'map');
return false;
});
});
This would let you add a popup to any link by just giving it the popup class.
这将允许您通过为其提供弹出类来向任何链接添加弹出窗口。
This idea could be extended even further like so:
这个想法可以进一步扩展,如下所示:
HTML:
HTML:
<a class="popup" data-width="300" data-height="300" href="/map/">link</a>
JS:
JS:
$(document).ready(function() {
$(".popup").click(function(){
popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
return false;
});
});
I can now use the same bit of code for lots of popups on my whole site without having to write loads of onclick stuff! Yay for reusability!
我现在可以在我的整个网站上为大量弹出窗口使用相同的代码,而无需编写大量的 onclick 内容!是的,可重用性!
It also means that if later on I decide that popups are bad practice, (which they are!) and that I want to replace them with a lightbox style modal window, I can change:
这也意味着,如果稍后我决定弹出窗口是不好的做法(它们是!)并且我想用灯箱样式的模态窗口替换它们,我可以更改:
popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
to
到
myAmazingModalWindow($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
and all my popups on my whole site are now working totally differently. I could even do feature detection to decide what to do on a popup, or store a users preference to allow them or not. With the inline onclick, this requires a huge copy and pasting effort.
我整个网站上的所有弹出窗口现在都完全不同了。我什至可以进行特征检测来决定在弹出窗口上做什么,或者存储用户首选项以允许与否。使用内联 onclick,这需要大量的复制和粘贴工作。
回答by YiddishNinja
With very large JavaScript applications, programmers are using more encapsulation of code to avoid polluting the global scope. And to make a function available to the onClick action in an HTML element, it has to be in the global scope.
对于非常大的 JavaScript 应用程序,程序员使用更多的代码封装来避免污染全局范围。为了使 HTML 元素中的 onClick 操作可以使用一个函数,它必须在全局范围内。
You may have seen JS files that look like this...
你可能见过这样的 JS 文件...
(function(){
...[some code]
}());
These are Immediately Invoked Function Expressions (IIFEs) and any function declared within them will only exist within their internal scope.
这些是立即调用函数表达式 (IIFE),在其中声明的任何函数将仅存在于其内部范围内。
If you declare function doSomething(){}
within an IIFE, then make doSomething()
an element's onClick action in your HTML page, you'll get an error.
如果function doSomething(){}
在 IIFE 中声明,然后doSomething()
在 HTML 页面中执行元素的 onClick 操作,则会出现错误。
If, on the other hand, you create an eventListener for that element within that IIFE and call doSomething()
when the listener detects a click event, you're good because the listener and doSomething()
share the IIFE's scope.
另一方面,如果您在该 IIFE 中为该元素创建一个 eventListener 并doSomething()
在侦听器检测到单击事件时调用,则您很好,因为侦听器并doSomething()
共享 IIFE 的范围。
For little web apps with a minimal amount of code, it doesn't matter. But if you aspire to write large, maintainable codebases, onclick=""
is a habit that you should work to avoid.
对于代码量最少的小型网络应用程序,这无关紧要。但是,如果您渴望编写大型、可维护的代码库,onclick=""
那么您应该努力避免这种习惯。
回答by Alnitak
It's not good for several reasons:
它不好有几个原因:
- it mixes code and markup
- code written this way goes through
eval
- and runs in the global scope
- 它混合了代码和标记
- 以这种方式编写的代码通过
eval
- 并在全局范围内运行
The simplest thing would be to add a name
attribute to your <a>
element, then you could do:
最简单的方法是为元素添加一个name
属性<a>
,然后您可以执行以下操作:
document.myelement.onclick = function() {
window.popup('/map/', 300, 300, 'map');
return false;
};
although modern best practise would be to use an id
instead of a name, and use addEventListener()
instead of using onclick
since that allows you to bind multiple functions to a single event.
尽管现代最佳实践是使用id
名称而不是名称,并且使用addEventListener()
而不是使用,onclick
因为这允许您将多个函数绑定到单个事件。
回答by Graham
There are a few reasons:
有几个原因:
I find it aids maintenence to separate markup, i.e. the HTML and client-side scripts. For example, jQuerymakes it easy to add event handlers programatically.
The example you give would be broken in any user agent that doesn't support javascript, or has javascript turned off. The concept of progressive enhancementwould encourage a simple hyperlink to
/map/
for user agents without javascript, then adding a click handler prgramatically for user agents that support javascript.
我发现分离标记有助于维护,即 HTML 和客户端脚本。例如,jQuery可以轻松地以编程方式添加事件处理程序。
您提供的示例在任何不支持 javascript 或关闭 javascript 的用户代理中都会被破坏。渐进增强的概念将鼓励
/map/
为没有 javascript 的用户代理提供一个简单的超链接,然后为支持 javascript 的用户代理以编程方式添加点击处理程序。
For example:
例如:
Markup:
标记:
<a id="example" href="/map/">link</a>
Javascript:
Javascript:
$(document).ready(function(){
$("#example").click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
})
回答by Kamil Kie?czewski
Revision
修订
Unobtrusive JavaScriptapproach was good in the PAST - especially events handler bind in HTML was considered as bad practice (mainly because onclick events run in the global scope and may cause unexpected error
what was mention by YiddishNinja)
Unobtrusive JavaScript方法在过去是很好的 - 特别是 HTML 中的事件处理程序绑定被认为是不好的做法(主要是因为YiddishNinjaonclick events run in the global scope and may cause unexpected error
提到的)
However...
然而...
Currently it seems that this approach is a little outdated and needs some update. If someone want to be professional frontend developper and write large and complicated apps then he need to use frameworks like Angular, Vue.js, etc... However that frameworks usually use (or allow to use) HTML-templateswhere event handlers are bind in html-template code directly and this is very handy, clear and effective - e.g. in angular template usually people write:
目前似乎这种方法有点过时,需要一些更新。如果有人想成为专业的前端开发人员并编写大型复杂的应用程序,那么他需要使用 Angular、Vue.js 等框架……但是,这些框架通常使用(或允许使用)HTML 模板,其中绑定了事件处理程序直接在 html-template 代码中,这非常方便,清晰和有效 - 例如在 angular 模板中,人们通常会这样写:
<button (click)="someAction()">Click Me</button>
In raw js/html the equivalent of this will be
在原始 js/html 中,相当于
<button onclick="someAction()">Click Me</button>
The difference is that in raw js onclick
event is run in the global scope - but the frameworks provide encapsulation.
不同之处在于原始 jsonclick
事件在全局范围内运行 - 但框架提供封装。
So where is the problem?
那么问题出在哪里呢?
The problem is when novice programmer who always heard that html-onclick is bad and who always use btn.addEventListener("onclick", ... )
wants to use some framework with templates (addEventListener
also have drawbacks- if we update DOM in dynamic way using innerHTML=
(which is pretty fast) - then we loose events handlers bind in that way). Then he will face something like bad-habits or wrong-approach to framework usage - and he will use framework in very bad way - because he will focus mainly on js-part and no on template-part (and produce unclear and hard to maintain code). To change this habits he will loose a lot of time (and probably he will need some luck and teacher).
问题是,当新手程序员总是听说 html-onclick 很糟糕并且总是使用btn.addEventListener("onclick", ... )
想要使用一些带有模板的框架时(addEventListener
也有缺点- 如果我们以动态方式更新 DOM innerHTML=
(非常快)) - 那么我们就会丢失事件处理程序以这种方式绑定)。然后他会面临一些坏习惯或错误的框架使用方法——而且他会以非常糟糕的方式使用框架——因为他将主要关注 js 部分而不关注模板部分(并且产生不清楚和难以维护的)代码)。为了改变这种习惯,他会浪费很多时间(可能他需要一些运气和老师)。
So in my opinion, based on experience with my students, better would be for them if they use html-handlers-bind at the beginning. As I say it is true that handlers are call in global scope but a this stage students usually create small applications which are easy to control. To write bigger applications they choose some frameworks.
所以在我看来,根据我学生的经验,如果他们在开始时使用 html-handlers-bind 对他们来说会更好。正如我所说,处理程序确实在全局范围内被调用,但在这个阶段,学生通常会创建易于控制的小型应用程序。为了编写更大的应用程序,他们选择了一些框架。
So what to do?
那么该怎么办?
We can UPDATE the Unobtrusive JavaScript approach and allow bind event handlers (eventually with simple parameters) in html (but only bind handler - not put logic into onclick like in OP quesiton). So in my opinion in raw js/html this should be allowed
我们可以更新 Unobtrusive JavaScript 方法并允许在 html 中绑定事件处理程序(最终使用简单的参数)(但仅绑定处理程序 - 不像在 OP 问题中那样将逻辑放入 onclick 中)。所以在我看来,在原始 js/html 中应该允许这样做
<button onclick="someAction(3)">Click Me</button>
or
或者
function popup(num,str,event) {
let re=new RegExp(str);
// ...
event.preventDefault();
console.log("link was clicked");
}
<a href="https://example.com" onclick="popup(300,'map',event)">link</a>
But below examples should NOT be allowed
但不应允许以下示例
<button onclick="console.log('xx'); someAction(); return true">Click Me</button>
<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
The reality changes, our point of view should too
现实在变,我们的观点也应该变
回答by Rocket Hazmat
It's a newparadigm called "Unobtrusive JavaScript". The current "web standard" says to separate functionality and presentation.
这是一种名为“ Unobtrusive JavaScript”的新范式。当前的“网络标准”表示将功能和呈现分开。
It's not really a "bad practice", it's just that most new standards want you to use event listeners instead of in-lining JavaScript.
这并不是真正的“坏习惯”,只是大多数新标准都希望您使用事件侦听器而不是内联 JavaScript。
Also, this may just be a personal thing, but I think it's much easier to read when you use event listeners, especially if you have more than 1 JavaScript statement you want to run.
此外,这可能只是个人的事情,但我认为当您使用事件侦听器时更容易阅读,尤其是当您要运行 1 个以上的 JavaScript 语句时。
回答by KooiInc
Your question will trigger discussion I suppose. The general idea is that it's good to separate behavior and structure. Furthermore, afaik, an inline click handler has to be eval
led to 'become' a real javascript function. And it's pretty old fashioned, allbeit that that's a pretty shaky argument. Ah, well, read some about it @quirksmode.org
我想你的问题会引发讨论。一般的想法是将行为和结构分开是好的。此外,afaik,必须将内联点击处理程序eval
“变成”真正的 javascript 函数。而且它非常老式,尽管这是一个非常不稳定的论点。啊,好吧,读一些关于它的@quirksmode.org
回答by Minghuan
- onclick events run in the global scope and may cause unexpected error.
- Adding onclick events to many DOM elements will slow down the
performance and efficiency.
- onclick 事件在全局范围内运行,可能会导致意外错误。
- 将 onclick 事件添加到许多 DOM 元素会降低
性能和效率。
回答by CertainPerformance
Two more reasons not to use inline handlers:
不使用内联处理程序的另外两个原因:
They can require tedious quote escaping issues
他们可能需要繁琐的报价转义问题
Given an arbitrarystring, if you want to be able to construct an inline handler that calls a function with that string, for the general solution, you'll have to escape the attributedelimiters (with the associated HTML entity), andyou'll have to escape the delimiter used for the string inside the attribute, like the following:
给定一个任意字符串,如果您希望能够构造一个使用该字符串调用函数的内联处理程序,对于一般解决方案,您必须转义属性定界符(使用关联的 HTML 实体),并且您将必须转义用于属性内字符串的分隔符,如下所示:
const str = prompt('What string to display on click?', 'foo\'"bar');
const escapedStr = str
// since the attribute value is going to be using " delimiters,
// replace "s with their corresponding HTML entity:
.replace(/"/g, '"')
// since the string literal inside the attribute is going to delimited with 's,
// escape 's:
.replace(/'/g, "\'");
document.body.insertAdjacentHTML(
'beforeend',
'<button onclick="alert(\'' + escapedStr + '\')">click</button>'
);
That's incrediblyugly. From the above example, if you didn't replace the '
s, a SyntaxError would result, because alert('foo'"bar')
is not valid syntax. If you didn't replace the "
s, then the browser would interpret it as an end to the onclick
attribute (delimited with "
s above), which would also be incorrect.
这是令人难以置信的丑陋。从上面的示例中,如果您没有替换'
s,则会导致 SyntaxError,因为alert('foo'"bar')
不是有效的语法。如果不替换"
s,那么浏览器会将其解释为onclick
属性的结尾("
上面用s分隔),这也是不正确的。
If one habitually uses inline handlers, one would have to make sure to remember do something similar to the above (and do it right) every time, which is tedious and hard to understand at a glance. Better to avoid inline handlers entirely so that the arbitrary string can be used in a simple closure:
如果习惯使用内联处理程序,则必须确保记住每次都执行与上述类似的操作(并正确执行),这很乏味且难以一目了然。最好完全避免内联处理程序,以便可以在简单的闭包中使用任意字符串:
const str = prompt('What string to display on click?', 'foo\'"bar');
const button = document.body.appendChild(document.createElement('button'));
button.textContent = 'click';
button.onclick = () => alert(str);
Isn't that so much nicer?
这不是更好吗?
The scope chain of an inline handler is extremely peculiar
内联处理程序的作用域链非常奇特
What do you think the following code will log?
你认为下面的代码会记录什么?
let disabled = true;
<form>
<button onclick="console.log(disabled);">click</button>
</form>
Try it, run the snippet. It's probably not what you were expecting. Why does it produce what it does? Because inline handlers run inside with
blocks. The above code is inside threewith
blocks: one for the document
, one for the <form>
, and one for the <button>
:
试试看,运行代码片段。这可能不是你所期望的。为什么它产生它所做的?因为内联处理程序在with
块内运行。上面的代码包含三个with
块:一个用于document
,一个用于<form>
,一个用于<button>
:
let disabled = true;
<form>
<button onclick="console.log(disabled);">click</button>
</form>
Since disabled
is a property of the button, referencing disabled
inside the inline handler refers to the button's property, not the outer disabled
variable. This is quite counter-intuitive. with
has many problems: it can be the source of confusing bugs and significantly slows down code. It isn't even permitted at all in strict mode. But with inline handlers, you're forcedto run the code through with
s - and not just through one with
, but through multiple nested with
s. It's crazy.
由于disabled
是disabled
按钮的属性,因此在内联处理程序内部引用是指按钮的属性,而不是外部disabled
变量。这是非常违反直觉的。with
有很多问题:它可能是令人困惑的错误的根源,并显着减慢代码速度。在严格模式下甚至根本不允许这样做。但是,随着在线处理,你不得不运行通过代码with
秒-而不是仅仅通过一个with
,而是通过多个嵌套with
秒。这很疯狂。
with
should never be used in code. Because inline handlers implicitly require with
along with all its confusing behavior, inline handlers should be avoided as well.
with
永远不应该在代码中使用。由于内联处理程序隐含地要求with
及其所有令人困惑的行为,因此也应避免使用内联处理程序。