加入我们一起学习,每天进步
本文作者为360七舞团后端开发工程师
概述
在Web开发中,风暴是在浏览器窗口中触发的,并且通常绑定到窗口内的特定部分。 风暴可以绑定到一个元素、一系列元素或整个浏览器窗口。 以下是一些可能发生的风暴:
风暴处理器
每个可用的风暴都有一个风暴处理程序(风暴触发时运行的代码块),有时称为风暴错误。
下面通过一个简单的例子来概述一下storm处理器的概念
例如
<html lang="en">
<meta charset="utf-8" />
Event Demo
const btn = document.querySelector("button");
function clickBtn() {
alert("点击了按钮");
}
btn.onclick = clickBtn;
里面的脚本代码首先使用Document.querySelector()函数获取按钮元素,然后使用btn变量存储按钮,然后定义一个storm处理函数,最后将函数(弹窗函数)形参赋值给“click”事件处理程序参数,窃听“click”这个风暴。 每当在元素上触发风暴时,都会执行风暴处理程序代码块。 也就是说,每次用户单击它时,该代码都会运行。
触发网络风暴的表单内联风暴处理程序
<button onclick="clickBtn()" >press me
在网络上注册风暴处理程序的最早方法是使用类似于前面所示的风暴处理程序 HTML 属性,该属性值实际上是风暴发生时运行的 JavaScript 代码。 里面的case调用的storm handler在script模块中,但是你也可以直接在属性中插入JavaScript,例如:
<button onclick="alert('点击了按钮')" >press me
不建议使用等效的 HTML 属性作为 Wave 处理程序属性,因为使用 Wave 处理程序属性虽然看似简单,但很快就会变得难以管理且效率低下。
首先,HTML和JavaScript不应该混合使用,这违反了职责分离的原则,并且会使文档难以解析。 最好的方法是只在一处编写 JavaScript 代码。
尽管在单个文件中,外部风暴处理器也不是一个好主意。 一个按钮看起来不错,如果有一百个按钮怎么办? 您必须向文件添加 100 个属性。 这很快就会成为维护人员的噩梦。 使用 JavaScript,您可以轻松地将风暴处理程序添加到网页中的按钮。 就像下面这样:
const buttons = document.querySelectorAll('button');
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = clickBtn;
}
此外,将编程逻辑与内容分开将使您的网站对搜索引擎更加友好。
Storm 处理器属性
const btn = document.querySelector("button");
function clickBtn() {
alert("点击了按钮");
}
btn.onclick = clickBtn;
这个onclick是button元素的storm处理程序的一个属性,它和按钮的其他属性(比如btn.textContent)一样,有一个特殊的地方——当你给它一些代码参数时,只要storm触发代码将运行。
有很多风暴处理参数可供选择,例如
有些元素非常通用,几乎可以在任何地方使用(例如 onclick 几乎可以在每个元素上使用),但其他元素只能在特定场景中使用,例如我们只能在 video 元素上使用 onplay。
addEventListener() 和 removeEventListener()
该函数与storm处理器的属性类似,但语法略有不同。
const btn = document.querySelector("button");
btn.addEventListener('click',(ev)=>{
alert("点击了按钮");
})
这种机制比旧方法带来了一些优势,因为有一个相应的方法:removeEventListener(),它可以消除storm bug。 例如,以下代码将删除前面代码块中的风暴错误:
btn.removeEventListener('click', bgChange);
它在简单、小型项目中可能不是很有用,但是在小型、复杂项目中却非常有用,它可以非常高效地消除未使用的风暴处理器,并且在其他一些场景中也非常有效 - 例如,如果您需要运行不同的环境下有不同的storm处理器,只需适当删除或添加storm处理器即可。
您还可以为同一个错误注册多个处理程序,但无法通过以下方法完成:
myElement.onclick = functionA;
myElement.onclick = functionB;
第二行将覆盖第一行,并且以下方法可以正常工作:
myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);
单击元素时有两个函数起作用:
在这三种机制中,决不应该使用内联处理程序。 内联表单严重违反了 HTML 和 JavaScript 职责分离的原则。
另外两个是相对可以互换的,至少对于简单的用途来说:
addEventListener() 的主要优点是您可以使用removeEventListener() 删除风暴处理程序代码,但如果需要,您可以向同一类型的元素添加多个侦听器。 例如,您可以对一个元素多次调用 addEventListener('click',function(){...}) 。 对于风暴处理程序属性来说这是不可能的,因为任何先前设置的属性都会覆盖之前的属性。
其他风暴概念抑制默认行为
有时,您会遇到希望 Storm 不执行其默认行为的情况。 最常见的例子是 Web 表单,例如自定义注册表单。当您填写详细信息并按下提交按钮时,自然的行为是将数据提交到服务器上的指定页面进行处理,并将浏览器重定向到某个排序“成功消息”页面
当用户没有正确提交数据时,您希望停止向服务器提交信息并给他们一条错误消息,告诉他们做错了什么以及需要采取哪些措施来纠正错误。 某些浏览器支持手动表单数据验证,但由于许多浏览器不支持,因此建议您不要依赖于此并实现自己的验证检查。 我们来看一个简单的反例。
首先,一个简单的 HTML 表单,要求你填写名字(firstname)和姓氏(lastname)
<label for="fname">First name:
<input id="fname" type="text">
<label for="lname">Last name:
<input id="lname" type="text">
<input id="submit" type="submit">
这里我们使用 onsubmit 事件处理程序来实现一个特别简单的检查,以查看文本数组是否为空。 如果是这样,我们在storm对象上调用preventDefault()函数,该函数会停止表单提交,然后在表单下面的段落中显示一条错误消息,告诉用户出了什么问题:
const form = document.querySelector('form');
const name = document.getElementById('nickName');
const pa = document.getElementById('password');
const para = document.querySelector('p');
form.onsubmit = function(e) {
if (name.value === '' || pa.value === '') {
e.preventDefault();
para.textContent = 'Please enter the user name and password!';
}
}
事实上,这是一种极其弱的表单验证——例如,如果用户输入空格或数字来提交表单html提交按钮,则表单验证不会阻止用户提交
风暴冒泡和风暴捕获
当具有父元素的元素发生风暴时,现代浏览器会运行两个不同的阶段 - 捕获阶段和冒泡阶段。 在捕获阶段:
在冒泡阶段,情况正好相反:
在现代浏览器中,默认情况下所有风处理程序都会在冒泡阶段注册。
标准wave对象有一个名为stopPropagation()的函数可用,当在wave对象上调用该函数时,只会让当前的wave处理程序运行,但wave不会沿着气泡链进一步传播,因此不会有更多的风暴处理程序运行(没有向下冒泡)。
注意:为什么我们要弄清楚捕获和冒泡? 这是因为,在过去糟糕的日子里,浏览器兼容性比现在低得多,Netscape 仅使用风暴捕获,而 Internet Explorer 仅使用风暴冒泡。 当 W3C 决定尝试标准化这种行为并达成共识时,他们最终得到了一个涵盖两种情况(捕获和冒泡)的系统,并在现代浏览器中使用。
默认情况下,所有风暴处理程序都会在冒泡阶段注册,因为这在大多数情况下更有意义。 如果您确实想在捕获阶段注册事件,可以使用 addEventListener() 并将可选的第三个属性设置为 true 来注册处理程序。
泡沫还是捕获?
对于storm代理来说,storm捕获和storm冒泡阶段并没有明显的区别,而且由于storm冒泡的风暴流模型兼容所有主流浏览器,所以从兼容性的角度建议大家使用storm气泡模型。
风暴委员会
冒泡还允许我们使用风暴委托——这一概念依赖于这样一个事实:如果你想在点击大量子元素中的任何一个时运行一段代码,你可以在其父节点上设置风暴错误,并且让子节点上发生的风暴冒泡到父节点,而不是为每个子节点单独设置风暴bug。
一个很好的例子是一系列列表项。 如果想在点击每个列表项时弹出消息,可以在父元素上设置点击bug,这样点击气泡就会从列表项冒泡到其父元素上。
例如
<ul class="animal_list">
pig
dog
cat
chicken
duck
<div class="box">