为什么内联事件处理程序属性在现代语义 HTML 中是一个坏主意?

Why are inline event handler attributes a bad idea in modern semantic HTML?

提问人:haxxerz 提问时间:7/31/2012 最后编辑:Tibrogarganhaxxerz 更新时间:6/12/2023 访问量:6687

问:

内联事件处理程序是否被视为一种不好的做法?

例如:<button onclick=someFunction()>Click me!</button>

如果是这样,使用内联事件处理程序的缺点是什么?

JavaScript HTML 事件

评论

0赞 Ivar 11/18/2019
这回答了你的问题吗?为什么在 HTML 中使用 onClick() 是一种不好的做法?

答:

33赞 Mitya 7/31/2012 #1

这是个坏主意,因为......

  1. 最佳实践建议在内容、风格和脚本之间明确划分。用内联 JavaScript(或 CSS)混淆 HTML 与此不一致。

  2. 例如,您只能将每种类型的一个事件(每个元素)与 -style events 绑定,因此您不能有两个事件处理程序。on*onclick

  3. 如果以内联方式指定事件,则 JS 将指定为字符串(属性值始终为字符串),并在事件触发时进行评估。评价是邪恶的。

  4. 由内联事件处理程序表示的函数必须是全局的(或者至少是全局可访问的),这在当今很少见;代码通常被命名空间化,或封装在模块中(感谢 Simon @Sebastian)。

  5. 你使用的任何内容安全策略 (CSP) 都必须(不明智地)扩展,以允许评估内联 JavaScript。

简而言之,通过专用 API 或通过 jQuery 或其他方式集中处理事件。addEventListener

[2021年编辑]

如今,响应式框架在一定程度上扭转了这一趋势;响应式框架中的事件通常被指定为属性,例如在 Vue 中:

<p v-on:click='foo'>Hello</p>

...其中 是当前组件的数据对象的方法。foo

但是,这不是真正的内联事件处理;请参阅@adnanmuttaleb的回答下@colin的评论。

评论

1赞 Sebastian Simon 4/27/2018
这是另一个很好的列表,为什么应该避免。onclick
0赞 adnanmuttaleb 6/25/2020
同样关于第 1 点,这可以通过使用类似 jsfiddle.net/ageck0bh 的东西来解决。onClick="handler1() || handler2()....|| handlern()"
3赞 Colin 10/10/2021
v-on:click实际上不是一个属性。这是一个指令,在引擎盖下,它将使用 vuejs.org/v2/guide/events.html#Listening-to-EventsaddEventListener
1赞 HyperTextCoffeePot 10/20/2022
真的很旧,但我自己对这个问题做了很多研究。关于React或Angular等的最后一部分,重要的是要注意区别。这些框架使用模板引擎,看似内联 JS 的东西实际上并非如此。它们将通过编译器或模板引擎传递,这些引擎会将代码输出为这些元素上的适当事件侦听器。原始 HTML 没有这样的好处。
2赞 Sebastian Simon 11/1/2022
关于第 4 点:当前的最佳实践包括使用 ECMAScript 模块。模块有自己的模块作用域,而不是全局作用域。由于属性依赖于全局范围,因此必须将函数设置为全局属性,从而破坏了模块的部分用途,特别是封装的好处。on*
7赞 Stof 6/2/2018 #2

除了语义和公认的答案中表达的其他意见外,所有内联脚本都被视为漏洞和高安全风险。任何希望在现代浏览器上运行的网站都应该通过元属性或标头设置“内容安全策略”(CSP) 属性。

这样做与所有内联脚本和样式不兼容,除非明确允许将它们作为排除项。虽然 CSP 目标主要是防止持续的跨站点脚本 (xss) 威胁,其中内联脚本和样式是 xss 的向量,但它不是当前浏览器中的默认行为,但将来可能会更改。

评论

0赞 barlop 7/27/2019
我想你的意思是 HTML 中根本没有 javascript。但是,由于您使用术语内联 javascript,我会注意到,根据此处得票最高的答案 stackoverflow.com/questions/19618571/what-is-inline-javascript 内联 javascript 只是脚本标签之间的问题。它不是 onclick 中的内容,而是内联事件处理程序。
2赞 Stof 7/28/2019
冒着重复我的风险,你指出 onclick 是一个内联事件处理程序是语义,或者是没有区别的区别。“处理程序”是脚本,即内联脚本。“得票最高”只是由于偏见而最高,SO 对开发人员有极端的偏见,而不是安全专业人员。如果 SO 上有更多像我这样的人,那么有安全意识的人投票会更均匀。票数较少并不意味着答案是错误的,除了 SO 之外,只允许 1 个被接受/正确的答案,但您将没有经验来考虑现实中有 1 个正确答案的问题。
0赞 CheddarLizzard 7/11/2023
@barlop 内联事件处理程序被 CSP 视为内联 JS:除非您在 中指定 或 中,否则它将阻止它们。'unsafe-inline''unsafe-hashes'script-src
0赞 Stof 7/17/2023
最主要的一点最好描述为,你想让一个水分子通过该死的,但不是整条河......当然,你可以稍微打开洪水来让它通过,但你无法控制只有一件事会通过。允许内联脚本,即使使用哈希或随机数,而不知道代码的作用,只是糟糕的安全性。你知道它不会在页面加载后提取漏洞并在内存中执行它吗?还是其他同样糟糕的事情?内联就是内联,盲目信任只是盲目和好感。
2赞 adnanmuttaleb 6/25/2020 #3

建立在@Mitya答案的基础上。

在大多数现代 JS 库中,ReactVue,..内联事件处理程序被认为是惯用的,但@Mitya提到的大多数限制都消失了。作为案例研究,我们将查看 Vuejs 并将其与上面列出的点进行比较:

  1. 你可以有多个事件处理程序,请看这里
  2. 事件值(处理程序)不是普通字符串,而是 js 表达式,请看这里onclick
  3. 全局范围问题根本不存在(因为你的代码将被 webpack 或其他工具翻译、最小化、重新打包)。

在我看来,内联事件处理程序主要增强了可读性,但意见可能会有所不同。

评论

7赞 Colin 10/12/2021
React、Vue 和 Angular 可能看起来像是在使用“内联事件处理程序”,但它们并没有像原始问题中描述的那样使用 HTML 属性。他们正在使用指令,在引擎盖下他们将使用 .因此,遵守最佳实践:developer.mozilla.org/en-US/docs/Learn/JavaScript/...addEventListener
2赞 Scott Marcus 1/4/2023
@Colin 我无法告诉你有多少次我不得不向那些想要将这些指令作为示例的人解释为什么实际上可以使用 HTML 事件属性(事实并非如此)!