一、JavaScript——DOM
宿主对象:JS运行环境提供的对象。
DOM(Document Object Model):JavaScript操作网页上的元素(标签)的API
- DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作
1.1、JavaScript——DOM开篇
1.什么是window
window:是一个全局对象,代表浏览器中一个打开的窗口,每个窗口都是一个window对象
窗口包含收藏夹、地址栏、选项卡。
2.什么是document
document是window的一个属性,这个属性是一个对象
document:代表当前窗口中的整个网页,整个内容区域
通过document对象我们就可以操作整个网页上的所有内容。
3.什么是DOM
DOM 定义了访问和操作HTML文档(网页)的标准方法
DOM(Document Object Model):文档模型对象
简单说DOM就是document对象
学习DOM就是学习如何通过document对象操作网页上的内容
console.log(window.document, typeof window.document);
1.2、JavaScript——获取DOM元素
1.在JavaScript中HTML标签也称之为DOM元素
2.使用document的时候前面不用加window
<div class="father">
<form>
<input type="text" name="test">
<input type="password" name="test">
</form>
</div>
<div class="father" id="box">我是div</div>
1.通过id获取指定元素
由于id不可以重复,所以找到了就会将找到的标签包装成一个对象返回给我们,找不到就返回null
注意点:DOM操作返回的是一个对象,这个对象是宿主类型对象(浏览器提供的对象)
let oDiv = document.getElementById("box");
console.log(oDiv, typeof oDiv);
2.通过class获取指定元素
由于class可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组
let oDiv2 = document.getElementsByClassName("father");
console.log(oDiv2, typeof oDiv2);
3.通过name名称获取
表单提交时将name当做key,值当做value提交。
由于name可以重复, 所以找到了就返回一个存储了标签对象的数组,找不到就返回一个空数组
注意点:
getElementsByName 在不同的浏览器其中工作方式不同。在IE和Opera中,getElementsByName() 方法还会返回那些 id 为指定值的元素。
let oDiv3 = document.getElementsByName("test");
console.log(oDiv3, typeof oDiv3);
4.通过标签名称(html标签)获取
由于标签名称可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组
let oDiv4 = document.getElementsByTagName("div");
console.log(oDiv4, typeof oDiv4);
5.通过选择器(CSS中的选择器)获取
querySelector只会返回根据指定选择器找到的第一个元素
let oDiv51 = document.querySelector("#box");
let oDiv52 = document.querySelector(".father");
console.log(oDiv51, typeof oDiv51,oDiv52, typeof oDiv52);
6.通过选择器获取
querySelectorAll会返回指定选择器找到的所有元素
let oDiv6 = document.querySelectorAll(".father");
console.log(oDiv6, typeof oDiv6);
7.获取指定元素所有的子元素
children属性获取到的是指定元素中所有的子元素(只取标签)
childNodes属性获取到的是指定元素中所有的节点(包括标签(元素),文本,属性)。
注意点:
回车:是一个文本节点
什么是节点:
DOM对象(document),这个对象以树的形式保存了界面上所有的内容
HTML页面每一部分都是由节点(标签(元素),文本,属性)
<div>
<h1>1</h1>
<h2>2</h2>
<p class="item">3</p>
<p>4</p>
<span>5</span>
</div>
let oDiv = document.querySelector("div");
console.log(oDiv.children);
console.log(oDiv.childNodes);
//childNodes使用后,只选取元素
for (let node of oDiv.childNodes) {
// console.log(node.nodeType);
//node.nodeType为3是文本节点;元素是1;
// if (node.nodeType == 1){
if (node.nodeType == Node.ELEMENT_NODE){
console.log(node);
}
}
8.获取指定节点中的第一个子节点
获取指定节点中的第一个子元素
let oDiv = document.querySelector("div");//获取父节点
console.log(oDiv.firstChild);//#text 第一个子节点是文本节点
console.log(oDiv.firstElementChild);//<h1>1</h1> 第一个子元素
9、获取指定节点中最后一个子节点
获取指定元素中最后一个子元素
console.log(oDiv.lastChild);//#text 最后一个子节点是文本节点
console.log(oDiv.lastElementChild);//<span>5</span> 最后一个子元素
10、通过子元素获取父元素、父节点
//过去火狐浏览器不支持parentElement,现在支持了
let item = document.querySelector(".item");//获取div的子元素
console.log(item.parentElement);
console.log(item.parentNode);
//兼容性的写法
let item2 = item.parentElement || item.parentNode;
11.获取相邻上一个节点
获取相邻上一个元素
//拿到item,获取上一个节点
console.log(item.previousSibling);//#text 上一个节点是文本节点
console.log(item.previousElementSibling);//<h2>2</h2> 上一个元素
12.获取相邻下一个节点
获取相邻下一个元素
console.log(item.nextSibling);//#text 下一个节点是文本节点
console.log(item.nextElementSibling);//<p>4</p> 下一个元素
1.3、JavaScript——节点增删改查
<div>
<h1>我是标题</h1>
<p>我是段落</p>、
</div>
1.创建节点
let oSpan = document.createElement("span");
console.log(oSpan, typeof oSpan);// <span></span> "object"
2.添加节点
注意点:appendChild默认会将指定的元素添加到最后
let oDiv = document.querySelector("div");//获取div并包装为对象
oDiv.appendChild(oSpan);
3.插入节点(不想用appendChild将节点放在最后)
let oH1 = document.querySelector("h1");
oDiv.insertBefore(oSpan, oH1);//将span添加到div中,并且添加到h1的前面
4.删除节点
//注意点:在JS中如果想要删除某一个元素,只能通过对应的父元素来删除,元素是不能自杀的。
oSpan.parentNode.removeChild(oSpan);//获取span的父节点,删除父节点div中的span
5.克隆节点
注意点:cloneNode方法默认不会克隆子元素,如果想要克隆子元素需要传递一个true。
let newDiv = oDiv.cloneNode();//克隆结果只有div,不包含其子元素
console.log(newDiv);
let newDiv2 = oDiv.cloneNode(true);//克隆div及其子元素
console.log(newDiv2);
1.4、JavaScript——属性增删改查
无论是通过document创建还是查询出来的标签,系统都会将元素包装成一个对象返回给我们,
系统在包装这个对象的时候会自动将元素的属性都包装到这个对象中,
所以只要拿到这个对象就可以拿到标签属性,操作标签属性
<img src="image/109951163693399700.jpg" alt="我是alt" title="我是title" gg = "GG">
let oImg = document.querySelector("img");
console.dir(oImg);
1.如何获取元素属性
注意点:通过对象.属性名称的方式(oImg.gg)无法获取到自定义属性的取值
通过对象.getAttribute的方式可以获取到自定义属性的取值
console.log(oImg.gg);
console.log(oImg.getAttribute("gg"));
2.如何修改元素属性
注意点:通过对象.属性名称的方式(oImg.gg)无法修改自定义属性的取值
通过对象.setAttribute的方式可以修改自定义属性的取值。
oImg.gg = "新的title";
oImg.setAttribute("gg", 222)
3.如何新增元素属性
注意点:setAttribute方法如果属性不存在就是新增,如果属性存在就是修改
oImg.it6656 = "sdf";
oImg.setAttribute("it6656", 222)
4.如何删除元素属性
注意点:通过对象.属性名称的方式(oImg.gg)无法删除自定义属性的取值
通过对象.removeAttribute方式可以删除自定义属性的取值。
oImg.gg = "";
oImg.removeAttribute("gg")
1.5、JavaScript——操作元素内容
<div>我是div
<h1>我是标题</h1>
<p>我是段落</p>
</div>
1.获取元素内容
1.1.innerHTML获取的内容包含标签,innerText/textContent获取的内容不包含标签。
1.2.innerText/textContent获取的内容不会去除两端的空格,innerText获取的内容会去除两端的空格
let oDiv = document.querySelector("div");
console.log(oDiv.innerHTML);
console.log(oDiv.innerText);
console.log(oDiv.textContent);
2.设置元素内容
无论通过innerHTML/innerText/textContent设置内容,新的内容都会覆盖原有的内容
区别:
如果通过innerHTML设置数据,数据中包含标签,会转换成标签之后再添加
如果通过innerText/textContent设置数据,数据中包含标签,不会转换成标签,会当做一个字符串直接设置。
oDiv.innerHTML = "<span>我是span</span>";
oDiv.innerText = "<span>我是span</span>";
oDiv.textContent = "<span>我是span</span>";
注意点:为了兼容性考虑,innerText、textContent在企业开发中需要结合使用。
setText(oDiv, "weru");
function setText(obj, text) {//兼容性解决办法。
if ("textContent" in obj){//判断obj对象中有没有textContent属性
obj.textContent = text;
}else{
obj.innerText = text;
}
}
1.6、JavaScript——操作元素样式
<style>
.box{
width: 200px;
height: 200px;
background: salmon;
}
</style>
<div></div>
1.设置元素样式
let oDiv = document.querySelector("div");
方法一
oDiv.className = "box";
方法二
注意点:过去CSS中通过-连接的样式,在JS中都是驼峰命名。
通过JS添加的样式都是行内样式,会覆盖掉同名的CSS样式。
oDiv.style.width = "300px";
oDiv.style.height = "300px";
oDiv.style.backgroundColor = "#123421";
2.获取元素样式
注意点:通过对象的style属性,只能获取行内样式的属性值,无法获取CSS的属性值。
如果想获取CSS设置的属性值,必须通过getComputedStyle方法来获取
getComputedStyle方法接收一个参数,这个参数就是要获取的元素对象
getComputedStyle方法返回一个参数,这个参数就是保存了CSS设置的样式和属性值。
console.log(oDiv.style.width);
let style = window.getComputedStyle(oDiv);
console.log(style.width);
1.7、JavaScript——点击事件
事件:http://www.w3school.com.cn/jsref/dom_obj_event.asp
<button>我是按钮</button>
<a href="www.baidu.com">我是a标签</a>
1.什么是事件:
用户和浏览器之间的交互行为我们就称之为事件,比如:点击,移入/移出
2.如何给元素绑定事件
在JavaScript中所有的HTML标签都可以添加事件
元素.事件名称 = function(){};
当对应事件被触发时候,就会自动执行function中的代码。
let oBtn = document.querySelector("button");
oBtn.onclick = function () {//给oBtn对象绑定了单击事件
alert("按钮被点击了")
};
注意点:如果给元素添加了和系统同名的事件,我们添加的事件不会覆盖系统添加的事件
let oA = document.querySelector("a");
oA.onclick = function () {
alert("a标签被点击了");
return false;//可以用我们添加的事件覆盖掉系统同名的事件
}
1.8、JavaScript——定时器
<button id="start">开始</button>
<button id="end">结束</button>
<script>
在JavaScript中有两种定时器,一种是重复执行的定时期,一种是执行一次的定时期
1.重复执行的定时期
// window.setInterval相当于setInterval。window可以省略
setInterval(function () {//创建一个可以重复执行的定时期,这个定时期可以每隔1000毫秒执行一次函数。并返回定时器的id
console.log("随便");
},1000)
let start = document.querySelector("#start");
let id = null;
//开启定时期
start.onclick = function () {
id = setInterval(function () {
console.log("随便");
},1000)
};
//关闭定时期
let end = document.querySelector("#end");
end.onclick = function () {
clearInterval(id);
};
2.只执行一次的的定时期
window.setTimeout(function () {//创建一个重复执行一次的定时期,这个定时期可以在1000毫秒之后执行一次函数。并返回定时器的id
console.log("只执行一次的的定时期");
},5000);
//开启定时期
let id = null;
let start = document.querySelector("#start");
start.onclick = function(){
let id = window.setTimeout(function () {
console.log("只执行一次的的定时期");
},5000);
};
//关闭定时期
let end = document.querySelector("#end");
end.onclick = function(){
clearTimeout(id);
};
2.3、JavaScript-箭头函数和普通函数区别
1.普通函数/方法中的this,谁调用就是谁。
2.箭头函数中的this,是父作用域的this,不是调用者。
function demo() {
console.log(this);
}
demo();//window。等于window.demo();
let p = {
name:"GG",
say:function () {
console.log(this);//{name: "GG", say: ƒ}
},
h1:()=>{//箭头函数不在其他函数中,所以箭头函数还是全局作用域。在JS中只有定义一个新的函数才会开启一个新的作用域
console.log(this);//Window
}
};
p.say();
p.h1();
console.log(this);//window
function Person() {
this.name = "JJ";
this.say = function () {
console.log(this);
}
this.h1 = () =>{//箭头函数在其他函数中,所以箭头函数属于其它函数。既然箭头函数属于构造函数,所以箭头函数中的this就是构造函数中的this
console.log(this);
}
}
let p2 = new Person();
p2.say();//Person {name: "JJ", say: ƒ, h1: ƒ} 谁调用就是谁
p2.h1();//Person {name: "JJ", say: ƒ, h1: ƒ} 箭头函数在构造函数中,有新的作用域。
function Person2() {
this.name = "JJ";
this.say = function () {
console.log(this);
};
this.h1 = () =>{
console.log(this);
}
}
let p3 = new Person2();
p3.say.call({name:"GG"});//{name: "GG"}
注意点:箭头函数中的this永远都指着它所属的作用域的this
无法通过bind/call/apply来修改
p3.h1.call({name:"GG"});//Person2 {name: "JJ", say: ƒ, h1: ƒ}
4.4、JavaScript-移入移出事件
<style>
*{
padding: 0;
margin: 0;
}
div{
width: 300px;
height: 300px;
background: darkgoldenrod;
}
</style>
<div></div>
<script>
获取div
let oDiv = document.querySelector("div");
1.移入事件
注意点:对于初学者来说,为了避免未知的bug。建议使用onmouseenter
oDiv.onmouseover = function () {
console.log("移入事件");
}
oDiv.onmouseenter = function () {
console.log("移入事件");
}
2.移出事件
注意点:对于初学者来说,为了避免未知的bug。建议使用onmouseleave
oDiv.onmouseout = function () {
console.log("移出事件");
}
oDiv.onmouseleave = function () {
console.log("移出事件");
}
3.移动事件
oDiv.onmousemove = function () {
console.log("移动事件");
}
</script>
4.7、JavaScript-表单校验
<form action="http://www.it666.com/">
<input type="text" placeholder="输入账号" name="userName">
<input type="password" placeholder="输入密码" name="userPas">
<input type="submit" value="登录">
</form>
<script>
需求:
1.账号和密码必须大于等于6位
2.如果账号密码的长度不够就改变input输入框的背景颜色
3.如果用户输入的账号或者密码不符合需求,那么就不能提交
1.拿到需要操作的元素
let oUserName = document.querySelector("input[type = text]");
let oPassword = document.querySelector("input[type = password]");
let oSubmit = document.querySelector("input[type = submit]");
2.监听注册按钮的点击
oSubmit.onclick = function () {
2.1、拿到输入的用户名,判断长度是否大于6位
注意点:如果想获取input中输入的内容,必须通过value属性来获取
console.log(oUserName.value);
console.log(oPassword.value);
if (oUserName.value.length < 6){
oUserName.style.background = "red";
return false;
}else{
oUserName.style.background = "#fff";
}
if (oPassword.value.length < 6){
oPassword.style.background = "red";
return false;
}else{
oPassword.style.background = "#fff";
}
}
</script>
4.8、JavaScript-表单事件
常用表单事件:
1.获取焦点
2.失去焦点
3.表单中数据发生变化的事件
<input type="text">
<script>
let oInput = document.querySelector("input");
//1.监听input获取焦点
oInput.onfocus = function () {
console.log("获取到焦点");
}
//2.监听input失去焦点
oInput.onblur = function () {
console.log("失去了焦点");
}
//3.监听input内容改变
//注意点:onchange事件只有表单失去焦点的时候,才能拿到修改之后的数据
// oninput事件可以实时获取用户修改之后的数据,只要用户修改了数据就会调用执行
// oninput事件只有在IE9以上的浏览器才能使用(现在大部分都是IE9以上的浏览器)
// 在IE9以下,如果想实时的获取到用户修改之后的数据,可以通过onpropertychange事件来实现
/*oInput.onchange = function () {
console.log(this.value);
}*/
oInput.oninput = function () {
console.log(this.value);
}
4.9、JavaScript-表单效果
<form action="http://www.it666.com">
<input type="text" placeholder="输入内容" name="text">
<input type="submit" value="搜索"><!--disabled = "disabled"-->
</form>
需求:输入框中没有内容就禁用提交按钮
1.input设置属性disabled,即不能使用
let oText = document.querySelector("input[type = text]");
let oSubmit = document.querySelector("input[type = submit]");
//在JS中如果HTML标签的属性名称和取值一样的时候,那么JS就会返回true/false
// console.log(oSubmit.disabled);//true
oSubmit.disabled = true;
oText.oninput = function () {
console.log(this.value.length);
oSubmit.disabled = this.value.length === 0;
console.log(oSubmit.disabled);
};
通过代码给input设置数据
注意点:通过JS代码给input设置数据,不会触发oninput事件
oText.value = "123";
4.10、JavaScript-TAB选项卡
应用:公告栏
淘宝是用移入移出做的
我们用点击事件尝试,为了引入闭包的知识点
五、JavaScript闭包
5.1、JavaScript-闭包基本概念
1.什么是闭包(closure)?
闭包是一种特殊的函数
2.如何生成一个闭包?
当一个内部函数引用了外部函数的数据(变量、函数)时,那么内部的函数就是闭包
所以只要满足“是函数嵌套”、“内部函数引用外部函数数据”
3.闭包特点:
只要闭包还在使用外部函数的数据,那么外部的数据就一直不会被释放
也就是说可以延长外部函数数据的生命周期
4.闭包注意点:
当后续不需要使用闭包的时候,一定要手动将闭包设置为null,否则会出现存泄露
function test() {//只要定义函数,就会开启新的作用域
// 在新的作用域内定义变量
var i = 666;//局部变量,有自己的生命周期。从定义开始一直到大括号结束。
}//只要代码执行到了大括号结束,i这个变量就会自动释放。
console.log(i);//i is not defined
function test() {
var i = 666;//局部变量
return function demo() {//由于demo满足闭包的两个条件,所以demo函数就是闭包
console.log(i);
}
}
let fn = test();//将demo函数整体给了fn
fn(); //输出fn函数,相当于输出demo函数。输出666不符合常理,i到test大括号之后就应该释放,现在却可以使用。验证了闭包的特点
5.2、JavaScript-循环索引同步上
本案例是循环索引的同步和闭包的使用
<button>我是按钮1</button>
<button>我是按钮2</button>
<button>我是按钮3</button>
<script>
函数同索引不同步
for循环执行完成后,创建了三个onclick对象的函数。此时i为3,点击按钮,才会触发onclick的function。因此点击三个按钮都是输出3
let oBtn = document.querySelectorAll("button");
for (var i = 0; i < oBtn.length; i++) {
let btn = oBtn[i];
btn.onclick = function () {
console.log(i);//3
}
}
函数同索引同步
let oBtn = document.querySelectorAll("button");
for (var i = 0; i < oBtn.length; i++) {
let btn = oBtn[i];
//循环执行了三次,开启了三次test函数的作用域。每个作用域中都有index
(function test(index) {
//注意点:onclick对应的方法由于满足了闭包的条件,所以onclick对应的方法也是一个闭包。只要闭包中还在使用外部函数的数据(index),外部函数数据就不会被释放
btn.onclick = function () { //在立即执行的函数中新开启作用域。是个闭包,可以调用其外部函数的数据(index),index不会被释放
console.log(index);
}
})(i);
}
5.3、JavaScript-循环索引同步下
在ES6中,如果在循环中通过let定义变量,那么这个变量是一个局部变量
i是全局变量
var list = [];
for (var i = 0; i < 3; i++){
var fn = function test() {
console.log(i);
};
list.push(fn);//通过循环创建三个函数,每创建一个函数,就将函数存入数组中。
}
console.log(i);//3 全局变量
console.log(list);//3
list[0]();//3 取出第0个元素,()代表执行第0个元素
list[1]();//3
list[2]();//3
i是局部变量
var list2 = [];
// 注意点:由于i2是局部变量,所以每次执行完循环体都会重新定义一个i2变量
for (let i2 = 0; i2 < 3; i2++){
let fn2 = function test2() {//定义一次开辟一次存储空间
console.log(i2);
};
list2.push(fn2);
}
// console.log(i2);//i is not defined 局部变量
console.log(list2);
list2[0](); //0
list2[1](); //1
list2[2](); //2
1.for循环中用let定义变量,是局部变量
2.for循环中let定义变量,每次执行循环体都会重新定义一个变量
3.通过let定义变量,在循环体中存在使用变量的函数,这个函数是闭包。
for (let i3 = 0; i3 < 3; i3++){
//ES6中由于{}是块级作用域,所以只要在块级作用域中定义了一个函数,并且这个函数中用到了块级作用域中的数据,那么这个函数就是闭包。
function test3() {
console.log(i3);
}
}
//i3是局部变量,执行到for的大括号后应该结束,为什么test3();后还可以访问到呢?意味着test3此时是个闭包函数。for(){}中是块级作用域,在块级作用域中定义函数,并且使用外部作用域中的数据,就是闭包。只要闭包访问外部数据,外部数据就不会销毁。
//后定义的test3覆盖了先定义的test3.
test3();//2
5.4、JavaScript-排它思想
1.什么是排他思想?
清除其他非选中元素的样式,只设置当前选中元素的样式
<style>
*{
margin: 0;
padding: 0;
}
ul{
list-style: none;
width: 400px;
border: 1px solid #000;
margin: 100px auto;
}
.current{
background: #ee9900;
}
</style>
<div>
<ul>
<li class="current">我是第1个li</li>
<li>我是第2个li</li>
<li>我是第3个li</li>
<li>我是第4个li</li>
<li>我是第5个li</li>
<li>我是第6个li</li>
</ul>
</div>
<script>
方法一:使用for循环,将所有li的样式清空:
//1.获取需要操作的元素
let oItems = document.querySelectorAll("li");
//2.给li绑定onclick事件
for (let i = 0; i < oItems.length; i++){
let item = oItems[i];//取出每个li
item.onclick = function () {
//3.排它。依靠for循环,性能降低。
for (let j = 0; j < oItems.length; j++){
let li = oItems[j];
li.className = "";
}
//4.给选中的元素设置样式
this.className = "current";
console.log(i);
}
}
方法二:ES6的做法
let oItems = document.querySelectorAll("li");
let previousIndex = 0;
for (let i = 0; i < oItems.length; i++){
let item = oItems[i];//取出每个li
item.onclick = function () {
//3.排它。
let preItem = oItems[previousIndex];
preItem.className = "";
//4.给选中的元素设置样式
this.className = "current";
previousIndex = i;
console.log(i);
}
}
方法三:ES6之前的做法。使用立即执行的函数
let oItems = document.querySelectorAll("li");
let previousIndex = 0;
for (var i = 0; i < oItems.length; i++){
let item = oItems[i];//取出每个li
(function (index) {
item.onclick = function () {
//3.排它。
let preItem = oItems[previousIndex];
preItem.className = "";
//4.给选中的元素设置样式
this.className = "current";
previousIndex = index;
console.log(i);
}
})(i);
}
命名规范:
在前端开发中,如果id名称是由多个单词组成的,那么建议使用下划线来链接
在前端开发中,如果class名称是由多个单词组成的,那么建议使用中划线来链接
在前端开发中,命名时用_开头,表示为私有方法,不要修改。实际上不一定真的是私有方法,仅仅为了方便程序员之间沟通