JavaScript 诞生于 1995 年。由Netscape(网景公司)的程序员Brendan Eich(布兰登)与Sun公司联手开发一门脚本语言, 最初名字叫做Mocha,1995年9月改为LiveScript。12月,Netscape公司与Sun公司(Java语言的发明者)达成协议,后者允许将这种语言叫做JavaScript。这样一来,Netscape公司可以借助Java语言的声势。
1996年3月, Netscape公司的浏览器Navigator2.0浏览器正式内置了JavaScript脚本语言. 此后其他主流浏览器逐渐开始支持JavaScript.
JavaScript这种语言的基本语法结构是由ECMAScript来标准化的, 所以我们说的JavaScript版本一般指的是ECMAScript版本.
- 1997年7月,ECMAScript 1.0发布。
- 1998年6月,ECMAScript 2.0版发布。
- 1999年12月,ECMAScript 3.0版发布。
- 2007年10月,ECMAScript 4.0版草案想要提交ECMA组织, 但由于4.0版的目标过于激进, 改动太大, 并且微软,谷歌等大公司极力反对;一直到2008年7月ECMA开会决定,中止ECMAScript 4.0的开发(即废除了这个版本)
- 2009年12月,ECMAScript 5.0版正式发布
- 2011年6月,ECMAscript 5.1版发布
- 2015年6月,ECMAScript 6正式发布,并且更名为“ECMAScript 2015”。
目前苹果公司的Safari, 谷歌的Chrome,微软的IE等几乎全部浏览器都支持JavaScript, 基于JavaScript开发的库和框架数不胜数, 例如: jQuery,Angular, React等…
JavaScript将在前端和服务器端(Node.js)有更好的发展
它的主要目的是,验证发往服务器端的数据、增加 Web互动、加强用户体验度等。可用于开发网站、游戏、移动端app等
javascript = ECMAScript + BOM + DOM
- 核心(ECMAScript)
- 浏览器对象模型(BOM)
- 文档对象模型(DOM)
h5全称html5,不是单纯的html的第5个版本,而是一项最新的web标准,是html、css、javascript等技术的集合
- sublime Text
- HBuilder
- Dreamweaver
- Notepad++
- …
- script标签
<script type="text/javascript">
alert('你好')
script>
- js文件
独立的js文件需要引入页面才能执行
<script type="text/javascript" src="js/common.js"><script>
script标签属性
- type:类型
- src :js文件路径
带src属性的script标签内不能写js代码
- 变量定义(使用var关键字):变量是存储数据的容器
//var:关键字,
//age:变量名
var age;
- 赋值:
age = 20;
- 定义的同时赋值:
var age=20;
- 可以一次定义多个变量:
var name="zhangsan", age=18, weight=108;
- JS变量的命名规范(规定)
_
和美元符$
组成;
- 代码可读性(约定)
var n = 1 + 2
;/*注释内容*/
- alert():弹窗输出
- console.log():控制台输出
- 输出到页面元素
- document.write() 输出到body内
- 值类型
- String:字符串
用引号(单/双引号)括起来的内容
- 引用数据类型
- 特殊数据类型
typeof 'html5'; //=>string
typeof 100; //=>number
typeof true //=>boolean
typeof null //=>object
- 基本数据类型转换:利用内置函数进行转换
|值(a) |转换为 |字符串String(a)|数字Number(a)|布尔值Boolean(a)
|— |:--------- |---- |---- |
|undefined|=> |“undefined” |NaN |false
|null |=> |“null” |0 |false
|true |=> |“true” |1 |
|false |=> |“false” |0 |
|"" |=> | |0 |false
|“1.2” |=> | |1.2 |true
|“one” |=> | |NaN |true
|0 |=> |“0” | |false
|-0 |=> |“0” | |false
|NaN |=> |“NaN” | |false
|1 |=> |“1” | |true
- 隐式转换
如果运算不能进行下去,内部就会尝试进行数据类型的转换
支持隐式转换的运算:逻辑运算、关系运算、算术运算
- if单分支:
if(条件){
//条件成立(返回true)时,执行这里的代码,否则不执行
}
- if双分支:
当if括号内的表达式结果成立,执行执行代码1,否则执行执行代码2
if(条件){
//代码1
//条件成立(返回true)时,执行这里的代码,忽略以下代码
}else{
//代码2
//条件不成立(返回false)时,执行这里的代码
}
- if多分支:
从上往下,满足哪个条件就执行其相对应的语句,都不满足时,执行最后的else的语句,只能进入其中之一
if(条件1){
//条件1成立(返回true)时,执行这里的代码,忽略以下代码
}else if(条件2){
//条件2成立(返回true)时,执行这里的代码,忽略以下代码
}
...
else{
//以上条件都不成立(都返回false)时,执行这里的代码
}
格式:条件 ? 条件成立代码 : 条件不成立代码
var a=20;
var b = 50;
var sum = a>b ? a-b : a+b;
switch(值) {
case value1: //要求value1与值恒等
//如果表达式的值恒等于value1,代码从这里开始执行
break;
case value2:
//如果表达式的值恒等于value2,代码从这里开始执行
break;
case value3:
//如果表达式的值恒等于value3,代码从这里开始执行
break;
case value4:
//如果表达式的值恒等于value4,代码从这里开始执行
break;
default:
//如果以上条件都不成立,默认执行这里的代码
}
- switch语句在比较值时使用的是全等操作符,因此不会发生类型转换
- case: 当符合条件时,会从符合条件的那一条case语句开始,依次顺序向下执行,直到结束或遇到break
- break: 跳出switch语句
- default: 当所有的case都不满足的情况下会执行defalut下面的语句
- 循环就是重复做一件事, 在JS中指的是循环执行某部分代码.
- 循环结构是程序中一种很重要的结构,其特点是在给定条件成立时,反复执行某程序段,直到条件不成立为止
只要条件成立,就会不断地执行花括号里的语句
编写条件时,要避免出现死循环
//变量初始化
while(条件){
//条件成立就会不断地执行这里的代码,直到条件不成立
//所以这里一般会伴随着条件的更新
}
//变量初始化
do {
//不管条件是否成立,先执行一次这里的代码,再进行条件判断,如果条件依然成立,则再次执行这里的代码,依此类推
//所以这里一般会伴随着条件的更新
} while(条件)
for(变量初始化; 条件判断; 变量更新){
//循环条件成立,则执行这里的代码
}
- 两个分号必须写
- break://退出当前整个循环
- continue://跳过本次循环,继续下一次循环
- label:给循环代码添加标识
break和continue后如果带标识,则跳到标识所在循环
break与continue的区别(如图) |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HfWZcN2N-1587994598278)(Pictures\break_continue.png “break与continue的区别”)] |
PS:
- 知道次数的循环用for循环,不知道次数用while循环
- 死循环
- 死循环就是重复执行代码, 不会停止.
- 死循环会造成程序卡死甚至崩溃等问题, 所以我们写代码要避免死循环
for(var i=0;i<10;i++){
for(var j=0;j<10;j++){
console.log(i,j);
}
}
函数就是把特定功能的代码抽取出来并进行封装,用来重复执行一些功能,并起个名字(函数名)。函数对任何语言来说都是一个核心的概念。通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行
- 使用函数的好处,为什么要使用函数
- 什么时候需要函数
- 关键字声明(声明式):
格式:function 函数名(){}
function sum(){}
函数的声明会提前 ==> 解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);
- 函数表达式(赋值式)
var sum = function(){}
- 匿名函数:
没有名字的函数就叫匿名函数
function(){}
- 手动调用:
sum();
- 事件驱动:
格式:元素.事件 = 函数名;
buton.onclick = sum;
- 函数声明提前
- 变量声明提前
声明但没有赋值的变量默认为undefined
function test(){
console.log(a);
var a=20;
}
test() ==> 会得到什么信息?
俗称“使用范围”,即能够使用某个变量的范围,分<全局作用域>和<局部作用域>
- 全局变量与局部变量
- 全局变量:在全局作用域下声明的变量,可以在任意地方中使用,作用范围比较大,我们称为全局变量
- 局部变量:在函数内(局部作用域)声明的变量,只在函数中可以使用,作用范围较小,我们称之为局部变量
- 变量的访问规则
- 就近原则(如查找变量a):
- 使用变量a时先从当前函数查找,如果当前函数有变量a则使用;
- 如果当前函数无变量a,则往父级函数查找,如果找到则使用,并停止查找;
- 如果在父级函数还是无法找到,则继续往上一层函数查找,以此类推,直到最顶层(全局作用域),如果还是没找到,则报not defined错误;
- 作用域链:每个函数在定义时就形成了局部作用域,如果存在多个函数嵌套,他们之间就会建立起某种联系,直到全局作用域,这种联系称之为作用域链。当函数访问变量时,根据就近原则在这个作用域链中从内到外查询变量。
闭包是js开发惯用的技巧,什么是闭包?闭包指的是:能够访问另一个函数作用域的变量的函数。清晰的讲:闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量。eg:
function outer() {
var a = '变量1'
var inner = function () {
console.info(a)
}
return inner // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}
- 形参,就是局部变量
- 形参与实参的区别:
- 形参:声明函数时圆括号内定义的变量
- 实参:函数执行时传入的参数
形参和实参的数量可以不同- arguments
函数内部隐藏的对象(是一个类数组),保存着实参的信息
- length: 实参的数量
- 了解索引值
- 读取值
- 回调函数:函数作为参数传递 eg:
//注意到click方法中是一个函数而不是一个变量
//它就是回调函数
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
//或者
function click() { // 它就是回调函数
alert("Btn 1 Clicked");
}
$("#btn_1").click(click);
- 终止函数的执行,return后的代码不会执行
- return后如果有值,则把这个值返回到函数执行的地方
如果函数没有return,执行完后返回undefined
函数中的this是一个关键字,表示当前对象,而当前对象是谁,取决于谁调用了这个函数
parseInt() 函数可解析一个字符串,并返回一个整数。
parseInt(string, radix)
参数 | 描述 |
---|---|
string | 必需。要被解析的字符串。 |
radix | 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。 |
返回解析后的数字。
当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。
举例,如果 string 以 “0x” 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数。如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现把其后的字符解析为八进制或十六进制的数字。如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。
**注释:**只有字符串中的第一个数字会被返回。
**注释:**开头和结尾的空格是允许的。
**提示:**如果字符串的第一个字符不能被转换为数字,那么 parseFloat() 会返回 NaN。
在本例中,我们将使用 parseInt() 来解析不同的字符串:
parseInt("10"); //返回 10
parseInt("19",10); //返回 19 (10+9)
parseInt("11",2); //返回 3 (2+1)
parseInt("17",8); //返回 15 (8+7)
parseInt("1f",16); //返回 31 (16+15)
parseInt("010"); //未定:返回 10 或 8
parseFloat() 函数可解析一个字符串,并返回一个浮点数。
该函数指定字符串中的首个字符是否是数字。如果是,则对字符串进行解析,直到到达数字的末端为止,然后以数字返回该数字,而不是作为字符串。
parseFloat(string)
参数 | 描述 |
---|---|
string | 必需。要被解析的字符串。 |
parseFloat 是全局函数,不属于任何对象。
parseFloat 将它的字符串参数解析成为浮点数并返回。如果在解析过程中遇到了正负号(+ 或 -)、数字 (0-9)、小数点,或者科学记数法中的指数(e 或 E)以外的字符,则它会忽略该字符以及之后的所有字符,返回当前已经解析到的浮点数。同时参数字符串首位的空白符会被忽略。
如果参数字符串的第一个字符不能被解析成为数字,则 parseFloat 返回 NaN。
**提示:**您可以通过调用 isNaN 函数来判断 parseFloat 的返回结果是否是 NaN。如果让 NaN 作为了任意数学运算的操作数,则运算结果必定也是 NaN。
返回解析后的数字。
**注释:**开头和结尾的空格是允许的。
**提示:**如果字符串的第一个字符不能被转换为数字,那么 parseFloat() 会返回 NaN。
**提示:**如果只想解析数字的整数部分,请使用 parseInt() 方法。
在本例中,我们将使用 parseFloat() 来解析不同的字符串:
parseFloat("10") //返回 10
parseFloat("10.00") //返回 10
parseFloat("10.33") //返回 10.33
parseFloat("34 45 66") //返回 34
parseFloat(" 60 ") //返回 60
parseFloat("40 years") //返回 40
parseFloat("He was 40") //返回 NaN
isNaN() 函数用于检查其参数是否是非数字值。
isNaN(x)
参数 | 描述 |
---|---|
x | 必需。要检测的值。 |
如果 x 是特殊的非数字值 NaN(或者能被转换为这样的值),返回的值就是 true。如果 x 是其他值,则返回 false。
isNaN() 函数可用于判断其参数是否是 NaN,该值表示一个非法的数字(比如被 0 除后得到的结果)。
如果把 NaN 与任何值(包括其自身)相比得到的结果均是 false,所以要判断某个值是否是 NaN,不能使用 == 或 === 运算符。正因为如此,isNaN() 函数是必需的。
**提示:**isNaN() 函数通常用于检测 parseFloat() 和 parseInt() 的结果,以判断它们表示的是否是合法的数字。当然也可以用 isNaN() 函数来检测算数错误,比如用 0 作除数的情况。
检查数字是否非法:
isNaN(123); //返回 false
isNaN(-1.23); //返回 false
isNaN(5-2); //返回 false
isNaN(0); //返回 false
isNaN("Hello"); //返回 true
isNaN("2005/12/12"); //返回 true
把字符串作为URI进行编码并返回(URL就是一种常见的URI),URIstring是一个含有URI或者其他要编码的文本的字符串,目的是对URI完整编码,不转义字符。
对encodeURI()函数编码过的URI进行解码,URIstring是一个含有URI或其他要解码的文本的字符串。
事件是可以被JavaScript侦测到的行为。网页中的每个元素都可以产生某些可以触发JavaScript函数的事件
格式:节点.on+事件名 = 事件处理函数;
div.onclick = function(){}
- 鼠标事件
PS:click = mousedown + mouseup, dblclick = click*2(短时间内两次单击);
执行顺序:mouseover=>mouseenter; mouseout => mouseleave
- 键盘事件
- UI事件
- 表单事件
- 其他事件
字符串就是一串字符,由双(单)引号括起来。
创建一个字符串
//方式一:字面量(推荐)
var str = '城市套路深,我想回农村';
//方式二:构造函数
//PS:用new产生的变量都是引用类型的变量,也叫对象
var str = new String('我不是黄蓉,我不会武功');
- length: 表示字符串的长度,只读(只能读取)
- charAt(3) //获取下标为3的字符
- indexOf/lastIndexOf(keyword [,startIndex]) 从开头/尾部向后查找字符串
keyword
第一次出现的位置,如果没找到返回-1- search(str|regExp) 查找字符串第一次出现的位置
与indexOf的区别:search方法支持正则表达式
- match(str|regExp) 匹配字符串,返回一个数组
index:匹配字符所在的索引
input:表示整个字符串的引用- replace(str|regExp,’’) 替换字符串
这里的替换只能执行一次,不能够进行全局匹配,如果需要全局匹配,则应使用正则表达式
- substring(start[,end]):不包括end所在字符,end省略表示截取到最后
- substr(start[,len]):支持负数,len为截取的数量
- slice(start[,end]) :截取start到end(不包括end)的字符串,支持负数
- split(分割符):根据分割字符,把字符串拆分成数组
- toLowerCase():转换成小写
- toUpperCase():转换成大写
- str[3]//通过下标获取
- trim():删除前后所有空格,返回新的字符串
- charCodeAt(3) //获取下标为3的字符的ASCII(American Standard Code for * Information Interchange) == > unicode编码
- String.fromCharCode(94) //编码转换成字符
[ascii码, GBK及Unicode由来]
字符编码是计算机技术的基石,想要熟练使用计算机,就必须懂得一点字符编码的知识。
- 什么是正则表达式
正则表达式(regular expression)是一个描述字符模式的对象。- 为什么要使用正则表达式
正则表达式能够进行强大的“模式匹配”和“文本检索与替换”功能。前端往往有大量的表单数据校验的工作,采用正则表达式会使得数据校验的工作量大大减轻
- 第一个参数就是我们的模式“字符串”
var reg= new RegExp('study');
//使用特殊字符
var reg= new RegExp('\\d\\w+');
- 第二个参数可选,模式修饰符
var reg = new RegExp('study', 'ig');
var reg = /study/gi;
- 直接量是字符匹配,不支持变量
- search
返回第一次匹配时所在的索引值,如果匹配不到则返回-1- match
- replace
替换字符串- split
'a,b ,c , d, e'.split(/\s*,\s* /);
- 测试正则表达式用test方法,返回布尔值
var reg = /study/gi
reg.test('study il')//返回 true
reg.test('aaaaa')//返回 false
- 测试正则表达式exec方法,返回匹配字符
var reg = /study/gi
reg.exec('study il')//返回 对象(包含匹配study)
reg.exec('aaaaa')//返回 null
- 原字匹配:所有字母和数字都是按照字面量进行匹配,和字符串匹配等效
/good/gi
- 字符类(只记小写字母即可)
.
: 除换行以外的字符PS:以上所有字符类都只是匹配“一个”字符
- 边界处理
- 特殊符号
^ $ . * + ? = ! : | \ / () [] {}
- []: 代表任意“单个字符” ,里面的内容表示“或”的关系
- -: 代表范围
- ^: 代表非- (): 表示分组(n是以最左边括号出现的顺序排列)
- $1: 表示第一个分组
- $n: 表示第n个分组(不能写在正则表达式里)
- \n: 在正则分组后面使用,表示对第n个分组的引用(一定要写在正则表达式里)
PS: 编写的正则分组数量越少越好
- |: 表示或者
- 锚点定位
- ^: 表示以什么开头
- $: 表示以什么结尾
- 表示数量,对前一个字符计数,
- *: 代表0个或0个以上 <=>{0,}
- +: 代表1个或1个以上 <=>{1,}
- ?: 代表0个或1个 <===>{0,1}
- {}:
\d{
5}: 匹配5个数字
\d{
5,10}: 匹配5个到10个数字
\d{
5,}: 匹配5个或5个以上的数字
PS:
1)数量词*,+,{5,},会尽可能多的去匹配结果(贪婪)
2)在后面加一个?表示尽可能少的去匹配结果(非贪婪)
google,goooogle ==> /go+/
BOM 是Browser Object Model(浏览器对象模型)的缩写,提供与浏览器窗口进行交互的对象。JavaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C, 而BOM缺乏标准。这也是各种浏览器不兼容的根源所在
window对象是BOM的核心, 是最顶层的对象,所有对象都是通过它延伸出来的,如图:
window 对象 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXzX6PY6-1587994598280)(Pictures\window.png “window对象”)] |
- 定义在全局环境下的变量都会成为window对象的属性
- 把变量定义在函数体里,可以有效减少全局环境下的变量冲突,避免污染全局环境
- window对象可以在代码中省略,如window.alert()可以写成alert();
- 在函数内部不用var声明的变量会成为全局变量,即window对象的属性
- 通过var在全局作用域下声明的变量用delete无法删除
var obj = {
name:'xxx'}
//删除对象的属性用delete:
delete obj.name;
常用属性
- 浏览器窗口尺寸
innerWidth/innerHeight, //表示浏览器窗口"可视区域"尺寸
outerWidth/outerHeight //表示整个浏览器窗口的尺寸- 滚动相关
系统对话框(了解)
- alert(msg)//弹出对话框
- confirm(msg)//弹出警告框,返回布尔值
- prompt(msg,default)//弹出输入框,返回消息或null
以上三个方法都会暂停代码的执行
open(url,name,[options])
: 打开一个新窗口并返回新 window 对象
'width=400,height=400,top=200,left=200'
- close(): 关闭窗口
- print(): 调出打印对话框
属性对象
- document(核心): 文档对象,让我们可以在js脚本中直接访问页面元素(DOM)
在DOM章节详细讲解
- history(重要): 历史对象,包含窗口的浏览历史,可以实现后退
history.go(2);//向前两个页面
history.go(-2);//后退两个页面
- location(重要):
location是BOM最有用的对象之一,保存着当前窗口中加载文档的相关信息,还提供一些导航功能,它是个很特别的对象,既是window的属性,也是document的属性
- 属性:
- hash 设置或返回从井号 (#) 开始的 URL(锚)==>哈希值。
- href 设置或返回完整的 URL。
- search 设置或返回从问号 (?) 开始的 URL(查询部分)。
encodeURI();//转码
decodeURI();//解码
PS:修改以上属性(hash除外)都会刷新当前页面,并生成历史纪录
- 方法:
- reload() 重新加载当前文档,带参数true表示不使用缓存刷新页面。
- navigator(了解):
导航对象, 包含所有有关访问者浏览器的信息,通常用于检测浏览器类型
- onload //页面资源全部加载完成后触发这个事件
- onscroll//窗口滚动条滚动时触发
- onresize //窗口大小改变时触发
- GMT:格林尼治标准时(Greenwich Mean Time),俗称“天文学时间”
- UTC:协调世界时(Universal Time Coordinated),“原子物理时间”,它更加精确,50亿年才误差1秒
- 时区:为了克服时间上的混乱,1884年在华盛顿召开的一次国际经度会议(又称国际子午线会议[1])上,规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为中时区(零时区)、东1-12区,西1-12区。每个时区横跨经度15度,时间正好是1小时
- 闰年:四年一闰,百年不闰,四百年再闰
- 纪元时间(UNIX TIME):1970-1-1零时
//1)创建当前时间的日期和时间
var d = new Date();//得到的是代码执行时的时间(本地时间)
//2)创建指定日期的时间和日期
var d = new Date("2015/08/22");
var d = new Date(56521211021);//参数为距1970-1-1零时的毫秒数
- 获取年月日
- 获取星期
- 获取时分秒
- getTime()/setTime():获取/修改某个日期自1970年1月1日0时以来的毫秒数
- toLocaleDateString(); 以特定地区格式显示年、月、日
- toUTCString(); 转换成UTC时间
ES5方法
- Date.parse(“2015-08-24”)//返回指定日期距1970-1-1零时的毫秒数
PS:转换格式默认支持2015-08-24或2015/08/24
- Date.now();//返回执行这行代码时距1970-1-1零时的毫秒数
- setTimeout(fn,200):两百毫秒后执行fn这个函数(只执行一次),返回一个id标识
- clearTimeout(timeoutID):清除指定id标识的延迟操作
- setInterval(fn,30):每隔30毫秒执行一次fn这个函数,返回一个id标识
- clearInterval(intervalID):清除指定id标识的定时器操作
var timer = setTimeout(function(){
//2s后执行这里的代码
},2000);
//清除
clearTimeout(timer);
[案例]
- 将当前日期格式化输出为 “2015年08月24 星期五”格式
- 判断两个日期相差的天数
- 封装一个函数afterDate(date,n),得到日期date的n天后的日期
- 使用定时器实现进度条效果
- 秒杀活动倒计时
- 倒计时结束后显示购买按钮
DOM是Document Object Model(文档对象模型)的缩写,它是W3C国际组织的一套Web标准。是针对HTML和XML文档的一个API,它定义了访问HTML文档对象的一套属性、方法和事件。
DOM结构 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qslE2WOp-1587994598282)(Pictures\dom_tree.png “Dom结构”)] |
- 每个节点都有一个nodeType属性,用于表明节点的类型。
- 常用节点类型与对应nodeType值:
- 用于判断获取到的元素属于什么类型节点
- 元素节点 <> 1
- 文本节点 <> 3
- 属性节点 <==> 2
<div class="content" title="属性节点">测试Divdiv>
<div class="content" id="testId" name="testName" title="属性节点">测试Divdiv>
- document.getElementById(id)
- getElementsByTagName(tagname)
- getElementsByClassName()
- document.getElementsByName()
注意: 如果确认元素存在, 但是返回null或[],一定是代码执行顺序的问题
- nodeName 返回节点的名称,根据其类型。
- nodeType 返回节点的类型。
- nodeValue 返回节点的值(元素节点的nodeValue为null)
- 创建:
- 插入:
对页面已存在节点的处理
- 复制
- 删除:
- 判断:
以上parent表示父级元素,ele表示元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-53Wh8mEM-1587994598284)(Pictures\节点关系.png “节点关系”)]
- 获取父级节点
- 获取子节点
- 获取兄弟节点
可以通过点语法或方括号访问
- id 设置/获取元素id属性
- name 设置/获取元素name属性
- style 设置/获取元素的内联样式
- className 设置/获取元素的class属性
- title
- value
- checked
- type
- src
- …
- tagName 获取元素元素的标签名
- innerHTML 设置/获取元素的内容(包含html代码)
- innerText 设置或获取位于元素标签内的文本
- outerHTML 设置或获取元素及其内容(包含html代码)
- outerText 设置(包括标签)或获取(不包括标签)元素的文本
- …
- offsetTop: 当前元素离<定位父级>元素顶部的距离。
- offsetLeft: 当前元素离<定位父级>元素左边的距离。
以上两个属性如果没定位的父级,则相对于根元素html的距离
- offsetWidth: 当前元素的宽度(border + padding + content)
- offsetHeight: 当前元素的高度(border + padding + content)
- ele.getAttribute(attr) //获取元素的属性值(自定义属性获取)
- ele.setAttribute(attr,val); //设置元素的属性
- ele.removeAttribute(attr) //删除属性attr
- ele.hasAttribute(attr) //判断是否存在属性attr
- parentElement 获取父级节点元素
- children 获取元素的全部子元素
- firstElementChild 获取第一个子元素
- lastElementChild 获取最后一个子元素
- previousElementSibling 获取前一个元素
- nextElementSibling 获取下一个元素
得到当前元素计算后的所有样式
- getComputedStyle(ele,pseudo) (标准)
- ele.currentStyle (IE8-)
- rows 返回包含表格中所有行的一个数组
- tBodies 返回包含表格中所有 tbody 的一个数组
- insertRow(index) 在表格中插入一个新行。
- deleteRow(index) 从表格删除一行。
- cells 返回包含表格中所有单元格的一个数组
- rowIndex 返回该行在表中的位置
- sectionRowIndex 返回在 tBody 、tHead 或 tFoot 中行的位置。
- insertCell(index) 在一行中的指定位置插入一个空的列
- deleteCell(index) 删除行中的指定的单元格
- cellIndex 返回单元格在表格行的单元格集合中的位置。
事件冒泡
和事件捕获
分别由微软
和网景
公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。
考虑下面这段代码,就不写html->head,body之类的代码了,自行脑补
<div id="outer">
<p id="inner">Click me!p>
div>
上面的代码当中一个div元素当中有一个p子元素,如果两个元素都有一个click的处理函数,那么我们怎么才能知道哪一个函数会首先被触发呢?
为了解决这个问题微软和网景提出了两种几乎完全相反的概念。
微软提出了名为
事件冒泡(event bubbling)
的事件流。事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。
因此在事件冒泡的概念下在p元素上发生click事件的顺序应该是p -> div -> body -> html -> document
网景提出另一种事件流名为
事件捕获(event capturing)
。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
因此在事件捕获的概念下在p元素上发生click事件的顺序应该是document -> html -> body -> div -> p
网景 和 微软 曾经的战争还是比较火热的,当时, 网景主张捕获方式,微软主张冒泡方式。后来 w3c 采用折中的方式,平息了战火,制定了统一的标准——先捕获再冒泡。
addEventListener的第三个参数就是为冒泡和捕获准备的.
addEventListener有三个参数:
element.addEventListener(event, function, useCapture)
第一个参数是需要绑定的事件
第二个参数是触发事件后要执行的函数
第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。
<div id="s1">s1
<div id="s2">s2div>
div>
<script>
s1.addEventListener("click",function(e){
console.log("s1 冒泡事件");
},false);
s2.addEventListener("click",function(e){
console.log("s2 冒泡事件");
},false);
script>
当我们点击s2的时候,执行结果如下: |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n9zXwvNA-1587994598286)(Pictures\20200331225233.png)] |
<div id="s1">s1
<div id="s2">s2div>
div>
<script>
s1.addEventListener("click",function(e){
console.log("s1 捕获事件");
},true);
s2.addEventListener("click",function(e){
console.log("s2 捕获事件");
},true);
script>
当我们点击s2的时候,执行结果如下: |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aOWtke01-1587994598288)(Pictures\20200331225333.png)] |
当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢。
这里记被点击的DOM节点为target节点
- document 往 target节点,捕获前进,遇到注册的捕获事件立即触发执行
- 到达target节点,触发事件(对于target节点上,是先捕获还是先冒泡则捕获事件和冒泡事件的注册顺序,先注册先执行)
- target节点 往 document 方向,冒泡前进,遇到注册的冒泡事件立即触发
总结下就是:
- 对于非target节点则先执行捕获在执行冒泡
- 对于target节点则是先执行先注册的事件,无论冒泡还是捕获
<div id="s1">s1
<div id="s2">s2div>
div>
<script>
s1.addEventListener("click",function(e){
console.log("s1 冒泡事件");
},false);
s2.addEventListener("click",function(e){
console.log("s2 冒泡事件");
},false);
s1.addEventListener("click",function(e){
console.log("s1 捕获事件");
},true);
s2.addEventListener("click",function(e){
console.log("s2 捕获事件");
},true);
script>
当我们点击s2的时候,执行结果如下: |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N3sESt2D-1587994598290)(Pictures\20200331225432.png)] |
这里大体分析下执行结果
点击s2,click事件从document->html->body->s1->s2(捕获前进)
这里在s1上发现了捕获注册事件,则输出**“s1 捕获事件”**
到达s2,已经到达目的节点,
s2上注册了冒泡和捕获事件,先注册的冒泡后注册的捕获,则先执行冒泡,输出**“s2 冒泡事件”**
再在s2上执行后注册的事件,即捕获事件,输出**“s2 捕获事件”**
下面进入冒泡阶段,按照s2->s1->body->html->documen(冒泡前进)
在s1上发现了冒泡事件,则输出**“s1 冒泡事件”**
在实际的开发当中,利用事件流的特性,我们可以使用一种叫做事件代理的方法。
<ul id="color-list">
<li>redli>
<li>yellowli>
<li>blueli>
<li>greenli>
<li>blackli>
<li>whiteli>
ul>
如果点击页面中的li元素,然后输出li当中的颜色,我们通常会这样写:
(function(){
var color_list = document.getElementById('color-list');
var colors = color_list.getElementsByTagName('li');
for(var i=0;i<colors.length;i++){
colors[i].addEventListener('click',showColor,false);
};
function showColor(e){
var x = e.target;
console.log("The color is " + x.innerHTML);
};
})();
利用事件流的特性,我们只绑定一个事件处理函数也可以完成:
(function(){
var color_list = document.getElementById('color-list');
color_list.addEventListener('click',showColor,false);
function showColor(e){
var x = e.target;
if(x.nodeName.toLowerCase() === 'li'){
console.log('The color is ' + x.innerHTML);
}
}
})();
使用事件代理的好处不仅在于将多个事件处理函数减为一个,而且对于不同的元素可以有不同的处理方法。假如上述列表元素当中添加了其他的元素(如:a、span等),我们不必再一次循环给每一个元素绑定事件,直接修改事件代理的事件处理函数即可。
对于事件代理来说,在事件捕获或者事件冒泡阶段处理并没有明显的优劣之分,但是由于事件冒泡的事件流模型被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型。
IE浏览器对addEventListener兼容性并不算太好,只有IE9以上可以使用。
要兼容旧版本的IE浏览器,可以使用IE的attachEvent函数
object.attachEvent(event, function)
两个参数与addEventListener相似,分别是事件和处理函数,默认是事件冒泡阶段调用处理函数,要注意的是,写事件名时候要加上"on"前缀(“onload”、"onclick"等)。
实现一个电商购物车功能案例,考察和熟悉对应
JS
这一章节技术点。·
该案例购物车主要功能如下:
- 商品单选、全选、反选功能
- 商品添加、删除功能
- 商品价格自动计算
购物车效果图 |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b5I3t4lZ-1587994598292)(Pictures\20200331163042.png)] |
html代码:
<body onselectstart="return false;">
<template id="temp">
<tr>
<td>
<input type="checkbox" class="check" checked>
td>
<td>
<img src="images/{src}">{txt}
td>
<td>{price}td>
<td>
<span class="reduce">-span><input class="text" value="1"><span class="add">+span>
td>
<td>{subtotal}td>
<td>
<span class="del">删除span>
td>
tr>
template>
<div class="box" id="box">
<table>
<thead>
<tr>
<th>
<label>
<input type="checkbox" class="checkAll check" checked>全选
label>
th>
<th>商品th>
<th>单价th>
<th>数量th>
<th>小计th>
<th>操作th>
tr>
thead>
<tbody id="tbody">
tbody>
table>
<div class="bottom" id="bottom">
<aside>
aside>
<label>
<input type="checkbox" class="checkAll check" checked>全选
label>
<span class="delAll">全部删除span>
<div>已选商品:
<span class="selected" id="num">3span>件
div>
<a href="#" class="show">显示或隐藏a>
<div>合计:¥
<span class="total" id="total">7000span>
div>
<div class="js">结算div>
div>
div>
CSS代码
body {
background-color: #FF4040;
}
div.box {
width: 700px;
margin: 50px auto 0;
}
div.box table {
border-collapse: collapse;
width: inherit;
text-align: center;
background-color: #f6f6f6;
}
div.box table td, div.box th {
border: 1px solid #999;
}
div.box th {
height: 40px;
}
div.box table tbody img {
height: 50px;
}
div.box table tbody tr span {
cursor: default;
}
div.box table tbody tr td:nth-child(2) img {
vertical-align: middle;
}
div.box table tbody tr td:nth-child(4) span {
display: inline-block;
width: 15px;
line-height: 30px;
background-color: #666;
color: #eee;
vertical-align: middle;
}
div.box table tbody tr td:nth-child(4) input {
width: 20px;
height: 20px;
outline: none;
vertical-align: middle;
text-align: center;
}
div.box table tbody tr td:nth-child(6) span {
padding: 4px 10px;
background-color: #999;
color: white;
}
div.box div.bottom {
padding: 15px 0;
margin-top: 15px;
height: 25px;
background-color: white;
display: flex;
justify-content: space-between;
position: relative;
}
div.box div.bottom span.delAll {
cursor: default;
}
div.box div.bottom div.js {
padding: 0 6px;
background-color: #00A5FF;
color: white;
margin-right: 10px;
cursor: default;
}
div.box div.bottom aside div {
display: inline-block;
}
div.box div.bottom aside div span {
position: absolute;
width: 50px;
line-height: 20px;
padding: 0 5px;
background-color: rgba(255, 255, 255, .4);
color: #333;
font-size: 10px;
margin-left: -60px;
margin-top: 20px;
transform: rotate(30deg);
cursor: pointer;
}
div.box div.bottom aside img {
height: 60px;
vertical-align: middle;
}
div.box div.bottom aside {
position: absolute;
background-color: #0a74cb;
width: 100%;
top: -70px;
padding: 5px;
box-sizing: border-box;
display: none;
}
div.box div.bottom aside.on {
display: block;
}
div.box div.bottom aside:after {
position: absolute;
content: "";
border: 10px solid transparent;
border-top-color: #0a74cb;
bottom: -19px;
right: 280px;
}
div.box div.bottom a, div.box div.bottom a:visited {
color: #0b97ff;
text-decoration: none;
}
JS代码
function $(exp){
//获取元素
var el;
if (/^#\w+$/.test(exp)){
el=document.querySelector(exp);
} else {
el=document.querySelectorAll(exp);
}
return el;
}
var arr = [];/*表单的数据*/
arr[arr.length] = {
src: '1.jpg', txt: 'Casio/卡西欧 EX-TR350', price: 5999.88};
arr[arr.length] = {
src: '2.jpg', txt: 'Canon/佳能 PowerShot SX50 HS', price: 3888.50};
arr[arr.length] = {
src: '3.jpg', txt: 'Sony/索尼 DSC-WX300', price: 1428.50};
arr[arr.length] = {
src: '4.jpg', txt: 'Fujifilm/富士 instax mini 25', price: 640.60};
var temp=$('#temp').innerHTML;
var tbody=$('#tbody');
arr.forEach(function (el) {
//把数据插入到HTML中
tbody.innerHTML += temp.replace("{src}", el.src).replace("{txt}", el.txt).replace("{price}", el.price)
.replace("{subtotal}", el.price);
});
var trs=$('#tbody tr');
var box=$('#box');
var aside=$('#bottom aside')[0];
box.onclick=function (ev) {
//利用事件冒泡的原理,把事件添加给父级box
var e=ev||event;
var target=e.target||e.srcElement;//获取当前点击对象
var cls=target.className;
if (cls.indexOf("check")!=-1)cls='check';
switch (cls) {
case 'add'://添加商品数量
var tr=target.parentNode.parentNode;//找到点击过那一行
var tds=tr.cells;
target.previousSibling.value++;//数量那一栏的数字加一
tds[4].innerText=(tds[2].innerText*target.previousElementSibling.value).toFixed(2);
//修改小计里面的价格
break;
case 'reduce'://减少商品数量
var tr=target.parentNode.parentNode;//找到点击过那一行
var tds=tr.cells;
if (target.nextElementSibling.value!=1) target.nextElementSibling.value--;
//数量那一栏减一
tds[4].innerText=(tds[2].innerText*target.nextElementSibling.value).toFixed(2);
//修改小计里面的价格
break;
case 'text'://直接修改数量那一栏input的值
var tr=target.parentNode.parentNode;
var tds=tr.cells;
target.onblur=function () {
//失去焦点时执行
tds[4].innerText=(tds[2].innerText*this.value).toFixed(2);
this.onblur=null;//销毁事件
};
break;
case 'del': //删除商品
var tr=target.parentNode.parentNode;
if (confirm('你确定要删除吗?'))
tbody.removeChild(tr);
break;
case 'check'://复选框选择商品
chk(target);//执行复选框函数
break;
case 'delAll'://删除全部商品
if (confirm('你确定要删除吗?'))
tbody.innerHTML='';
break;
case 'show'://显示、隐藏商品
aside.classList.toggle('on');
break;
case 'cancel':
var index=target.getAttribute('data-index');
trs[index].cells[0].children[0].checked=false;
}
total();//计算价格
};
var total_all=$('#total');
var num=$('#num');
total();
function total() {
//计算价格
var sum=0,number=0;
trs=$('tbody tr');
var str='';
trs.forEach(function (tr,i) {
//遍历每一行判断,将已选择商品添加到显示隐藏里面
var tds=tr.cells;
if (tds[0].children[0].checked){
sum+=parseFloat(tds[4].innerText);
number+=parseInt(tds[3].children[1].value);
str+=`${
i+1}.jpg">${
i}">取消选择`
}
total_all.innerText=sum.toFixed(2);
num.innerText=number;
aside.innerHTML=str;
})
}
var checkAll=$('#box .checkAll');
function chk(target) {
//复选框判断
var cls=target.className;
var flag = true;
if (cls==='check'){
//点击非全选复选框
/*当存在一个复选框未选中,全选框为false*/
for (var i = 0; i < trs.length; i++) {
var checkbox = trs[i].cells[0].children[0];
if (!checkbox.checked) {
flag = false;
break
}
}
checkAll[0].checked = checkAll[1].checked = flag;
}else {
//点击全选复选框,所有复选框的状态保持一致
for (var i = 0; i < trs.length; i++) {
var checkbox = trs[i].cells[0].children[0];
checkbox.checked = target.checked;
}
checkAll[0].checked = checkAll[1].checked = target.checked;
}
}