为什么在HTML中使用onClick()是一种不好的做法?

NiL*_*iLL 130 html javascript

我曾多次听说过onClick()在HTML 中使用JavaScript事件是一种不好的做法,因为它对语义不利.我想知道缺点是什么以及如何修复以下代码?

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Run Code Online (Sandbox Code Playgroud)

Mic*_*rdt 166

你可能正在谈论不引人注目的Javascript,它看起来像这样:

<a href="#" id="someLink">link</a>
Run Code Online (Sandbox Code Playgroud)

与中央javascript文件中的逻辑看起来像这样:

$('#someLink').click(function(){
    popup('/map/', 300, 300, 'map'); 
    return false;
});
Run Code Online (Sandbox Code Playgroud)

优点是

  • 行为(Javascript)与演示文稿(HTML)分离
  • 没有混合语言
  • 你正在使用像jQuery这样的javascript框架,可以为你处理大多数跨浏览器问题
  • 您可以一次性向许多HTML元素添加行为,而无需重复代码

  • @FrançoisWahl:主要的缺点是可发现性:查看HTML并不会告诉你哪些回调附加到它,甚至没有任何回调. (41认同)
  • 你列举了很多优点,但缺点是什么?调试蓝烟? (29认同)
  • 对于这个讨论的另一点,新手 - "onclick"可以更明确地映射元素交互和效果(函数调用)之间的因果关系,以及减少认知负荷.许多课程将学习者引入`addEventListener`,它在更困难的语法中包含更多的想法.此外,最近基于组件的框架如Riot和React使用`onclick`属性,并且正在改变分离应该是什么的概念.从HTML`onclick`转移到支持这一点的组件可能是一个更有效的"步骤"学习过程. (3认同)
  • @abelito:不确定调试是否有缺点.如果有的话,调试是有利的,因为您可以在任何浏览器调试工具中逐步执行JavaScript.如果代码是内联的`onClick`,不确定你能做到这一点很简单.如果你想反对你的脚本,你也可以编写单元测试,如果代码在`onClick`中并且没有逻辑分离,我也会认为这也很难.我个人在调试不引人注目的JavaScript方面没有任何问题,管理和测试JavaScript代码的好处远远不能使用它. (2认同)

Ric*_*haw 41

如果您正在使用jQuery,那么:

HTML:

 <a id="openMap" href="/map/">link</a>
Run Code Online (Sandbox Code Playgroud)

JS:

$(document).ready(function() {
    $("#openMap").click(function(){
        popup('/map/', 300, 300, 'map');
        return false;
    });
});
Run Code Online (Sandbox Code Playgroud)

这样做的好处是仍可以在没有JS的情况下工作,或者如果用户中间点击链接.

这也意味着我可以通过再次重写来处理通用弹出窗口:

HTML:

 <a class="popup" href="/map/">link</a>
Run Code Online (Sandbox Code Playgroud)

JS:

$(document).ready(function() {
    $(".popup").click(function(){
        popup($(this).attr("href"), 300, 300, 'map');
        return false;
    });
});
Run Code Online (Sandbox Code Playgroud)

这可以让你通过给它弹出类添加一个弹出窗口到任何链接.

这个想法可以进一步扩展如下:

HTML:

 <a class="popup" data-width="300" data-height="300" href="/map/">link</a>
Run Code Online (Sandbox Code Playgroud)

JS:

$(document).ready(function() {
    $(".popup").click(function(){
        popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
        return false;
    });
});
Run Code Online (Sandbox Code Playgroud)

我现在可以在我的整个网站上使用相同的代码来获取大量弹出窗口,而无需编写大量的onclick内容!是的可重用性!

这也意味着如果以后我决定弹出窗口是不好的做法(他们是!)并且我想用灯箱样式模态窗口替换它们,我可以改变:

popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
Run Code Online (Sandbox Code Playgroud)

myAmazingModalWindow($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
Run Code Online (Sandbox Code Playgroud)

而我在整个网站上的所有弹出窗口现在的工作方式完全不同了.我甚至可以进行功能检测来决定在弹出窗口上做什么,或者存储用户偏好以允许或不允许.使用内联onclick,这需要大量的复制和粘贴工作.

  • *link*无需JavaScript即可使用.查看链接如何具有正常的href属性.如果用户没有JavaScript,则链接仍然是链接,用户仍然可以访问该内容. (12认同)
  • 没有JavaScript工程?这是jQuery.它需要在浏览器上启用Javascript才能工作,对吧? (4认同)
  • @Thomas Shields:不,他的意思是如果你没有Javascript,链接仍会带你到/ map/... (3认同)
  • 是的,但在这种情况下,链接可以重定向到执行没有JavaScript的操作的页面. (2认同)
  • 啊,富有!我们都爱你这个最麻烦的解决方案! (2认同)

Aln*_*tak 20

这有几个原因并不好:

  • 它混合了代码和标记
  • 以这种方式编写的代码经历了 eval
  • 并在全球范围内运行

最简单的方法是name<a>元素中添加一个属性,然后你就可以:

document.myelement.onclick = function() {
    window.popup('/map/', 300, 300, 'map');
    return false;
};
Run Code Online (Sandbox Code Playgroud)

虽然现代最佳实践是使用id而不是使用名称,addEventListener()而是使用而不是使用,onclick因为它允许您将多个函数绑定到单个事件.

  • @preaction就像内联事件处理程序一样,因此为什么我的回答说最好使用`addEventListener()`,以及为什么. (3认同)
  • 有人可以解释一下吗?“以这种方式编写的代码会通过 eval”……我在网上找不到任何关于此的信息。你是说使用内联 Javascript 有一些性能或安全方面的缺点吗? (3认同)

Yid*_*nja 20

对于非常大的JavaScript应用程序,程序员使用更多的代码封装来避免污染全局范围.要使一个函数可用于HTML元素中的onClick操作,它必须位于全局范围内.

您可能已经看到过这样的JS文件......

(function(){
    ...[some code]
}());
Run Code Online (Sandbox Code Playgroud)

这些是立即调用函数表达式(IIFE),其中声明的任何函数将仅存在于其内部范围内.

如果您function doSomething(){}在IIFE中声明,然后doSomething()在HTML页面中进行元素的onClick操作,您将收到错误.

另一方面,如果您在该IIFE中为该元素创建一个eventListener并doSomething()在侦听器检测到click事件时调用,那么您是好的,因为该侦听器并doSomething()共享IIFE的范围.

对于使用最少量代码的小型Web应用程序,无关紧要.但是如果你渴望编写大型的,可维护的代码库,onclick=""那么你应该努力避免这种习惯.

  • *如果您渴望编写大型、可维护的代码库*,请使用 Angular、Vue、React 等 JS 框架...这些框架建议在 HTML 模板中绑定事件处理程序 (2认同)

Gra*_*ham 12

有几个原因:

  1. 我发现它有助于分离标记,即HTML和客户端脚本.例如,jQuery可以轻松地以编程方式添加事件处理程序.

  2. 您提供的示例将在任何不支持javascript或关闭javascript的用户代理中被破坏.渐进增强的概念将鼓励/map/为没有javascript的用户代理提供简单的超链接,然后为支持javascript的用户代理添加一个单击处理程序.

例如:

标记:

<a id="example" href="/map/">link</a>
Run Code Online (Sandbox Code Playgroud)

使用Javascript:

$(document).ready(function(){

    $("#example").click(function(){
        popup('/map/', 300, 300, 'map');
        return false;
    });

})
Run Code Online (Sandbox Code Playgroud)

  • 感谢提醒我逐步增强,所以这仍然符合这个范例:`<a href ="/ map /"onclick ="popup('/ map /',300,300,'map'); return false; ">链接</a>` (2认同)

Roc*_*mat 8

这是一种名为" Unobtrusive JavaScript " 的范例.当前的"网络标准"表示要分离功能和演示.

这不是一个"糟糕的做法",只是大多数新标准都希望您使用事件监听器而不是内嵌JavaScript.

此外,这可能只是个人的事情,但我认为使用事件监听器时更容易阅读,特别是如果您要运行多于1个JavaScript语句.

  • 关于这个主题的维基百科页面已超过4年.它不再是一个*新的范例.:) (2认同)

Koo*_*Inc 6

你的问题将触发我想的讨论.一般的想法是将行为和结构分开是好的.此外,afaik,内联点击处理程序必须eval导致'成为'真正的javascript函数.而且它很老式,尽管这是一个非常不稳定的论点.啊,好吧,请阅读@ quirksmode.org


Kam*_*ski 5

修订版

在PAST中,不引人注目的JavaScript方法是不错的-特别是HTML中的事件处理程序绑定被认为是不好的做法(主要是因为YiddishNinjaonclick events run in the global scope and may cause unexpected error提到了这一点

然而...

当前,这种方法似乎有些过时了,需要进行一些更新。如果有人想成为专业的前端开发人员并编写大型而复杂的应用程序,则他需要使用Angular,Vue.js等框架。但是,该框架通常使用(或允许使用)绑定了事件处理程序的HTML模板。直接在html模板代码中,这非常方便,清晰和有效-例如,在人们通常使用的有角度模板中,他们写道:

<button (click)="someAction()">Click Me</button> 
Run Code Online (Sandbox Code Playgroud)

在原始js / html中,这等效于

<button onclick="someAction()">Click Me</button>
Run Code Online (Sandbox Code Playgroud)

区别在于,原始js onclick事件在全局范围内运行-但是框架提供了封装。

那么问题出在哪里呢?

问题是,当新手程序员总是听说html-onclick不好并且总是使用html时,他们btn.addEventListener("onclick", ... )想使用带有模板的框架(addEventListener也有缺点 -如果我们使用动态方式更新DOM innerHTML=(这是非常快的),那么我们就会松散事件处理程序以这种方式绑定)。然后,他将面对使用框架的不良习惯或错误方法-他将以非常糟糕的方式使用框架-因为他将主要关注js-part而不是template-part(并且产生不清楚且难以维护的东西)码)。要改变这种习惯,他会浪费很多时间(也许他需要一些运气和老师)。

因此,我认为,根据与学生的经验,如果他们在一开始使用html-handlers-bind会更好。就像我说的那样,确实可以在全局范围内调用处理程序,但是在此阶段,学生通常会创建易于控制的小型应用程序。为了编写更大的应用程序,他们选择了一些框架。

那么该怎么办?

我们可以更新Unobtrusive JavaScript方法,并允许在html中使用绑定事件处理程序(最终使用简单的参数)(但只能使用绑定处理程序-不能像OP提问中那样将逻辑放入onclick中)。所以我认为在原始js / html中应该允许

<button onclick="someAction(3)">Click Me</button>
Run Code Online (Sandbox Code Playgroud)

要么

<button (click)="someAction()">Click Me</button> 
Run Code Online (Sandbox Code Playgroud)
<button onclick="someAction()">Click Me</button>
Run Code Online (Sandbox Code Playgroud)

但是下面的例子不应该被允许

<button onclick="console.log('xx'); someAction(); return true">Click Me</button>

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Run Code Online (Sandbox Code Playgroud)

现实改变了,我们的观点也应该

  • React、Vue 和 Angular 可能看起来像使用“内联事件处理程序”,但它们_不_使用原始问题中描述的 HTML 属性。他们正在使用_directives_,并且在幕后他们将使用“addEventListener”。`(click)` 和 `onclick` 绝对不等同 (6认同)
  • 恕我直言,您的`onclick =“someAction(3)”`是“错误习惯”的一个例子。`someAction` 函数正在污染全局命名空间 (2认同)