使用表单、prompt 获取过来的数据默认是字符串类型的,此时就不能直接简单的进行加法运算,而需要转换变量的数据类型。通俗来说,就是把一种数据类型的变量转换成另外一种数据类型。
浮点数值的最高精度是17位小数,但在进行算数计算时其精确度远远不如整数
var result = 0.1 +0.2; //结果不是0.3,0.30000000000000004
console.log(0.07 * 100); //结果不是7,而是7.000000000000001
所以不要直接判断两个浮点数是否相等
console.log(18 == '18'); //true
console.log(18 === '18'); //false
浏览器中: 检查–> sources -->找到需要调试的文件–>在程序的某一行设置断点(在行数点一下)
刷新浏览器
Watch: 监视,通过watch可以监视变量的值的变化,非常的常用
continue 关键字用于立即跳出本次循环,继续下一次循环(本次循环体中 continue 之后的代码就会少执行一次)
break 关键字用于立即跳出整个循环
for (var i = 1; i <= 5; i++) {
if (i == 3) {
console.log('这个包子有虫子,扔掉');
continue; // 跳出本次循环,跳出的是第3次循环
// break; 直接退出整个for 循环,跳到整个for下面的语句
}
console.log('我正在吃第' + i + '个包子呢');
}
return 只能返回一个值。如果用逗号隔开多个值,以最后一个为准
当我们不确定有多少个参数传递的时候,可以用 arguments 来获取。
在 JavaScript 中,arguments 实际上它是当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有实参。
// 函数声明
function fn() {
console.log(arguments); //里面存储了所有传递过来的实参
console.log(arrguments.length); // 3
console.log(arrguments[2]); // 3
}
// 函数调用
fn(1,2,3);
命名函数: 因为有名字,所以称为命名函数。调用函数的代码既可以放到声明函数的前面,也可以放在声明函数的后面
// 声明定义方式
function fn() {...}
// 调用
fn();
匿名函数: 因为函数没有名字,所以称为匿名函数
这个fn 里面存储的是一个函数(只是存储,而不是函数名)
函数调用的代码必须写到函数体后面
// 这是函数表达式写法,匿名函数后面跟分号结束
var fn = function(){...};
// 调用的方式,函数调用必须写到函数体下面
fn();
JavaScript (ES6前) 中的作用域有两种:
JS 没有块级作用域: 块作用域由 {} 包括在其他编程语言中(如 java、c#等),在 if 语句、循环语句中创建的变量,仅仅只能在本 if 语句、本循环语句中使用
if(true){
var num = 123;
console.log(num) // 123
}
console.log(num); // 123
JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析和代码执行。
预解析只会发生在通过 var 定义的变量和 function 上。学习预解析能够让我们知道为什么在变量声明之前访问变量的值是 undefined,为什么在函数声明之前就可以调用函数。
变量提升: 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升
console.log(num); // 结果是多少?
var num = 10;
// undefined
//相当于执行了以下代码
var num; // 变量声明提升到当前作用域最上面
console.log(num);
num = 10; // 变量的赋值不会提升
函数提升: 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。
fn(); //11
function fn() {
console.log('11');
}
// 匿名函数(函数表达式方式):若我们把函数调用放在函数声明上面
fn();
var fn = function() {
console.log('22'); // 报错
}
//相当于执行了以下代码
var fn;
fn(); //fn没赋值,没这个,报错
var fn = function() {
console.log('22'); //报错
}
预解析练习:
// 练习2
var num = 10;
function fn(){
console.log(num); //undefined
var num = 20;
console.log(num); //20
}
fn();
// 最终结果是 undefined 20
//上述代码相当于执行了以下操作
var num;
function fn(){
var num;
console.log(num);
num = 20;
console.log(num);
}
num = 10;
fn();
// 练习4
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
var a = b = c = 9;
// 相当于 var a = 9; b = 9;c = 9; b和c的前面没有var声明,当全局变量看
// 集体声明 var a = 9,b = 9,c = 9;
console.log(a);
console.log(b);
console.log(c);
}
//上述代码相当于执行了以下操作
function f1() {
var a;
a = b = c = 9;
console.log(a); //9
console.log(b); //9
console.log(c); //9
}
f1();
console.log(c); //9
console.log(b); //9
console.log(a); //报错 a是局部变量
(1)创建对象
在 JavaScript 中,现阶段我们可以采用三种方式创建对象(object):
字面量
创建对象:就是花括号 { } 里面包含了表达这个具体事物(对象)的属性和方法。 { } 里面采取键值对的形式表示new Object
创建对象: var 对象名 = new Object();构造函数
创建对象: 是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。// 1. 利用`字面量`创建对象 ----------------------------
var star = {
name : 'pink',
age : 18,
sex : '男',
sayHi : function(){ // 方法冒号后面跟的是一个匿名函数
alert('大家好啊~');
}
}; // 多个属性或者方法中间用逗号隔开
// 2. 利用 `new Object`创建对象 ----------------------------
var obj = new Object(); //创建了一个空的对象
obj.name = '张三丰';
obj.age = 18;
obj.sex = '男';
obj.sayHi = function() {
console.log('hi~');
}
// 3. 利用`构造函数`创建对象 ----------------------------
//构造函数的语法格式
function 构造函数名() {
this.属性 = 值;
this.方法 = function() {}
}
new 构造函数名();
// 实例: 我们只要new Star() 调用函数就创建了一个对象
function Star(uname,age,sex) {
this.name = uname;
this.age = age;
this.sex = sex;
this.sing = function(sang){
console.log(sang);
}
}
var ldh = new Star('刘德华',18,'男');
console.log(typeof ldh) // object对象,调用函数返回的是对象
console.log(ldh.name);
console.log(ldh['sex']);
ldh.sing('冰雨');//把冰雨传给了sang
var zxy = new Star('张学友',19,'男');
构造函数和对象
(2)对象的调用
属性调用 : 对象.属性名 ,对象['属性名']
,注意方括号里面的属性必须加引号
方法调用:对象.方法名()
,注意这个方法名字后面一定加括号
(3)遍历对象
for…in 语句用于对数组或者对象的属性进行循环操作
var obj = {
name: '秦sir',
age: 18,
sex: '男',
fn:function() {}
};
console.log(obj.name);
console.log(obj.age);
console.log(obj.sex);
//for in 遍历我们的对象
//for (变量 in 对象){}
//我们使用for in 里面的变量 我们喜欢写k 或者key
for(var k in obj){
console.log(k); // k 变量 输出得到的是属性名
console.log(obj[k]); // obj[k] 得到的是属性值
}
JavaScript 中的对象分为3种:自定义对象 、内置对象、 浏览器对象
内置对象就是指 JS 语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能。JavaScript 提供了多个内置对象:Math、 Date 、Array、String等
Math 对象不是构造函数,它具有数学常数和函数的属性和方法。
// Math数学对象,不是一个构造函数,所以我们不需要new 来调用,
//而是直接使用里面的属性和方法即可
Math.PI // 圆周率
Math.floor() // 向下取整
Math.ceil() // 向上取整
Math.round() // 四舍五入版 就近取整 注意 -3.5 结果是 -3
Math.abs() // 绝对值
Math.max()/Math.min() // 求最大和最小值
console.log(Math.PI);
console.log(Math.max(1,99,3)); // 99
随机数方法random()
random() 方法可以随机返回一个小数,其取值范围是 [0,1),左闭右开 0 ≤ x < 1
// 练习
得到两个数之间的随机整数,并且包含这两个整数
function getRandom(min,max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(getRandom(1,10));
随机点名
var arr = ['张三', '李四','王五','秦六'];
console.log(arr[getRandom(0,arr.length - 1)]);
Date 对象和 Math 对象不一样,他是一个构造函数,所以我们需要实例化后才能使用
Date 实例用来处理日期和时间
获取当前时间必须实例化:
var now = new Date();
console.log(now);
1.检测是否为数组
var arr = [1, 23];
var obj = {};
console.log(arr instanceof Array); // true
console.log(obj instanceof Array); // false
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
添加删除数组元素:
数组排序:
数组索引:
数组转化为字符串:
// 2.join('分隔符')
var arr1 = ['green', 'blue', 'red'];
console.log(arr1.join()); // 不写默认用逗号分割
console.log(arr1.join('-')); // green-blue-red
console.log(arr1.join('&')); // green&blue&red
1、基本包装类型
基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。
var str = 'andy';
console.log(str.length);
按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为 js 会把基本数据类型包装为复杂数据类型,其执行过程如下 :
// 1.生成临时变量,把简单类型包装为复杂数据类型
var temp = new String('andy');
// 2.赋值给我们声明的字符变量
str = temp;
// 3.销毁临时变量
temp = null;
2,字符串的不可变
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
var str = 'abc';
str = 'hello';
// 当重新给 str 赋值的时候,常量'abc'不会被修改,依然在内存中
// 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
// 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
var str = '';
for(var i = 0; i < 10000; i++){
str += i;
}
console.log(str);
// 这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间
3,字符串操作方法
replace() :一些字符替换另一些字符 replace(被替换的字符,要替换为的字符串)
<script>
// 1. 替换字符 replace('被替换的字符', '替换为的字符') 它只会替换第一个字符
var str = 'andyandy';
console.log(str.replace('a', 'b'));
// 有一个字符串 'abcoefoxyozzopp' 要求把里面所有的 o 替换为 *
var str1 = 'abcoefoxyozzopp';
while (str1.indexOf('o') !== -1) {
str1 = str1.replace('o', '*');
}
console.log(str1);
</script>
split() :切分字符串,它可以将字符串切分为数组。在切分完毕之后,返回的是一个新数组。
var str = 'a,b,c,d';
console.log(str.split(','));
// 返回的是一个数组 ['a', 'b', 'c', 'd']
toUpperCase() 转换大写
toLowerCase() 转换小写
(1)堆和栈
堆栈空间分配区别:
值类型变量(简单类型)的数据直接存放在变量(栈空间)中
引用类型(复杂数据类型):通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等。引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中
简单数据类型 是存放在栈里面 里面直接开辟一个空间存放的是值
复杂数据类型 首先在栈里面存放地址 十六进制表示 然后这个地址指向堆里面的数据
传参数时情况: | |
---|---|
简单类型传参 | 函数的形参可以看做一个变量,当把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量 |
复杂类型传参 | 函数的形参可以看做一个变量,当把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象 |
1, API(Application Programming Interface,应用程序编程接囗)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而无需访问源码,或埋解内部工作机制的细节。
简单理解:API是给程席员提供的一种工具,以便能更轻松现想要完成的功能。eg: 手机充电,我们不关心手机内部充电细节,我们只用知道充电器插入就可以充电就好
2, Web API 是浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)eg:想要浏览器弹出一个警示框,我们只需要写alert()就可以
3, DOM:Document Object Model 文档对象模型,是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。
(1) DOM树:在内存中,集中保存一个网页中所有内容的树状图。
(2) 为什么使用DOM树:能够直观的保存上下级包含关系的数据结构,html标签就是包含的关系,所以网页中每一项内容,在内存中,都是存在树形结构的。
(3) 如何形成的:① 浏览器读取html文件时,会先在内存中创建一个document对象,作为树根对象
②然后解析html文件中每个元素,文本等内容,每解析一项内容,就在document下 对应的位置创建一个节点(node)对象
(4) DOM作用:document代表整个文档,html是整个文档的根标签,DOM操作元素可以改变网页内容结构和样式,可以利用DOM操作元素来改变元素里面的内容、属性等;
(5) DOM概念:①文档:一个页面就是一个文档,DOM 中使用 document 表示
②元素:页面中的所有标签都是元素,DOM 中使用 element 表示
③节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM 中使 用node 表示
注意:DOM 把以上内容都看做是对象。
doucument.getElementByld('id名')
console.dir() //可以直接页面上显示打印我们获取的元素对象,更好的查看对象里面的属性和方法
// 返回的是一个元素对象
var timer = document.getElementById('time');
console.log(timer);
// console.dir 打印我们的元素对象,更好的查看里面的属性和方法
console.dir(timer);
根据标签名获取:doucument.getElementsByTagName('标签名');
因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历。
得到元素对象是动态的,返回的是获取过来元素对象的集合,以伪数组的形式存储。
如果获取不到元素,则返回为空的伪数组(因为获取不到对象)
根据标签名获取
还可以根据标签名获取某个元素(父元素)内部所有指定标签名的子元素,获取的时候不包括父元素自己
element.getElementsByTagName('标签名')
ol.getElementsByTagName('li');
注意:父元素必须是单个对象(必须指明明确到是哪一个元素对象),获取的时候不包括父元素自己
getElementsByClassName
根据类名返回元素对象合集document.querySelector
根据指定选择器返回第一个元素对象document.querySelectorAll
根据指定选择器返回所有元素对象注意:querySelector
和 querySelectorAll
里面的选择器需要加符号,比如: document.querySelector(‘#nav’);
// 切记里面的选择器需要加符号 :类选择器.box id选择器 #nav
var firstBox = document.querySelector('.box');
获取自定义的属性 | element.getAttribute(‘属性’); |
主要设置自定义的属性 | element.setAttribute(‘属性’,‘值’); |
移除属性 | element.removeAttribute(‘属性’); |
<div id="demo" index="1" class="nav"></div>
<script>
var div = document.querySelector('div');
// 1. 获取元素的属性值
console.log(div.id);// (1) element.属性
console.log(div.getAttribute('index'));
// 2. 设置元素属性值
div.className = 'navs';// (1) element.属性= '值'
div.setAttribute('class', 'footer'); // (2) element.setAttribute('属性', '值');class 特殊 这里面写的就是class 不是className
div.removeAttribute('index'); // 3 移除属性 removeAttribute(属性)
</script>
事件三要素:
// 点击一个按钮,弹出对话框
//(1) 事件源 事件被触发的对象 谁 按钮
var btn = document.getElementById('btn');
//(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
//(3) 事件处理程序 通过一个函数赋值的方式 完成
btn.onclick = function() {
alert('点秋香');
}
var aa = document.getElementById('btn'); //1. 获取事件源
// aa.onclick 2. 注册事件(绑定事件)
aa.onclick = function(){ //3. 添加事件处理程序(采用函数赋值形式)
alert('hello);
}
element.innerText
:去除html标签,同时空格和换行也会去掉(不识别html标签,去掉一切修饰,只要里面内容text)element.innerHTML
:起始位置到终止位置的全部内容,包括HTML标签,同时保留空格和换行(识别html标签,保留一切,包括空格和换行)<body>
<div></div>
<p>
我是文字
<span>123</span>
</p>
<script>
var div = document.querySelector('div');
div.innerText = '今天是: 2019';
// innerText 就是把插入的内容完完全全当作content来看,完全不考虑它是否有html表示
div.innerHTML = '今天是: 2019';
// 这两个属性是可读写的 可以获取元素里面的内容
var p = document.querySelector('p');
console.log(p.innerText); //输出: 我是文字 123
console.log(p.innerHTML);
//输出: 我是文字
// 123
</script>
</body>
element.style
element.className
<body>
<div class="first">文本</div>
<script>
var test = document.querySelector('div');
test.onclick = function() {
this.style.backgroundColor = 'purple'; // 1. 使用 element.style 获得修改元素样式
this.style.marginTop = '100px';
this.className = 'first change';// 1. 使用 element.className
}
</script>
</body>
注意:JS 修改 style 样式操作 ,产生的是行内样式,CSS权重比较高
如果有同一组元素,我们相要某一个元素实现某种样式,需要用到循环的排他思想算法:
1. 所有元素全部清除样式(干掉其他人)
2. 给当前元素设置样式 (留下我自己)
3. 注意顺序不能颠倒,首先干掉其他人,再设置自己
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
// 1. 获取所有的按钮元素
var btns = document.getElementsByTagName('button');
//btns得到的是一个伪数组,里面每一个元素btns[i]
for(var i=0 ; i<btns.length ; i++){
btns[i].onclick = function(){
// (1. 所有元素全部清除样式(干掉其他人)
for(var i=0;i<btns.length;i++){
btns[i].style.backgroundColor = '';
}
// (2. 给当前元素设置样式 (留下我自己)
this.style.backgroundColor = 'pink';
}
}
</script>
onclick | 鼠标单击 |
ondbclick | 鼠标双击 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onmousedown | 按下鼠标 |
onmouseup | 释放鼠标 |
onblur | 失去焦点触发,当光标离开时触发 |
onfocus | 得到焦点 ,当屏幕上的光标进入对象区域内时触发focus事件 |
onload | 当页面或元素完成加载 |
onchange | 元素的内容发生改变,包括value,name等属性的内容(eg:对文本框输入文字,当鼠标指针移离该文本框时,若对象内容与原来内容不同,则就会触发Change事件.) |
onkeydown | 按下键盘 |
onkeyup | 松开键盘 |
onkeypress | 按下键盘并松开 |
onresize | 浏览器窗口或帧的大小发生变化时触发 |
onsubmit | 通常和form标记配合使用,其作用是用于送出数据 |
onselect | 当一个文本框,文本区域对象中的文本被选中时就会触发Select事件,未被选择则不会出现提示框 |
onerror | 当网页因为某种原因出现错误时就会触发,在错误处理程序中可以绑定操作,该标签常用与, 配合. |
DOM元素属性分为内置属性和自定义属性,比如我们常见的class、id、src、input等属性,都是内置属性,简单理解就是元素自带的属性。而自定义属性就是程序员为了方便使用而自己给元素设置的属性。
1.获取属性值
① element.属性
获取属性值:获取内置属性值(元素本身自带的属性),如img.src
② element.getAttribute('属性');
获得自定义的属性 (标准) 我们程序员自定义的属性;
2.设置属性值
① element.属性 = ‘值’
设置内置属性值。
② element.setAttribute('属性', '值');
设置自定义的属性 (标准)
3.移除属性
① element.属性 = ''
将值设置为空,就相当于移除了内置属性值。
② element.removeAttribute('属性');
移除自定义的属性
自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。为此H5给我们新增了自定义属性:
(1) 设置H5自定义属性
H5规定自定义属性以data-开头作为属性名并且赋值。比如 或者使用 JS 设置
element.setAttribute(‘data-index’, 2)
(2) 获取H5自定义属性
① 原来获取自定义属性的方式依旧起效: element.getAttribute(‘data-index’);
② H5新增方式: element.dataset.index
或者 element.dataset[‘index’]
具有兼容性问题(dataset 是一个集合里面存放了所有以data开头的自定义属性)
如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
<div getTime="20" data-index="2" data-list-name="andy"></div>
//如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
console.log(div.dataset.listName);
获取元素通常使用两种方式:
(1) 利用 DOM 提供的方法获取元素 (2) 利用节点层级关系获取元素
DOM提供的方法,在前面的文章里已经介绍过了,就是document.getElementById(),document.querySelector 等。现在将介绍第二种方式–利用节点关系来获取元素,以及进行节点操作。
- 父级节点:
node.parentNode
或node.parentElement
都可返回某节点的父节点,注意是最近的一个父节点,如果指定的节点没有父节点则返回 null- 子节点:
childNodes
(不推荐)所有的子节点 包含 元素节点 文本节点等等,因此,如果只想要获得里面的元素节点,则需要专门处理。 所以我们一般不提倡使用
children
(推荐) 只返回子元素节点 也是我们实际开发常用的
firstChild
返回第一个子节点,找不到则返回null。同样,也是包含所有的节点,即包含文本节点。
lastChild
返回最后一个子节点,找不到则返回null。同样,也是包含所有的节点。
firstElementChild
返回第一个子元素节点,找不到则返回null。lastElementChild
返回最后一个子元素节点,找不到则返回null。但是这两个方法有兼容性问题,IE9 以上才支持。
实际开发中,firstChild 和 lastChild 包含其他节点,操作不方便,那么我们如何获取第一个子元素节点或最后一个子元素节点呢?(利用children返回子元素节点的性质)
解决方案:
parentNode.chilren[0]
parentNode.chilren[parentNode.chilren.length - 1]。
- 兄弟节点
nextSibling
(包含所有节点)返回当前元素的下一个兄弟节点,找不到则返回null。同样,也是包含所有的节点。
previousSibling
(包含所有节点)返回当前元素上一个兄弟节点,找不到则返回null。同样,也是包含所有的节点。
nextElementSibling
返回当前元素下一个兄弟元素节点,找不到则返回null,previousElementSibling
返回当前元素上一个兄弟元素节点,找不到则返回null。但是这两个方法有兼容性问题, IE9 以上才支持。
如何解决兼容性问题 ?
—— 自己封装一个兼容性的函数,如下:
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if (el.nodeType === 1) {
return el;
}
}
return null;
}
2. 创建节点
我们想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
① 创建节点 document.createElement('tagName');
② 添加节点
node.appendChild(child)
将一个节点添加到指定父节点的子节点列表末尾,类似于 CSS 里面的 after伪元素。
node.insertBefore(child,指定元素)
将一个节点添加到父节点的指定子节点前面,类似于 CSS 里面的 before 伪元素。
// 1. 创建节点元素节点
var li = document.createElement('li');
// 2. 添加节点 node.appendChild(child)
var ul = document.querySelector('ul');
ul.appendChild(li);
removeChild()
方法从 DOM 中删除一个子节点,返回删除的节点cloneNode()
方法返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点
<body>
<ul>
<li>1111</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1. node.cloneNode(); 括号为空或者里面是false 浅拷贝 只复制标签不复制里面的内容
// 2. node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容
var lili = ul.children[0].cloneNode(true);
var bb= ul.children[0].cloneNode();
ul.appendChild(lili);
console.log(lili);//除了复制标签,还将内容复制
console.log(bb);//只会复制标签
</script>
</body>
三种动态创建元素的区别:
doucument.write()
:是直接将内容写入页面的内容流。但是文档流加载完毕,再调用这句话会导致页面全部重绘
element.innerHTML
:是将内容写入某个 DOM 节点,不会导致页面全部重绘
document.createElement()
:创建多个元素效率稍低一点点,但是结构更清晰
document.write()的用法
9.1. 注册事件(绑定事件)
给元素添加事件,称为注册事件或者绑定事件。
注册事件有两种方式:传统方式和方法监听注册方式
传统注册方式 | 如onclick 注册事件是唯一的,同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数 |
方法监听注册方式 | addEventListener(),同一个元素同一个事件可以注册多个监听器,按注册顺序依次执行(IE9 之前的 IE 不支持此方法,可使用 attachEvent() 代替) |
eventTarget.addEventListener(type,listener[,useCapture])
当该对象触发指定的事件时,就会执行事件处理函数
type:事件类型字符串,比如click,mouseover,注意这里不要带on
listener:事件处理函数,事件发生时,会调用该监听函数
useCapture:可选参数,是一个布尔值,默认是 false。学完 DOM 事件流后,我们再进一步学习
<button>方法监听注册事件</button>
<button>ie9 attachEvent</button>
<script>
var btns = document.querySelectorAll('button');
// (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
btns[0].addEventListener('click', function() {
alert(22);
})
btns[0].addEventListener('click', function() {
alert(33);
})
// 3. attachEvent ie9以前的版本支持(这里必须加on)
btns[1].attachEvent('onclick', function() {
alert(11);
})
</script>
9.2. 解绑事件(删除事件)
eventTarget.removeEventListener(type,listener[,useCapture]);
type:事件类型字符串,比如click,mouseover,注意这里不要带on
eventTarget.detachEvent(eventNameWithOn,callback);
eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带 on
传统方式删除监听:
eventTarget.onclick = null;
<div></div>
<script>
var div = document.querySelector('div');
// 监听事件:相当于点击div块会弹出alert,但是只有第一次点击有用,之后就取消监听
div.addEventListener('click',fn); // 里面的fn 不需要调用加小括号
function fn(){
alert('监听!!!!!');
div.removeEventListener('click',fn) //删除监听
}
div.attachEvent('onclick',fnl);
function fnl(){
alert('老式方法');
div.detachEvent('onclick',fnl)
}
</script>
删除事件兼容性解决方案
function removeEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 removeEventListener 方法
if (element.removeEventListener) {
element.removeEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.detachEvent) {
element.detachEvent('on' + eventName, fn);
} else {
element['on' + eventName] = null;
}
9.3 DOM事件流
事件流描述的是从页面中接收事件的顺序
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
DOM事件流分为三个阶段:①捕获阶段 ②当前目标阶段 ③冒泡阶段
eventTarget.removeEventListener(type,listener[,useCapture]);
其中useCapture为true时表示捕获阶段,为false或不写时冒泡阶段。JS 代码中只能执行捕获或者冒泡其中的一个阶段。
捕获阶段实例: document -> html -> body -> father -> son
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// dom 事件流 三个阶段
// 1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
// 2. onclick 和 attachEvent(ie) 只能得到冒泡阶段。
// 3. 捕获阶段 如果addEventListener 第三个参数是 true 那么则处于捕获阶段
// DOM事件流:document -> html -> body -> father -> son (按照这个顺序查找
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, true);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, true);
//虽然father监听代码写得靠后,但是因为DOM流的影响,我们点击子盒子,会先弹出 father,再弹出 son
/* document -> html -> body -> father -> son
根据上面DOM事件流,先看 document 的事件,没有;再看 html 的事件,没有;
再看 body 的事件,没有;再看 father 的事件,有就先执行;再看 son 的事件,再执行。*/
</script>
冒泡阶段实例:son -> father ->body -> html -> document
有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// 4. 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 那么则处于冒泡阶段 son -> father ->body -> html -> document
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
// 我们点击子盒子,会弹出 son、father、document
/* 冒泡阶段开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点
son -> father ->body -> html -> document */
</script>
9.4 事件对象
eventTarget.onclick = function(event) {
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
}
eventTarget.addEventListener('click', function(event) {
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
})
官方解释:event 对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态
简单理解:事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面
它有很多属性和方法,比如:
这个 event 是个形参,系统帮我们设定为事件对象,不需要传递实参过去。当我们注册事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)
事件对象也有兼容性问题 在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event
中获取查找 。兼容性的写法 e = e || window.event;
var div = document.querySelector('div');
div.onclick = function(e) {
// console.log(window.event); //在IE6~8中,浏览器不传递参数,用window.event中获取查找
// e = e || window.event; //兼容方法
console.log(e); //标准浏览器中是浏览器给方法传递的参数,只需要定义形参 e 就可以获取到
}
事件对象的常见属性和方法
e.target 和 this 的区别::
<ul>
<li>abc</li>
<li>abc</li>
</ul>
<script>
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
console.log(this); // 我们给ul 绑定了事件 那么this 就指向ul
console.log(e.currentTarget);
console.log(e.target);//我们点击的是li时 e.target 指向的就是li;当点击ul时就是ul
//当上面点击li时,分别返回:ul ul li
//当点击ul时,全部为ul
})
// 了解兼容性
// div.onclick = function(e) {
// e = e || window.event;
// var target = e.target || e.srcElement;
// console.log(target);
// }
</script>
事件对象阻止默认行为
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
<input type="submit" value="提交" name="sub">
</form>
<script>
// 2. 阻止默认行为(事件) 让链接不跳转 或者让提交按钮不提交
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 标准写法
})
// 3. 传统的注册方式
a.onclick = function(e) {
// 普通浏览器 e.preventDefault(); 方法
// 低版本浏览器 ie678 returnValue 属性
// 我们可以利用return false 也能阻止默认行为 没有兼容性问题 特点: return 后面的代码不执行了, 而且只限于传统的注册方式
return false;
alert(11);
}
</script>
禁止事件冒泡: 禁止事件冒泡
e.stopPropagation(); // stop 停止 Propagation 传播
e.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡
//结合上面,兼容性写法
if(e && e.stopPropagation){
e.stopPropagation();
}else{
window.event.cancelBubble = true;
}
9.5 事件委托
不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点
<ul>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) { // 1. 给父节点添加侦听器
// e.target 这个可以得到我们点击的对象(根据上面学到的e.taeget和this区别。e.target点击了哪个元素,就返回哪个元素)
e.target.style.backgroundColor = 'pink'; // 2. 利用冒泡原理
})
</script>
以上案例:给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上, ul 有注册事件,就会触发事件监听器。
禁止鼠标右键与鼠标选中
contextmenu
主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单selectstart
禁止鼠标选中// 1. contextmenu 我们可以禁用右键菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
})
// 2. 禁止选中文字 selectstart
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
鼠标事件对象
// 鼠标事件对象 MouseEvent
document.addEventListener('click', function(e) {
// 1. client 鼠标在可视区的x和y坐标
console.log(e.clientX);
console.log(e.clientY);
// 2. page 鼠标在页面文档的x和y坐标
console.log(e.pageX);
console.log(e.pageY);
// 3. screen 鼠标在电脑屏幕的x和y坐标
console.log(e.screenX);
console.log(e.screenY);
})
鼠标不断的移动,使用鼠标移动事件:mousemove (例如:跟随鼠标移动的天使)
案例分析: “跟随鼠标移动的天使” 视频学习
① 鼠标不断的移动,使用鼠标移动事件:mousemove
② 在页面中移动,给document注册事件
③ 图片要移动距离,而且不占位置,使用绝定位即可
④ 核心原理:每次鼠标移动,我们都会获得最新的鼠标坐标,把这个x和y坐标做为图片的top和left值就可以移动图片
var pic = document.querySelector('img');
document.addEventListener('mousemove', function(e) {
// 1. mousemove只要我们鼠标移动1px 就会触发这个事件
// 2.核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 把这个x和y坐标做为图片的top和left 值就可以移动图片
var x = e.pageX;
var y = e.pageY;
console.log('x坐标是' + x, 'y坐标是' + y);
//3 . 千万不要忘记给left 和top 添加px 单位
pic.style.left = x - 50 + 'px';
pic.style.top = y - 40 + 'px';
});
window.onload :页面加载。当文档内容完全加载完成会触发该事件(包括图像,脚本文件,CSS文件等),就调用的处理函数。
onload是等页面内容全部加载完毕,再去执行处理函数。
只能写一次,如果有多个,会以最后一个window.onload为准
window.onload = function(){
};
// 或者
window.addEventListener("load",function(){});
addEventListener :事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等,加载速度比load更快一些
document.addEventListener('DOMContentLoaded',function(){})
window.onresize | 只要窗口大小发生像素变化,就会触发(经常利用这个完成响应式布局) |
setTimeout() | 用于设置一个定时器,该定时器在定时器到期后执行调用函数 |
clearTimeout() | 用于取消了先前通过调用 setTimeout()建立的定时器 |
setInterval() | 重复调用一个函数,每隔这个时间,就去调用一次回调函数 |
clearInterval ( ) | 取消了先前通过调用 setInterval() 建立的定时器 |
用法格式:
window.setTimeout(调用函数,[延迟的毫秒数]);
window.clearTimeout(timeoutID)
window.setInterval(回调函数,[间隔的毫秒数]);
<body>
<button>点击停止定时器</button>
<script>
var btn = document.querySelector('button');
function callback() {
console.log('爆炸了');
}
var timer = setTimeout(callback, 3000);
btn.addEventListener('click', function() {
clearTimeout(timer);
})
// setInterval 每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数
setInterval(function() {
console.log('继续输出');
}, 1000);
//clearInterval ( ) 方法取消了先前通过调用 setInterval() 建立的定时器
btn.addEventListener('click', function() {
clearInterval(timer);
})
</script>
</body>
2. JS执行机制
JS是单线程,也就是说,同一个时间只能做一件事。这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。
这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
3. navigator对象
navigator 对象包含有关浏览器的信息,它有很多属性。常用的是userAgent,该属性可以返回由客户机发送服务器的user-agent头部的值。
下面前端代码可以判断用户是用哪个终端打开页面的,如果是用 PC 打开的,我们就跳转到 PC 端的页面,如果是用手机打开的,就跳转到手机端页面
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
window.location.href = ""; //手机
} else {
window.location.href = ""; //电脑
}
4. history对象
history 对象,与浏览器历史记录进行交互
btn.addEventListener('click', function() {
// history.forward();
history.go(1);
})
总结:
构造函数原型 prototype
prototype
属性,指向另一个对象,注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有问答:原型是什么?
问答:原型的作用是什么?
constructor 构造函数:
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
一般情况下,对象的方法都在构造函数(prototype)的原型对象中设置
如果有多个对象的方法,我们可以给原型对象prototype采取对象形式赋值,但是这样会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个constructor指向原来的构造函数
Star.prototype = {//采取对象方式给它添加一个方法,就是把原先的prototype原型对象给覆盖了
constructor:Star, //一定要记得指回constructor 我们称为构造函数,因为它指回构造函数本身
//constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
sing:function(){
console.log('我会唱歌');
},
movie:function(){
console.log('我会电影');
}
}
2. 对象原型 __ proto __
对象都会有一个属性 _proto_
指向构造函数的prototype
原型对象,之所以我们对象可以使用构造函数prototype
原型对象的属性和方法,就是因为对象有_proto_
原型的存在。
_proto_
对象原型和原型对象 prototype
是等价的
_proto_对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
构造函数、实例、原型对象三者关系:
原型对象this指向:构造函数中的 this指向我们的实例对象
原型对象里面放的是方法,这个方法里面的this指向的是这个方法的调用者,也就是这个实例对象
// 1. 构造函数的问题.
function Star(uname, age) {
//公共属性定义到构造函数里面
this.uname = uname; // 构造函数中的 this指向我们的实例对象 也就是ldh和zxy
this.age = age;
}
//公共的方法我们放到原型对象身上
//Star.prototype.sing = function() {
// console.log('我会唱歌');
//}
//Star.prototype.movie= function() {
// console.log('我会电影');
//}
//上面两个方法可以合并
Star.prototype = function(){
constructor:Star, //一定要记得指回constructor 我们称为构造函数,因为它指回构造函数本身
//constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
//如果不写这一步,就相当于star新建一个对象原型,原先的
sing:function(){
console.log('我会唱歌');
},
movie:function(){
console.log('我会电影');
}
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(ldh.sing === zxy.sing);
console.log(ldh.__proto__ === Star.prototype);
下面两个区别:第一个是采取点形式,是在原来的prototype 原型对象里面追加一个方法
而第二个是采取对象方式给它添加一个方法,就是把原先的prototype原型对象给覆盖了,这就会错
Star.prototype.sing = function() {
console.log('我会唱歌');
}
Star.prototype = function(){
sing:function(){
console.log('我会唱歌');
}
}
9、扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法
比如给数组增加自定义求偶数和的功能
// 原型对象的应用 扩展内置对象方法
Array.prototype.sum = function() {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
};
// Array.prototype = {
// sum: function() {
// var sum = 0;
// for (var i = 0; i < this.length; i++) {
// sum += this[i];
// }
// return sum;
// }
// }
var arr = [1, 2, 3];
console.log(arr.sum());
console.log(Array.prototype);
var arr1 = new Array(11, 22, 33);
console.log(arr1.sum());
10. call()
调用这个函数,并且修改函数运行时的 this 指向
fun.call(thisArg,arg1,arg2,......)
其中fun
是调用的函数,thisArg
是当前调用函数 this 的指向对象。arg1,arg2
传递的其他参数
// call 方法
function fn(x, y) {
console.log('我希望我的希望有希望');
console.log(this); // Object{...}
console.log(x + y); // 3
}
var o = {
name: 'andy'
};
fn.call(o, 1, 2);
借用构造函数继承父类型属性:
核心原理: 通过 call() 把父类型的 this 指向子类型的 this,这样就可以实现子类型继承父类型的属性
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 2 .子构造函数
function Son(uname, age, score) {
Father.call(this, uname, age);// this 指向子构造函数的对象实例
// 这就相当于继承父类Father了
this.score = score;
}
var son = new Son('刘德华', 18, 100);
console.log(son);