JavaScript

文章目录

一、JS 概述

1.1 JavaScript的诞生

JavaScript 诞生于 1995 年。由Netscape(网景公司)的程序员Brendan Eich(布兰登)与Sun公司联手开发一门脚本语言, 最初名字叫做Mocha,1995年9月改为LiveScript。12月,Netscape公司与Sun公司(Java语言的发明者)达成协议,后者允许将这种语言叫做JavaScript。这样一来,Netscape公司可以借助Java语言的声势。

1996年3月, Netscape公司的浏览器Navigator2.0浏览器正式内置了JavaScript脚本语言. 此后其他主流浏览器逐渐开始支持JavaScript.

1.2 js版本

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”。
1.3 js的优势

目前苹果公司的Safari, 谷歌的Chrome,微软的IE等几乎全部浏览器都支持JavaScript, 基于JavaScript开发的库和框架数不胜数, 例如: jQuery,Angular, React等…

JavaScript将在前端和服务器端(Node.js)有更好的发展

1.4 js用途

它的主要目的是,验证发往服务器端的数据、增加 Web互动、加强用户体验度等。可用于开发网站、游戏、移动端app等

1.5 js语言的组成

javascript = ECMAScript + BOM + DOM

  1. 核心(ECMAScript)
  2. 浏览器对象模型(BOM)
  3. 文档对象模型(DOM)
1.6 JS和H5的关系

h5全称html5,不是单纯的html的第5个版本,而是一项最新的web标准,是html、css、javascript等技术的集合

1.7 编辑工具
  • sublime Text
  • HBuilder
  • Dreamweaver
  • Notepad++

二、JS语法

2.1 js代码的编写位置
  • 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代码

2.2 JS变量的定义、关键字
  • 变量定义(使用var关键字):变量是存储数据的容器
    //var:关键字,
    //age:变量名
    var age;       
  • 赋值:
    age = 20;
  • 定义的同时赋值:
    var age=20;
  • 可以一次定义多个变量:
    var name="zhangsan", age=18, weight=108;
2.3 JS代码规范
  • JS变量的命名规范(规定)
  • 变量名必须是数字,字母,下划线_和美元符$组成;
  • 第一个字符不能为数字
  • 不能使用关键字或保留字
  • 代码可读性(约定)
  • 标识符区分大小写,如:age和Age是不同的变量。但强烈不建议用同一个单词的大小写区分两个变量。
  • 变量命名尽量遵守驼峰原则: myStudentScore
  • 变量命名尽量见名知意
  • 保持代码缩进
  • JS语句的末尾尽量写上分号;
  • 运算符两边都留一个空格, 如 var n = 1 + 2;
  • 注释
    • 单行注释://注释内容
    • 多行注释(和CSS注释一样)
      • /*注释内容*/
      • 多行注释不能嵌套
2.4 输出结果
  • alert():弹窗输出
  • console.log():控制台输出
  • 输出到页面元素
  • innerHTML:双标签输出
  • value:表单输出
  • document.write() 输出到body内
2.5 JS数据类型
  • 值类型
  • Number:数字
  • NaN:是一个特殊的值,即非数值(Not a Number)。数学运算无法得到数字时,就会返回NaN
    • 不代表任何值,也不等于任何值,甚至自己都不等于自己
    • 任何数据与它运算都返回NaN
  • String:字符串
    用引号(单/双引号)括起来的内容
  • Boolean: 布尔类型
  • true
  • false
  • 引用数据类型
  • Object:对象
  • Array:数组
  • 特殊数据类型
  • Null
    • null ,通过id获取不到元素时则得到 null
  • Undefined
    • undefined,声明变量但不赋值则得到 undefined
数据类型判断typeof
    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
  • 隐式转换
    如果运算不能进行下去,内部就会尝试进行数据类型的转换
    支持隐式转换的运算:逻辑运算、关系运算、算术运算

三、运算符

3.1 条件判断语句
3.1.1 if语句
  • 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)时,执行这里的代码
}
3.1.2 三元运算

格式:条件 ? 条件成立代码 : 条件不成立代码

var a=20;
var b = 50;
var sum = a>b ? a-b : a+b;
3.1.3 switch语句
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下面的语句
3.2 循环语句
  • 循环就是重复做一件事, 在JS中指的是循环执行某部分代码.
  • 循环结构是程序中一种很重要的结构,其特点是在给定条件成立时,反复执行某程序段,直到条件不成立为止

只要条件成立,就会不断地执行花括号里的语句
编写条件时,要避免出现死循环

3.2.1 while循环
//变量初始化
while(条件){
    //条件成立就会不断地执行这里的代码,直到条件不成立
    //所以这里一般会伴随着条件的更新
}
3.2.2 do…while
//变量初始化
do {
    //不管条件是否成立,先执行一次这里的代码,再进行条件判断,如果条件依然成立,则再次执行这里的代码,依此类推
    //所以这里一般会伴随着条件的更新
} while(条件)
3.2.3 for循环
for(变量初始化; 条件判断; 变量更新){
    //循环条件成立,则执行这里的代码
}
  • 两个分号必须写
3.3 循环跳转
  • break://退出当前整个循环
  • 只能在循环语句中使用
  • 循环体中位于break后的语句不会被执行
  • 在多层循环嵌套中,一个break语句只向外跳一层
  • continue://跳过本次循环,继续下一次循环
  • 只能在循环语句中使用,
  • 跳过本次循环(即跳过循环体中下面尚未执行的代码),接着执行下次循环。
  • label:给循环代码添加标识
    break和continue后如果带标识,则跳到标识所在循环
break与continue的区别(如图)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HfWZcN2N-1587994598278)(Pictures\break_continue.png “break与continue的区别”)]

PS:

  • 知道次数的循环用for循环,不知道次数用while循环
  • 死循环
  • 死循环就是重复执行代码, 不会停止.
  • 死循环会造成程序卡死甚至崩溃等问题, 所以我们写代码要避免死循环
3.3.1 嵌套循环
for(var i=0;i<10;i++){
    for(var j=0;j<10;j++){
        console.log(i,j);
    }
}

四、函数

4.1 了解函数

函数就是把特定功能的代码抽取出来并进行封装,用来重复执行一些功能,并起个名字(函数名)。函数对任何语言来说都是一个核心的概念。通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行

  • 使用函数的好处,为什么要使用函数
  • 函数可以重复执行某一部分代码(通过函数名调用)
  • 使程序变得更简短而清晰
  • 有利于程序维护
  • 什么时候需要函数
  • 当相同的代码出现多次时
  • 当需要提取公共代码时
4.2 函数的定义
  • 关键字声明(声明式):
    格式:function 函数名(){}
function sum(){}

函数的声明会提前 ==> 解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);

  • 函数表达式(赋值式)
var sum = function(){}
  • 匿名函数:
    没有名字的函数就叫匿名函数
function(){}
4.3 函数的执行
  1. 手动调用:
    sum();
  2. 事件驱动:
    格式:元素.事件 = 函数名;
    buton.onclick = sum;
4.4 声明提前
  • 函数声明提前
  • 变量声明提前
    声明但没有赋值的变量默认为undefined
function test(){
    console.log(a);
    var a=20;
}
test() ==> 会得到什么信息?
4.5 作用域

俗称“使用范围”,即能够使用某个变量的范围,分<全局作用域>和<局部作用域>

  • 全局变量与局部变量
    • 全局变量:在全局作用域下声明的变量,可以在任意地方中使用,作用范围比较大,我们称为全局变量
    • 局部变量:在函数内(局部作用域)声明的变量,只在函数中可以使用,作用范围较小,我们称之为局部变量
  • 变量的访问规则
    • 就近原则(如查找变量a):
      • 使用变量a时先从当前函数查找,如果当前函数有变量a则使用;
      • 如果当前函数无变量a,则往父级函数查找,如果找到则使用,并停止查找;
      • 如果在父级函数还是无法找到,则继续往上一层函数查找,以此类推,直到最顶层(全局作用域),如果还是没找到,则报not defined错误;
    • 作用域链:每个函数在定义时就形成了局部作用域,如果存在多个函数嵌套,他们之间就会建立起某种联系,直到全局作用域,这种联系称之为作用域链。当函数访问变量时,根据就近原则在这个作用域链中从内到外查询变量。
4.5.1 闭包

闭包是js开发惯用的技巧,什么是闭包?闭包指的是:能够访问另一个函数作用域的变量的函数。清晰的讲:闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量。eg:

function outer() {
     var  a = '变量1'
     var  inner = function () {
            console.info(a)
     }
    return inner    // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}
4.6 函数的参数
  • 形参,就是局部变量
  • 形参与实参的区别:
    • 形参:声明函数时圆括号内定义的变量
    • 实参:函数执行时传入的参数
      形参和实参的数量可以不同
  • arguments
    函数内部隐藏的对象(是一个类数组),保存着实参的信息
    • length: 实参的数量
    • 了解索引值
      • 读取值
  • 回调函数:函数作为参数传递 eg:
//注意到click方法中是一个函数而不是一个变量
//它就是回调函数
$("#btn_1").click(function() {
    alert("Btn 1 Clicked");
});  
//或者
function click() { // 它就是回调函数
    alert("Btn 1 Clicked");
}
$("#btn_1").click(click);  
4.7 函数返回值
  1. 终止函数的执行,return后的代码不会执行
  2. return后如果有值,则把这个值返回到函数执行的地方
    如果函数没有return,执行完后返回undefined
4.8 函数中的this

函数中的this是一个关键字,表示当前对象,而当前对象是谁,取决于谁调用了这个函数

五、系统函数

5.1 parseInt(numString,[radix])
定义和用法

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
5.2 parseFloat(string)
定义和用法

parseFloat() 函数可解析一个字符串,并返回一个浮点数。

该函数指定字符串中的首个字符是否是数字。如果是,则对字符串进行解析,直到到达数字的末端为止,然后以数字返回该数字,而不是作为字符串。

语法
parseFloat(string)
参数描述
string必需。要被解析的字符串。
详细说明

parseFloat 是全局函数,不属于任何对象。

parseFloat 将它的字符串参数解析成为浮点数并返回。如果在解析过程中遇到了正负号(+ 或 -)、数字 (0-9)、小数点,或者科学记数法中的指数(e 或 E)以外的字符,则它会忽略该字符以及之后的所有字符,返回当前已经解析到的浮点数。同时参数字符串首位的空白符会被忽略。

如果参数字符串的第一个字符不能被解析成为数字,则 parseFloat 返回 NaN。

**提示:**您可以通过调用 isNaN 函数来判断 parseFloat 的返回结果是否是 NaN。如果让 NaN 作为了任意数学运算的操作数,则运算结果必定也是 NaN。

返回值

返回解析后的数字。

提示和注释

**注释:**开头和结尾的空格是允许的。
**提示:**如果字符串的第一个字符不能被转换为数字,那么 parseFloat() 会返回 NaN。
**提示:**如果只想解析数字的整数部分,请使用 parseInt() 方法。

实例
例子 1

在本例中,我们将使用 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
5.3 isNaN(x)
定义和用法

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
5.4 encodeURI(URIstring)

把字符串作为URI进行编码并返回(URL就是一种常见的URI),URIstring是一个含有URI或者其他要编码的文本的字符串,目的是对URI完整编码,不转义字符。

5.5 decodeURI(URIstring)

对encodeURI()函数编码过的URI进行解码,URIstring是一个含有URI或其他要解码的文本的字符串。

六、事件

6.1 什么是事件

事件是可以被JavaScript侦测到的行为。网页中的每个元素都可以产生某些可以触发JavaScript函数的事件

6.1.1 事件绑定方式

格式:节点.on+事件名 = 事件处理函数;

div.onclick = function(){}
6.1.2 事件分类
  • 鼠标事件
  • onclick 当用户点击某个对象时调用的事件。
  • ondblclick 当用户双击某个对象时调用的事件。
  • onmousedown 鼠标按钮被按下。
  • onmouseup 鼠标按键被松开。
  • onmouseover 鼠标移到某元素之上。
  • onmouseout 鼠标从某元素移开。
  • onmousemove 鼠标被移动时触发。
  • onmouseenter 在鼠标光标从元素外部移动到元素范围之内时触发。这个事件不冒泡
  • onmouseleave 在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,
  • oncontextmenu 鼠标右键菜单展开时触发。

PS:click = mousedown + mouseup, dblclick = click*2(短时间内两次单击);
执行顺序:mouseover=>mouseenter; mouseout => mouseleave

  • 键盘事件
  • onkeydown 某个键盘按键被按下。
  • onkeyup 某个键盘按键被松开。
  • onkeypress 键盘<字符键>被按下,而且如果按住不放的话,会重复触发此事件。
  • UI事件
  • onload 页面元素(包括图片多媒体等)加载完成后
  • onscroll 滚动时触发。
  • onresize 窗口或框架被重新调整大小。
  • 表单事件
  • onblur 元素失去焦点时触发。
  • onfocus 元素获得焦点时触发。
  • onchange 元素内容被改变,且失去焦点时触发。
  • oninput 输入字符时触发
  • onreset 重置按钮被点击。
  • onsubmit 确认按钮被点击。
  • onselect 输入框文本被选中。
  • 其他事件

七、字符串

7.1 了解字符串

字符串就是一串字符,由双(单)引号括起来。

创建一个字符串

//方式一:字面量(推荐)
var str = '城市套路深,我想回农村';

//方式二:构造函数
//PS:用new产生的变量都是引用类型的变量,也叫对象
var str = new String('我不是黄蓉,我不会武功');
7.2 字符串的属性和方法
7.2.1 属性
  • length: 表示字符串的长度,只读(只能读取)
7.2.2 字符串的获取方法
  • charAt(3) //获取下标为3的字符
7.2.3 字符串的查找替换方法
  • indexOf/lastIndexOf(keyword [,startIndex]) 从开头/尾部向后查找字符串keyword第一次出现的位置,如果没找到返回-1
  • search(str|regExp) 查找字符串第一次出现的位置

与indexOf的区别:search方法支持正则表达式

  • match(str|regExp) 匹配字符串,返回一个数组
    index:匹配字符所在的索引
    input:表示整个字符串的引用
  • replace(str|regExp,’’) 替换字符串
    这里的替换只能执行一次,不能够进行全局匹配,如果需要全局匹配,则应使用正则表达式
7.2.4 字符串的截取方法
  • substring(start[,end]):不包括end所在字符,end省略表示截取到最后
  • substr(start[,len]):支持负数,len为截取的数量
  • slice(start[,end]) :截取start到end(不包括end)的字符串,支持负数
7.2.5 字符串分割
  • split(分割符):根据分割字符,把字符串拆分成数组
7.2.6 字符串大小写转换
  • toLowerCase():转换成小写
  • toUpperCase():转换成大写
7.2.7 ECMAscript5新增
  • str[3]//通过下标获取
  • trim():删除前后所有空格,返回新的字符串
7.2.8 编码与字符集(了解)
  • charCodeAt(3) //获取下标为3的字符的ASCII(American Standard Code for * Information Interchange) == > unicode编码
  • String.fromCharCode(94) //编码转换成字符

[ascii码, GBK及Unicode由来]
字符编码是计算机技术的基石,想要熟练使用计算机,就必须懂得一点字符编码的知识。

八、正则

8.1 了解正则表达式
  • 什么是正则表达式
    正则表达式(regular expression)是一个描述字符模式的对象。
  • 为什么要使用正则表达式
    正则表达式能够进行强大的“模式匹配”和“文本检索与替换”功能。前端往往有大量的表单数据校验的工作,采用正则表达式会使得数据校验的工作量大大减轻
8.2 创建正则表达式
8.2.1 RegExp构造函数,
  • 第一个参数就是我们的模式“字符串”
var reg= new RegExp('study');

//使用特殊字符
var reg= new RegExp('\\d\\w+');
  • 第二个参数可选,模式修饰符
  • i: case-insensitive,表示忽略大小写
  • g: global,表示全局匹配
  • m: multiline,表示多行匹配,影响^,$的匹配结果
var reg = new RegExp('study', 'ig');
8.2.2 字面量方式直接声明
var reg = /study/gi;
  • 直接量是字符匹配,不支持变量
8.3 使用正则表达式
8.3.1 支持正则表达式的字符串方法
  • search
    返回第一次匹配时所在的索引值,如果匹配不到则返回-1
  • match
  • 默认匹配字符串,返回一个数组
    • 0:所匹配的字符
    • index:匹配第一个字符所在的索引
    • input:对字符串的引用
  • 全局匹配(g),返回一个匹配所有字符串数组
  • 如果匹配不到则返回null
  • replace
    替换字符串
  • split
    'a,b ,c , d, e'.split(/\s*,\s* /);
8.3.2 正则表达式的属性和方法
  • 测试正则表达式用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
8.3.3 匹配规则
  • 原字匹配:所有字母和数字都是按照字面量进行匹配,和字符串匹配等效
    /good/gi
  • 字符类(只记小写字母即可)
  • . : 除换行以外的字符
  • \w : 代表数字或字母或下划线
  • \W : 非数字字母和下划线字符
  • \d : 数字
  • \D : 非数字
  • \s : 代表一个空格
  • \S : 空格以外的字符

PS:以上所有字符类都只是匹配“一个”字符

  • 边界处理
  • \b : 匹配一个单词边界,也就是指单词和空格间的位置
  • \B : 匹配非单词边界。
  • 特殊符号
    ^ $ . * + ? = ! : | \ / () [] {}
  • []: 代表任意“单个字符” ,里面的内容表示“或”的关系
    - -: 代表范围
    - ^: 代表非
  • (): 表示分组(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

9.1 BOM的概念

BOM 是Browser Object Model(浏览器对象模型)的缩写,提供与浏览器窗口进行交互的对象。JavaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C, 而BOM缺乏标准。这也是各种浏览器不兼容的根源所在

9.2 window对象

window对象是BOM的核心, 是最顶层的对象,所有对象都是通过它延伸出来的,如图:

window 对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXzX6PY6-1587994598280)(Pictures\window.png “window对象”)]
9.2.1 全局作用域
  • 定义在全局环境下的变量都会成为window对象的属性
  • 把变量定义在函数体里,可以有效减少全局环境下的变量冲突,避免污染全局环境
  • window对象可以在代码中省略,如window.alert()可以写成alert();
  • 在函数内部不用var声明的变量会成为全局变量,即window对象的属性
  • 通过var在全局作用域下声明的变量用delete无法删除
var obj = {name:'xxx'}

//删除对象的属性用delete:
delete obj.name;
9.2.2 window对象下的属性

常用属性

  • 浏览器窗口尺寸
    innerWidth/innerHeight, //表示浏览器窗口"可视区域"尺寸
    outerWidth/outerHeight //表示整个浏览器窗口的尺寸
  • 滚动相关
  • scrollX/scrollY //获取浏览器窗口滚动条滚动过的距离
  • scrollTo(x,y) //指定滚动位置
  • scrollBy(xnum,ynum) //设置基于当前位置滚动的距离,可以为负数

系统对话框(了解)

  • alert(msg)//弹出对话框
  • confirm(msg)//弹出警告框,返回布尔值
  • prompt(msg,default)//弹出输入框,返回消息或null

以上三个方法都会暂停代码的执行

  • open(url,name,[options]) : 打开一个新窗口并返回新 window 对象
  • name:不命名会每次打开新窗口,命名的第一次打开新窗口,之后在这个窗口中加载
  • options为字符串:'width=400,height=400,top=200,left=200'
  • opener父窗口对象,通过open方法打开的窗口才有opener对象
  • close(): 关闭窗口
  • print(): 调出打印对话框

属性对象

  • document(核心): 文档对象,让我们可以在js脚本中直接访问页面元素(DOM)

在DOM章节详细讲解

  • history(重要): 历史对象,包含窗口的浏览历史,可以实现后退
  • 属性:
    • length 返回浏览器历史列表中的 URL 数量。
  • 方法:
    • back() 加载 history 列表中的前一个 URL。
    • forward() 加载 history 列表中的下一个 URL。
    • go() 加载 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(了解):
    导航对象, 包含所有有关访问者浏览器的信息,通常用于检测浏览器类型
  • appName 浏览器名称
  • appVersion 浏览器版本
  • platform 操作系统
  • userAgent 用户代理信息,通过该属性可以获取浏览器及操作系统信息
  • geolocation 获取地理位置信息
9.2.3 window对象常用事件
  • onload //页面资源全部加载完成后触发这个事件
  • onscroll//窗口滚动条滚动时触发
  • onresize //窗口大小改变时触发

十、Date

10.1 了解时间
  • 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零时
10.2 创建日期时间
//1)创建当前时间的日期和时间
var d = new Date();//得到的是代码执行时的时间(本地时间)

//2)创建指定日期的时间和日期
var d = new Date("2015/08/22");
var d = new Date(56521211021);//参数为距1970-1-1零时的毫秒数
10.3 获取/设置时间
  • 获取年月日
  • getFullYear()/setFullYear(2014)
  • getMonth()/setMonth(8)注意:获取月份是从0开始的
  • getDate()/setDate(25)
  • 获取星期
  • getDay() 0-6:星期天-星期六
  • 获取时分秒
  • getHours()/setHours()
  • getMinutes()/setMinutes()
  • getSeconds()/setSeconds()
10.4 日期处理
  • 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零时的毫秒数
10.5 延迟与定时器
  • 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

11.1 什么是DOM

DOM是Document Object Model(文档对象模型)的缩写,它是W3C国际组织的一套Web标准。是针对HTML和XML文档的一个API,它定义了访问HTML文档对象的一套属性、方法和事件。

DOM结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qslE2WOp-1587994598282)(Pictures\dom_tree.png “Dom结构”)]
11.2 节点类型
  • 每个节点都有一个nodeType属性,用于表明节点的类型。
  • 常用节点类型与对应nodeType值:
  • 用于判断获取到的元素属于什么类型节点
    - 元素节点 <> 1
    - 文本节点 <
    > 3
    - 属性节点 <==> 2
<!--找出所有节点-->
<div class="content" title="属性节点">测试Div</div>
11.3 节点获取
11.3.1 获取元素节点方法
<div class="content" id="testId" name="testName" title="属性节点">测试Div</div>
  • document.getElementById(id)
  • 通过 ID获取元素的节点(速度最快)
  • 必须通过document调用
  • 返回元素节点对象,如果id不存在返回null
  • getElementsByTagName(tagname)
  • 通过标签名获取元素节点列表
  • 返回类数组,如果tagname不存在返回空数组[]
  • getElementsByClassName()
  • 通过class类名获取节点列表
  • 返回类数组,如果类名不存在返回空数组[]
  • document.getElementsByName()
  • 通过name属性获取元素节点列表
  • 必须通过document调用
  • 返回类数组,如果name属性不存在返回空数组[]

注意: 如果确认元素存在, 但是返回null或[],一定是代码执行顺序的问题

11.4 节点操作
11.4.1 节点属性
  • nodeName  返回节点的名称,根据其类型。
  • nodeType  返回节点的类型。
  • nodeValue 返回节点的值(元素节点的nodeValue为null)
11.4.2 节点方法
  • 创建:
  • document.createElement() 创建一个元素节点
  • document.createTextNode() 创建一个文本节点
  • document.createAttribute() 创建一个属性节点(几乎不用)
  • 插入:
  • parent.appendChild()  向节点的子节点列表的结尾添加新的子节点
  • parent.insertBefore(new,node)  在指定的子节点node前插入新的子节点new。
  • ele.setAttributeNode(attrNode) 在指定元素中插入一个属性节点(几乎不用)

对页面已存在节点的处理

  • 复制
  • cloneNode(boolean)  复制节点,为true是深复制。
  • 删除:
  • parent.removeChild(ele)  删除(并返回)当前节点parent的指定子节点ele。
  • 判断:
  • parent.hasChildNodes() 判断当前节点是否拥有子节点,返回布尔值

以上parent表示父级元素,ele表示元素

11.4.3 利用节点关系获取其他节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-53Wh8mEM-1587994598284)(Pictures\节点关系.png “节点关系”)]

  • 获取父级节点
  • ele.parentNode 得到ele元素的父节点
  • 获取子节点
  • ele.childNodes 得到ele元素的全部子节点列表(类数组)
  • ele.firstChild 获得ele元素的第一个子节点
  • ele.lastChild 获得ele元素的最后一个子节点
  • 获取兄弟节点
  • ele.nextSibling 获得ele元素的下一个兄弟节点
  • ele.previousSibling 得到ele元素的上一个兄弟节点

11.5 元素节点的操作
11.5.1 常用节点属性

可以通过点语法或方括号访问

  • id 设置/获取元素id属性
  • name 设置/获取元素name属性
  • style 设置/获取元素的内联样式
  • className 设置/获取元素的class属性
  • title
  • value
  • checked
  • type
  • src
  • tagName 获取元素元素的标签名
  • innerHTML 设置/获取元素的内容(包含html代码)
  • innerText 设置或获取位于元素标签内的文本
  • outerHTML 设置或获取元素及其内容(包含html代码)
  • outerText 设置(包括标签)或获取(不包括标签)元素的文本
11.5.2 盒模型相关
  • offsetTop: 当前元素离<定位父级>元素顶部的距离。
  • offsetLeft: 当前元素离<定位父级>元素左边的距离。

以上两个属性如果没定位的父级,则相对于根元素html的距离

  • offsetWidth: 当前元素的宽度(border + padding + content)
  • offsetHeight: 当前元素的高度(border + padding + content)
11.5.3 操作html属性
  • ele.getAttribute(attr) //获取元素的属性值(自定义属性获取)
  • ele.setAttribute(attr,val); //设置元素的属性
  • ele.removeAttribute(attr) //删除属性attr
  • ele.hasAttribute(attr) //判断是否存在属性attr
11.5.4 根据元素关系获取其他元素
  • parentElement 获取父级节点元素
  • children 获取元素的全部子元素
  • firstElementChild 获取第一个子元素
  • lastElementChild 获取最后一个子元素
  • previousElementSibling 获取前一个元素
  • nextElementSibling 获取下一个元素
11.5.5 获取css样式(非内联样式)

得到当前元素计算后的所有样式

  • getComputedStyle(ele,pseudo) (标准)
  • ele:要获取样式的元素
  • pseudo:伪元素样式字符(可选),可获取伪元素样式
  • ele.currentStyle (IE8-)
11.6 table对象(了解)
11.6.1 table对象属性&方法
  • rows 返回包含表格中所有行的一个数组
  • tBodies 返回包含表格中所有 tbody 的一个数组
  • insertRow(index) 在表格中插入一个新行。
  • deleteRow(index) 从表格删除一行。
11.6.2 tr对象属性&方法
  • cells 返回包含表格中所有单元格的一个数组
  • rowIndex 返回该行在表中的位置
  • sectionRowIndex 返回在 tBody 、tHead 或 tFoot 中行的位置。
  • insertCell(index) 在一行中的指定位置插入一个空的列
  • deleteCell(index) 删除行中的指定的单元格
11.6.3 td/th对象属性&方法
  • cellIndex 返回单元格在表格行的单元格集合中的位置。

十二、事件冒泡与事件捕获

事件冒泡事件捕获分别由微软网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。
考虑下面这段代码,就不写html->head,body之类的代码了,自行脑补

<div id="outer">
    <p id="inner">Click me!</p>                                     
</div>

上面的代码当中一个div元素当中有一个p子元素,如果两个元素都有一个click的处理函数,那么我们怎么才能知道哪一个函数会首先被触发呢?
为了解决这个问题微软和网景提出了两种几乎完全相反的概念。

12.1 事件冒泡

微软提出了名为事件冒泡(event bubbling)的事件流。事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。

因此在事件冒泡的概念下在p元素上发生click事件的顺序应该是p -> div -> body -> html -> document

12.2 事件捕获

网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

因此在事件捕获的概念下在p元素上发生click事件的顺序应该是document -> html -> body -> div -> p

12.3 addEventListener的第三个参数

网景 和 微软 曾经的战争还是比较火热的,当时, 网景主张捕获方式,微软主张冒泡方式。后来 w3c 采用折中的方式,平息了战火,制定了统一的标准——先捕获再冒泡
addEventListener的第三个参数就是为冒泡和捕获准备的.
addEventListener有三个参数:

element.addEventListener(event, function, useCapture)

第一个参数是需要绑定的事件
第二个参数是触发事件后要执行的函数
第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。

12.3.1 冒泡的案例
<div id="s1">s1
    <div id="s2">s2</div>
</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)]
12.3.2 捕获的案例
<div id="s1">s1
    <div id="s2">s2</div>
</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)]
12.4 事件捕获vs事件冒泡

当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢。
这里记被点击的DOM节点为target节点

  1. document 往 target节点,捕获前进,遇到注册的捕获事件立即触发执行
  2. 到达target节点,触发事件(对于target节点上,是先捕获还是先冒泡则捕获事件和冒泡事件的注册顺序,先注册先执行)
  3. target节点 往 document 方向,冒泡前进,遇到注册的冒泡事件立即触发

总结下就是:

  • 对于非target节点则先执行捕获在执行冒泡
  • 对于target节点则是先执行先注册的事件,无论冒泡还是捕获
<div id="s1">s1
    <div id="s2">s2</div>
</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 冒泡事件”**

12.5 事件冒泡与事件捕获应用:事件代理

在实际的开发当中,利用事件流的特性,我们可以使用一种叫做事件代理的方法。

<ul id="color-list">
    <li>red</li>
    <li>yellow</li>
    <li>blue</li>
    <li>green</li>
    <li>black</li>
    <li>white</li>
</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等),我们不必再一次循环给每一个元素绑定事件,直接修改事件代理的事件处理函数即可。

12.6 冒泡还是捕获?

对于事件代理来说,在事件捕获或者事件冒泡阶段处理并没有明显的优劣之分,但是由于事件冒泡的事件流模型被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型。

12.6.1 IE浏览器兼容

IE浏览器对addEventListener兼容性并不算太好,只有IE9以上可以使用。
要兼容旧版本的IE浏览器,可以使用IE的attachEvent函数

object.attachEvent(event, function)

两个参数与addEventListener相似,分别是事件和处理函数,默认是事件冒泡阶段调用处理函数,要注意的是,写事件名时候要加上"on"前缀(“onload”、"onclick"等)。

十三、购物车案例

实现一个电商购物车功能案例,考察和熟悉对应JS这一章节技术点。·
该案例购物车主要功能如下:

  • 商品单选、全选、反选功能
  • 商品添加、删除功能
  • 商品价格自动计算
13.1 效果图
购物车效果图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b5I3t4lZ-1587994598292)(Pictures\20200331163042.png)]
13.2 源码

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">3</span></div>
        <a href="#" class="show">显示或隐藏</a>
        <div>合计:¥
            <span class="total" id="total">7000</span>
        </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+=`<div><img src="images/${i+1}.jpg"><span class="cancel" data-index="${i}">取消选择</span></div>`
            }
            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;
        }
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值