JavaScript基础要件

指南

在代码中做决定 --条件

在任何程序语言中,程序需要根据不同的输入数据作出相应的选择并执行相关的操作。例如,在游戏中,如果玩家的生命值是 0,那么游戏就结束了。在天气应用中,如果在早上打开应用,则显示一个太阳升起的图片,如果在晚上打开,则显示星星和月亮。在这篇文章里我们将探索如何在JS中使用条件结构

循环语句

有时候你需要在一个行中重复执行某一个任务。例如,查看一整列的名字。在程序中,循环能非常好的处理好这个问题。在本章中我们将介绍JavaScript的循环语句。

函数—可重用的代码块

在编码中的另一个基本概念是函数(functions)。 函数 允许你在定义的区块内存储一段代码用来执行一个单独的任务,然后调用该段代码时,你需要使用一个简短的命令,而不用重复编写多次该段代码。在这篇文章中我们将探讨函数的基本概念,如语法、如何调用定义的函数、作用域和参数

函数返回值

在这个课程中,我们要讨论的最后一个基本概念是返回值(通过返回值结束我们的函数)。有些函数在完成后不返回任何值,而有些函数返回。重要的是了解返回的值是什么,和如何在你的代码中使用他们,以及如何使自定义的函数返回需要的值。

事件介绍

事件是你正在编写的系统中发生的动作或事件,系统告诉你的是这些动作或事件,如果需要的话,你可以以某种方式对它们做出反应。例如,如果用户单击网页上的按钮,您可能希望通过显示信息框来响应该操作。在这最后一篇文章中,我们将重点讨论一些围绕事件有关的概念,看看他们如何在浏览器中工作。

代码决策—条件语句

目标:了解怎样在JavaScript中使用条件语句的结构

人类(以及其他的动物)无时无刻都在做决定,这些决定都影响着他们的生活,从小事(“我应该吃一片还是两片饼干”)到重要的大事(“我应该留在我的祖国在我父亲的农场工作,还是应该去美国学习天体物理学”)
条件语句结构允许我们来描述在JavaScript中这样的选择,从不得不作出的选择(例如:“一片还是两片”)到产生的结果或这些选择(也许是“吃一片饼干”可能会“仍然感觉饿”,或者是“吃两片饼干”可能会“感觉饱了,但妈妈会因为我吃掉了所有的饼干而骂我”。

在这里插入图片描述

if … else 语句

基本的if else 语句

if (condition) {
code to run if condition is true
} else {
run some other code instead
}

关键字 if,并且后面跟随括号。
要测试的条件,放到括号里(通常是“这个值大于另一个值吗”或者“这个值存在吗”)。这个条件会利用比较运算符进行比较,并且返回true或者false。
一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且只会在条件语句返回true的时候运行。
关键字else。
另一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且当条件语句返回值不是true的话,它才会运行。
它说“如果(if)条件(condition)返回true,运行代码A,否则(else)运行代码B”

else if 语句

最后一个例子提供给我们两个选择或结果,但是如果我们想要两个以上呢?

<label for="weather">Select the weather type today: </label>
<select id="weather">
  <option value="">--Make a choice--</option>
  <option value="sunny">Sunny</option>
  <option value="rainy">Rainy</option>
  <option value="snowing">Snowing</option>
  <option value="overcast">Overcast</option>
</select>

<p></p>
var select = document.querySelector('select');
var para = document.querySelector('p');

select.addEventListener('change', setWeather);

function setWeather() {
  var choice = select.value;

  if (choice === 'sunny') {
    para.textContent = 'It is nice and sunny outside today. Wear shorts! Go to the beach, or the park, and get an ice cream.';
  } else if (choice === 'rainy') {
    para.textContent = 'Rain is falling outside; take a rain coat and a brolly, and don\'t stay out for too long.';
  } else if (choice === 'snowing') {
    para.textContent = 'The snow is coming down — it is freezing! Best to stay in with a cup of hot chocolate, or go build a snowman.';
  } else if (choice === 'overcast') {
    para.textContent = 'It isn\'t raining, but the sky is grey and gloomy; it could turn any minute, so take a rain coat just in case.';
  } else {
    para.textContent = '';
  }
}

在这里插入图片描述

嵌套 if … else

将另一个if … else 语句放在另一个中 - 嵌套它是完全可行的。例如,我们可以更新我们的天气预报应用程序,以显示更多的选择,具体取决于温度:

if (choice === 'sunny') {
  if (temperature < 86) {
    para.textContent = 'It is ' + temperature + ' degrees outside — nice and sunny. Let\'s go out to the beach, or the park, and get an ice cream.';
  } else if (temperature >= 86) {
    para.textContent = 'It is ' + temperature + ' degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.';
  }
}
逻辑运算符:&&,||和!
  • && — 逻辑与; 使得并列两个或者更多的表达式成为可能,只有当这些表达式每一个都返回true时,整个表达式才会返回true.
  • 逻辑或; 当两个或者更多表达式当中的任何一个返回 true 则整个表达式将会返回 true.
  • 逻辑非; 对一个布尔值取反, 非true返回false,非false返回true.

switch语句

if…else 语句能够很好地实现条件代码,但是它们不是没有缺点。 它们主要适用于您只有几个选择的情况,每个都需要相当数量的代码来运行,和/或 的条件很复杂的情况(例如多个逻辑运算符)。 对于只想将变量设置一系列为特定值的选项或根据条件打印特定语句的情况,语法可能会很麻烦,特别是如果您有大量选择。

switch 语句在这里是您的朋友 - 他们以单个表达式/值作为输入,然后查看多个选项,直到找到与该值相匹配的选项,执行与之相关的代码。 这里有一些伪代码,可以给你一点灵感:

switch (expression) {
  case choice1:
    run this code
    break;

  case choice2:
    run this code instead
    break;
    
  // include as many cases as you like

  default:
    actually, just run this code
}

这里我们得到:

关键字 switch, 后跟一组括号.
括号内的表达式或值.
关键字 case, 后跟一个选项的表达式/值,后面跟一个冒号.
如果选择与表达式匹配,则运行一些代码.
一个 break 语句, 分号结尾. 如果先前的选择与表达式/值匹配,则浏览器在此停止执行代码块,并执行switch语句之后的代码.
你可以添加任意的 case 选项(选项3-5).
关键字 default, 后面跟随和 case 完全相同的代码模式 (选项 3–5), except that default 之后不需要再有选项, 并且您不需要 break 语句, 因为之后没有任何运行代码. 如果之前没有选项匹配,则运行default选项.

switch语句示例
<label for="weather">Select the weather type today: </label>
<select id="weather">
  <option value="">--Make a choice--</option>
  <option value="sunny">Sunny</option>
  <option value="rainy">Rainy</option>
  <option value="snowing">Snowing</option>
  <option value="overcast">Overcast</option>
</select>

<p></p>

var select = document.querySelector('select');
var para = document.querySelector('p');

select.addEventListener('change', setWeather);


function setWeather() {
  var choice = select.value;

  switch (choice) {
    case 'sunny':
      para.textContent = 'It is nice and sunny outside today. Wear shorts! Go to the beach, or the park, and get an ice cream.';
      break;
    case 'rainy':
      para.textContent = 'Rain is falling outside; take a rain coat and a brolly, and don\'t stay out for too long.';
      break;
    case 'snowing':
      para.textContent = 'The snow is coming down — it is freezing! Best to stay in with a cup of hot chocolate, or go build a snowman.';
      break;
    case 'overcast':
      para.textContent = 'It isn\'t raining, but the sky is grey and gloomy; it could turn any minute, so take a rain coat just in case.';
      break;
    default:
      para.textContent = '';
  }
}
三元运算操作符

( condition ) ? run this code : run this code instead

来一起循环

编程中的循环也是一直重复着去做一件事 - 此处循环便是编程中的术语.
一段循环通常需要一个或多个条件:

一个开始条件,它被初始化为一个特定的值 - 这是循环的起点("开始:我没有食物”,上面)。
一个结束条件,这是循环停止的标准 - 通常计数器达到一定值。 以上所说的“我有足够的食物”吗? 假设他需要10份食物来养活他的家人。
一个迭代器,这通常在每个连续循环上递增少量的计数器,直到达到退出条件。 我们以前没有明确说明,但是我们可以考虑一下农民能够每小时收集2份食物。 每小时后,他收集的食物量增加了两倍,他检查他是否有足够的食物。 如果他已经达到10分(退出条件),他可以停止收集回家。

循环的标准

我们开始探索一些特定的循环结构。 第一个,你会经常使用到它,for循环 - 以下为for循环的语法:

for (initializer; exit-condition; final-expression) {
  // code to run
}

我们有:

关键字for,后跟一些括号。
在括号内,我们有三个项目,以分号分隔:
一个初始化器 - 这通常是一个设置为一个数字的变量,它被递增来计算循环运行的次数。它也有时被称为计数变量。
一个退出条件 -如前面提到的,这个定义循环何时停止循环。这通常是一个表现为比较运算符的表达式,用于查看退出条件是否已满足的测试。
一个最终条件 -这总是被判断(或运行),每个循环已经通过一个完整的迭代消失时间。它通常用于增加(或在某些情况下递减)计数器变量,使其更接近退出条件值。
一些包含代码块的花括号 - 每次循环迭代时都会运行这个代码。
我们来看一个真实的例子,所以我们可以看出这些做得更清楚。

var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
var info = 'My cats are called ';
var para = document.querySelector('p');

for (var i = 0; i < cats.length; i++) {
  info += cats[i] + ', ';
}

para.textContent = info;

这显示了一个循环用于迭代数组中的项目,并与每个项目进行一些操作 - JavaScript中非常常见的模式。 这里:

迭代器i从0开始(var i = 0)。
循环将会一直运行直到它不再小于猫数组的长度。 这很重要 - 退出条件显示循环仍然运行的条件。 所以在这种情况下,<cats.length仍然是真的,循环仍然运行。
在循环中,我们将当前的循环项(cats[i]是cats[当前下标的任何东西])以及逗号和空格连接到info变量的末尾。 所以:
在第一次运行中,i = 0,所以cats[0] +’,‘将被连接到info(“Bill”)上。
在第二次运行中,i = 1,所以cats[1] +’,'将被连接到info(“Jeff”)上。
等等。 每次循环运行后,1将被添加到i(i ++),然后进程将再次启动。
当等于cats.length时,循环将停止,浏览器将移动到循环下面的下一个代码位。

使用break退出循环

如果要在所有迭代完成之前退出循环,可以使用break语句。 当我们查看switch语句时,我们已经在上一篇文章中遇到过这样的情况 - 当switch语句中符合输入表达式的情况满足时,break语句立即退出switch语句并移动到代码之后。

与循环相同 - break语句将立即退出循环,并使浏览器移动到跟随它的任何代码。

使用continue跳过迭代

continue语句以类似的方式工作,而不是完全跳出循环,而是跳过当前循环而执行下一个循环。 我们来看另外一个例子,它把一个数字作为一个输入,并且只返回开平方之后为整数的数字(整数)。

var num = input.value;

for (var i = 1; i <= num; i++) {
  var sqRoot = Math.sqrt(i);
  if (Math.floor(sqRoot) !== sqRoot) {
    continue;
  }

  para.textContent += i + ' ';
}

在这里插入图片描述

while 语句和do···while语句

for 不是JavaScript中唯一可用的循环类型。 实际上还有很多其他的,而现在你不需要理解所有这些,所以值得看几个人的结构,这样你就可以在稍微不同的方式识别出相同的功能。

initializer
while (exit-condition) {
  // code to run

  final-expression
}

除了在循环之前设置初始化器变量,并且在运行代码之后,循环中包含final-expression,而不是这两个项目被包含在括号中,这与以前的for循环非常类似。 退出条件包含在括号内,前面是while关键字而不是for。

同样的三个项目仍然存在,它们仍然以与for循环中相同的顺序定义 - 这是有道理的,因为您必须先定义一个初始化器,然后才能检查它是否已到达退出条件; 在循环中的代码运行(迭代已经完成)之后,运行最后的条件,这只有在尚未达到退出条件时才会发生。

do…while循环非常类似但在while后提供了终止条件
initializer
do {
  // code to run

  final-expression
} while (exit-condition)

在这种情况下,在循环开始之前,初始化程序先重新开始。 do关键字直接在包含要运行的代码的花括号和终止条件之前。

这里的区别在于退出条件是一切都包含在括号中,而后面是一个while关键字。 在do … while循环中,花括号中的代码总是在检查之前运行一次,以查看是否应该再次执行(在while和for中,检查首先出现,因此代码可能永远不会执行)。

函数–可复用的代码块

浏览器内置函数

var myText = 'I am a string';
var newString = myText.replace('string', 'sausage');
console.log(newString);
// the replace() string function takes a string,
// replaces one substring with another, and returns
// a new string with the replacement made

var myArray = ['I', 'love', 'chocolate', 'frogs'];
var madeAString = myArray.join(' ');
console.log(madeAString);
// the join() function takes an array, joins
// all the array items together into a single
// string, and returns this new string

var myNumber = Math.random()
// the random() function generates a random
// number between 0 and 1, and returns that
// number

浏览器与方法

方法是在对象内定义的函数。浏览器内置函数(方法)和变量(称为属性)存储在结构化对象内,以使代码更加高效,易于处理。

自定义函数

function draw() {
  ctx.clearRect(0,0,WIDTH,HEIGHT);
  for (var i = 0; i < 100; i++) {
    ctx.beginPath();
    ctx.fillStyle = 'rgba(255,0,0,0.5)';
    ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
    ctx.fill();
  }
}

该函数在元素中绘制100个随机圆。每次我们想要这样做,我们可以使用这个函数来调用这个功能

调用函数

function myFunction() {
  alert('hello');
}

myFunction()
// calls the function once

匿名函数

形如:

unction() {
  alert('hello');
}

这个函数叫做匿名函数 — 它没有函数名! I它也不会自己做任何事情。 你通常使用匿名函数以及事件处理程序, 例如,如果单击相关按钮,以下操作将在函数内运行代码

var myButton = document.querySelector('button');

myButton.onclick = function() {
  alert('hello');
}

上述示例将要求 在页面上提供可用于选择并单击的元素。您在整个课程中已经看到过这种结构了几次,您将在下一篇文章中了解更多信息并在其中使用。

函数参数

一些函数需要在调用它们时指定参数 - 这些值需要包含在函数括号内,它需要正确地完成其工作。
浏览器的内置字符串replace()函数需要两个参数:在主字符串中查找的子字符串,以及用以下替换该字符串的子字符串:

var myText = 'I am a string';
var newString = myText.replace('string', 'sausage');

还应该注意,有时参数是可选的 - 您不必指定它们。如果没有,该功能一般会采用某种默认行为。作为示例,数组join()函数的参数是可选的

var myArray = ['I', 'love', 'chocolate', 'frogs'];
var madeAString = myArray.join(' ');
// returns 'I love chocolate frogs'
var madeAString = myArray.join();
// returns 'I,love,chocolate,frogs'

函数作用域和冲突

我们来谈一谈 scope即作用域 — 处理函数时一个非常重要的概念。当你创建一个函数时,函数内定义的变量和其他东西都在它们自己的单独的范围内, 意味着它们被锁在自己独立的隔间中, 不能从函数外代码其它函数内访问。

所有函数的最外层被称为全局作用域。 在全局作用域内定义的值可以在任意地方访问。

JavaScript由于各种原因而建立,但主要是由于安全性和组织性。有时您不希望变量可以在代码中的任何地方访问 - 您从其他地方调用的外部脚本可能会开始搞乱您的代码并导致问题,因为它们恰好与代码的其他部分使用了相同的变量名称,造成冲突。这可能是恶意的,或者是偶然的。

<!-- Excerpt from my HTML -->
<script src="first.js"></script>
<script src="second.js"></script>
<script>
  greeting();
</script>
// first.js
var name = 'Chris';
function greeting() {
  alert('Hello ' + name + ': welcome to our company.');
}
// second.js
var name = 'Zaptec';
function greeting() {
  alert('Our company is called ' + name + '.');
}

这两个函数都使用 greeting() 形式调用,但是你只能访问到 second.js文件的greeting()函数。second.js 在源代码中后应用到HTML中,所以它的变量和函数覆盖了 first.js 中的。

函数内部的函数

请记住,您可以从任何地方调用函数,甚至可以在另一个函数中调用函数。这通常被用作保持代码整洁的方式 - 如果您有一个复杂的函数,如果将其分解成几个子函数,它更容易理解

function myBigFunction() {
  var myValue;

  subFunction1();
  subFunction2();
  subFunction3();
}

function subFunction1() {
  console.log(myValue);
}

function subFunction2() {
  console.log(myValue);
}

function subFunction3() {
  console.log(myValue);
}

只需确保在函数内使用的值正确的范围. 上面的例子会抛出一个错误ReferenceError:MyValue没有被定义,因为尽管myValue变量与函数调用的作用域相同, 但函数定义内没有定义 - 调用函数时运行的实际代码。为了使这个工作,你必须将值作为参数传递给函数,如下所示:

function myBigFunction() {
  var myValue = 1;
      
  subFunction1(myValue);
  subFunction2(myValue);
  subFunction3(myValue);
}

function subFunction1(value) {
  console.log(value);
}

function subFunction2(value) {
  console.log(value);
}

function subFunction3(value) {
  console.log(value);
}

函数返回值

返回值听起来就像是函数执行完毕后返回的值。你已经多次遇见过返回值,尽管你可能没有明确的考虑过他们。让我们一起回看一些熟悉的代码:

var myText = 'I am a string';
var newString = myText.replace('string', 'sausage');
console.log(newString);
// the replace() string function takes a string,
// replaces one substring with another, and returns
// a new string with the replacement made

在第一篇函数文章中,我们确切地看到了这一块代码。我们调用replace()功能对mytext字符串,并通过这两个参数的字符串查找,和子串替换它。当这个函数完成(完成运行)后,它返回一个值,这个值是一个新的字符串,它具有替换的功能。在上面的代码中,我们保存这个返回值,以作为newString变量的内容。

如果你看看替换功能MDN参考页面,你会看到一个返回值。知道和理解函数返回的值是非常有用的,因此我们尽可能地包含这些信息。

一些函数没有返回值就像(在我们的参考页中,返回值在这种情况下被列出为空值或未定义值。).例如, 我们在前面文章中创建的 displayMessage() function , 由于调用的函数的结果,没有返回特定的值。它只是让一个盒子出现在屏幕的某个地方——就是这样!

通常,返回值是用在函数在计算某种中间步骤。你想得到最终结果,其中涉及到一些价值观。那些值需要通过一个函数计算得到,然后返回结果可用于计算的下一个阶段

事件介绍

事件是您在编程时系统内发生的动作或者发生的事情,系统通过它来告诉您在您愿意的情况下您可以以某种方式对它做出回应。例如:如果您在网页上单击一个按钮,您可能想通过显示一个信息框来响应这个动作。在这篇文章中我们将围绕事件讨论一些重要的概念,并且观察它们在浏览器上是怎么工作的。这篇文章并不做彻底的研究仅聚焦于您现阶段需要掌握的知识。

一系列事件

在Web中, 事件在浏览器窗口中被触发并且通常被绑定到窗口内部的特定部分 — 可能是一个元素、一系列元素、被加载到这个窗口的 HTML 代码或者是整个浏览器窗口。举几个可能发生的不同事件:

  • 用户在某个元素上点击鼠标或悬停光标。
  • 用户在键盘中按下某个按键。
  • 用户调整浏览器的大小或者关闭浏览器窗口。
  • 一个网页停止加载。
  • 提交表单。
  • 播放、暂停、关闭视频。
  • 发生错误。
    每个可用的事件都会有一个事件处理器,也就是事件触发时会运行的代码块。当我们定义了一个用来回应事件被激发的代码块的时候,我们说我们注册了一个事件处理器。注意事件处理器有时候被叫做事件监听器——从我们的用意来看这两个名字是相同的,尽管严格地来说这块代码既监听也处理事件。监听器留意事件是否发生,然后处理器就是对事件发生做出的回应。

一个简单的例子

<button>Change color</button>
// jacascript 代码
var btn = document.querySelector('button');

function random(number) {
  return Math.floor(Math.random()*(number+1));
}

btn.onclick = function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

我们使用btn变量存储 button,并使用了Document.querySelector()函数。我们也定义了一个返回随机数字的函数。代码第三部分就是事件处理器。btn变量指向 button 元素,在 button 这种对象上可触发一系列的事件,因此也就可以使用事件处理器。我们通过将一个匿名函数(这个赋值函数包括生成随机色并赋值给背景色的代码)赋值给“点击”事件处理器参数,监听“点击”这个事件。

只要点击事件在元素上触发,该段代码就会被执行。即每当用户点击它时,都会运行此段代码。

这不仅仅应用在网页上

值得注意的是并不是只有 JavaScript 使用事件——大多的编程语言都有这种机制,并且它们的工作方式不同于 JavaScript。实际上,JavaScript 网页上的事件机制不同于在其他环境中的事件机制。

比如, Node.js 是一种非常流行的允许开发者使用 JavaScript 来建造网络和服务器端应用的运行环境。Node.js event model 依赖定期监听事件的监听器和定期处理事件的处理器——虽然听起来好像差不多,但是实现两者的代码是非常不同的,Node.js 使用像 on ( ) 这样的函数来注册一个事件监听器,使用 once ( ) 这样函数来注册一个在运行一次之后注销的监听器。 HTTP connect event docs 提供了很多例子。

另外一个例子:您可以使用 JavaScript 来开发跨浏览器的插件(使用 WebExtensions 开发技术。事件模型和网站的事件模型是相似的,仅有一点点不同——事件监听属性是大驼峰的(如onMessage而不是onmessage),还需要与 addListener 函数结合。

使用网页事件的方式

事件处理器属性

var btn = document.querySelector('button');

btn.onclick = function() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

这个 onclick 是被用在这个情景下的事件处理器的属性,它就像 button 其他的属性(如 btn.textContent, or btn.style), 但是有一个特别的地方——当您将一些代码赋值给它的时候,只要事件触发代码就会运行。

您也可以将一个有名字的函数赋值给事件处理参数(正如我们在 Build your own function 中看到的),下面的代码也是这样工作的:

var btn = document.querySelector('button');

function bgChange() {
  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

btn.onclick = bgChange;

其他事件概念

事件对象

有时候在事件处理函数内部,您可能会看到一个固定指定名称的参数,例如event,evt或简单的e。 这被称为事件对象,它被自动传递给事件处理函数,以提供额外的功能和信息。 例如,让我们稍稍重写一遍我们的随机颜色示例

  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  e.target.style.backgroundColor = rndCol;
  console.log(e);
}  

btn.addEventListener('click', bgChange);

在这里,您可以看到我们在函数中包括一个事件对象e,并在函数中设置背景颜色样式在e.target上 - 它指的是按钮本身。 事件对象 e 的target属性始终是事件刚刚发生的元素的引用。 所以在这个例子中,我们在按钮上设置一个随机的背景颜色,而不是页面。
当您要在多个元素上设置相同的事件处理程序时,e.target非常有用,并且在发生事件时对所有元素执行某些操作. 例如,你可能有一组16块方格,当它们被点击时就会消失。用e.target总是能准确选择当前操作的东西(方格)并执行操作让它消失,而不是必须以更困难的方式选择它。在下面的示例中(请参见useful-eventtarget.html完整代码;也可以在线运行running live)我们使用JavaScript创建了16个

元素。接着我们使用 document.querySelectorAll()选择全部的元素,然后遍历每一个,为每一个元素都添加一个onclick单击事件,每当它们点击时就会为背景添加一个随机颜色。

var divs = document.querySelectorAll('div');

for (var i = 0; i < divs.length; i++) {
  divs[i].onclick = function(e) {
    e.target.style.backgroundColor = bgChange();
  }
}

在这里插入图片描述
这里返回一个div元素的集合,然后循环等待点击,一旦点击,这里需要理解e.target引用的是刚刚改变元素的那个元素。
你遇到的大多数事件处理器的事件对象都有可用的标准属性和函数(方法)(请参阅完整列表Event对象引用 )。然而,一些更高级的处理程序会添加一些专业属性,这些属性包含它们需要运行的额外数据。例如,媒体记录器API有一个dataavailable事件,它会在录制一些音频或视频时触发,并且可以用来做一些事情(例如保存它,或者回放)。对应的ondataavailable处理程序的事件对象有一个可用的数据属性。

阻止默认行为

有时,你会遇到一些情况,你希望事件不执行它的默认行为。 最常见的例子是Web表单,例如自定义注册表单。 当你填写详细信息并按提交按钮时,自然行为是将数据提交到服务器上的指定页面进行处理,并将浏览器重定向到某种“成功消息”页面(或 相同的页面,如果另一个没有指定。)

当用户没有正确提交数据时,麻烦就来了 - 作为开发人员,你希望停止提交信息给服务器,并给他们一个错误提示,告诉他们什么做错了,以及需要做些什么来修正错误。 一些浏览器支持自动的表单数据验证功能,但由于许多浏览器不支持,因此建议你不要依赖这些功能,并实现自己的验证检查。 我们来看一个简单的例子。
首先,一个简单的HTML表单,需要你填入名(first name)和姓(last name)

<form>
  <div>
    <label for="fname">First name: </label>
    <input id="fname" type="text">
  </div>
  <div>
    <label for="lname">Last name: </label>
    <input id="lname" type="text">
  </div>
  <div>
     <input id="submit" type="submit">
  </div>
</form>
<p></p>

这里我们用一个onsubmit事件处理程序(在提交的时候,在一个表单上发起submit事件)来实现一个非常简单的检查,用于测试文本字段是否为空。 如果是,我们在事件对象上调用preventDefault()函数,这样就停止了表单提交,然后在我们表单下面的段落中显示一条错误消息,告诉用户什么是错误的:

var form = document.querySelector('form');
var fname = document.getElementById('fname');
var lname = document.getElementById('lname');
var submit = document.getElementById('submit');
var para = document.querySelector('p');

form.onsubmit = function(e) {
  if (fname.value === '' || lname.value === '') {
    e.preventDefault();
    para.textContent = 'You need to fill in both names!';
  }
}

事件冒泡及捕获

是一个非常简单的例子,它显示和隐藏一个包含元素的

元素:

<button>Display video</button>

<div class="hidden">
  <video>
    <source src="rabbit320.mp4" type="video/mp4">
    <source src="rabbit320.webm" type="video/webm">
    <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
  </video>
</div>

当‘’button‘’元素按钮被单击时,将显示视频,它是通过将改变

的class属性值从hidden变为showing(这个例子的CSS包含两个class,它们分别控制这个
盒子在屏幕上显示还是隐藏。):

btn.onclick = function() {
  videoBox.setAttribute('class', 'showing');
}

然后我们再添加几个onclick事件处理器,第一个添加在

元素上,第二个添加在元素上。这个想法是当视频()外
元素内这块区域被单击时,这个视频盒子应该再次隐藏;当单击视频()本身,这个视频将开始播放。

videoBox.onclick = function() {
  videoBox.setAttribute('class', 'hidden');
};

video.onclick = function() {
  video.play();
};

对事件的冒泡和捕捉的解释

当一个事件发生在具有父元素的元素上(例如,在我们的例子中是元素)时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。 在捕获阶段:

浏览器检查元素的最外层祖先,是否在捕获阶段中注册了一个onclick事件处理程序,如果是,则运行它。
然后,它移动到中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。
在冒泡阶段,恰恰相反:

浏览器检查实际点击的元素是否在冒泡阶段中注册了一个onclick事件处理程序,如果是,则运行它
然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达元素。
在这里插入图片描述

用stopPropagation()修复问题

这是令人讨厌的行为,但有一种方法来解决它!标准事件对象具有可用的名为 stopPropagation()的函数, 当在事件对象上调用该函数时,它只会让当前事件处理程序运行,但事件不会在冒泡链上进一步扩大,因此将不会有更多事件处理器被运行(不会向上冒泡)。所以,我们可以通过改变前面代码块中的第二个处理函数来解决当前的问题:

video.onclick = function(e) {
  e.stopPropagation();
  video.play();
};

注解: 为什么我们要弄清楚捕捉和冒泡呢?那是因为,在过去糟糕的日子里,浏览器的兼容性比现在要小得多,Netscape(网景)只使用事件捕获,而Internet Explorer只使用事件冒泡。当W3C决定尝试规范这些行为并达成共识时,他们最终得到了包括这两种情况(捕捉和冒泡)的系统,最终被应用在现在浏览器里。
注解: 如上所述,默认情况下,所有事件处理程序都是在冒泡阶段注册的,这在大多数情况下更有意义。如果您真的想在捕获阶段注册一个事件,那么您可以通过使用addEventListener()注册您的处理程序,并将可选的第三个属性设置为true。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值