如果我想要执行一个函数,我更喜欢使用内联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事件监听器?
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>
点击 按钮
记录日志
<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上的文章很好地解释了不同绑定事件处理程序的方式及其(不)优点。
with
类似的作用域包装器了;你知道这是真的吗? - Pointy<a>
元素,或者至少新问题声称是这样。不要紧。 - Pointy基本上,这与整个保持一切分离有关。所以要将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);
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.
}
}
我看到有人说需要在展示和业务逻辑方面进行分离。
在他的例子中,OP确实展示了这种分离!内联事件处理程序中没有逻辑,只是一个函数引用/调用,将在“click”事件上执行...逻辑本身可以在其他地方单独维护。
我个人更喜欢这种方法,因为它具有逻辑流的可发现性。如果我以前从未见过一个应用程序...我会开始遍历代码DOM,然后就会清楚哪些事件处理程序正在运行,哪些函数正在提供处理程序的逻辑。使用例如“JQuery.On”事件处理方法,仅仅通过浏览html,你无法知道哪些处理程序实际上已经连接并提供功能。
简单提供指向函数的内联事件处理程序仅仅是连接事件,并没有将逻辑泄漏到演示中。
我很惊讶没有人提到内容安全策略(CSP)作为使用事件属性替代事件属性的原因。我认为这比迄今为止给出的任何个人偏好都更重要。
当正确实现时(即不添加“unsafe-inline”,在我看来完全是无稽之谈),CSP1.0基本上阻止了所有形式的内联脚本。使内联事件属性变得不可能。虽然CSP2.0通过为内联脚本提供“nonce”来试图解决此问题,但它并未为内联事件属性提供此类方法。CSP3.0为内联事件添加了“unsafe-hashes”,但仍没有nonce的迹象。
因此,在缺少内联事件nonce的情况下,从CSP的角度来看,事件属性(或事件侦听器)非常适合。
你可以用相同的方式来说Cascading Style Sheets(CSS)和包含内联样式。不必浏览css文件就能找到当前的类/ID或者父元素/子元素,这是很容易做到的。
将点击事件与10个不同的项绑定行不行呢?还是只有一个定位到一个类的事件处理程序?即使你不需要10个单击处理程序,也应该考虑为可维护性和未来升级设置代码。
我不知道使用内联处理程序与稍后附加处理程序相比有什么优势。根据我的经验,当我必须处理动态元素时,例如如果我想要为每个图像添加一个处理程序并且图像的数量根据其他数据(例如数据库中的图像)而变化时,我更喜欢使用内联方法。在这种情况下,使用内联允许我为每个图像添加一个处理程序,否则我必须重新计算JavaScript文件中的图像数量以附加处理程序。
当然,当您可以使用诸如jQuery之类的库时,您可以轻松获取元素列表,内联就没有用处了。
除了编码标准的角度来看
使用属性:
<p id="element" onclick="doSomething();">点击我</p>
如果您再次添加相同的属性(如下所示)-则将考虑最新的属性。
<p id="element" onclick="doSomethingMore();">点击我</p>
使用事件处理程序:
document.getElementById('element').onclick = doSomething;
假设您添加了以下行
document.getElementById('element').onclick = doSomethingMore;
两个处理程序都会被调用。
<body onload="...">
,它很容易被window.onload覆盖,因此请将事件处理程序放在一个地方。 - mplungjan:)
- Šime Vidas