目录
一、Web APIs
1. Web APIs 和JS基础关联性
1.1 JS的组成
1.2 JS基础阶段以及Web APIs 阶段
JS 基础学习 ECMAScript 基础语法为后面做铺垫,Web APIs 是 JS 的应用,大量使用 JS 基础语法做交互效果
2. API 和 Web API 总结
- API 是为程序员提供的一个接口,帮助我们实现某种功能,我们会使用就可以了,不必纠结内部如何实现
- Web API 主要是针对于浏览器提供的借口,主要针对于浏览器做交互效果。
- Web API 一般都有输入和输出(函数的传参的返回值),Web API 很多都是方法(函数)
- 学习 Web API 可以结合前面学习内置对象方法的思路学习
二、DOM
1.DOM简介
1.1 什么是 DOM
文档对象模型(Document Object Model,简称 DOM ),是 W3C 组织推荐的处理可拓展标记语言(HTML或者XML)的标准编程接口。
W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容,结构和样式。
1.2 DOM 树
- 一个页面就是一个文档,DOM 中使用 document 表示
- 元素:页面中所有标签都是元素,DOM 中使用 element 表示
- 节点:页面中的所有内容都是节点(标签、属性、文本、注释等),DOM 中使用 node 表示
DOM 把以上内容都看作是对象
2.获取元素
2.1 如何获取页面元素
DOM 在我们实际开发中主要用来操作元素。
我们如何来获取页面中的元素呢?
获取页面中的元素可以使用以下几种方式:
- 根据 ID 获取
- 根据标签名获取
- 通过 HTML5 新增的方法获取
- 特殊元素获取
2.2 根据 ID 获取
使用 getElementByld()方法可以获取带有ID的元素对象。
2.3 根据标签名获取
使用 getElementsByTagName() 方法可以返回带有指定标签名的对象的集合。
注意:
1. 因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历。
2.得到元素对象是动态的。
还可以获取某个元素(父元素)内部所有指定标签名的子元素
element.getElementsByTagName('标签名');
注意:父元素必须是单个对象(必须指明是哪一个元素对象),获取的时候不包括父元素自己。
<body>
<ul>
<li>ul里面的li 1</li>
<li>ul里面的li 2</li>
<li>ul里面的li 3</li>
<li>ul里面的li 4</li>
</ul>
<ol id="ol">
<li>ol里面的li 1</li>
<li>ol里面的li 2</li>
<li>ol里面的li 3</li>
</ol>
<script>
// 1.返回的是 获取过来的元素对象的集合 以伪数组的形式存储
var lis = document.getElementsByTagName('li');
console.log(lis);
console.log(lis[0]);
// 2.我们想要一次打印里面的元素对象我们可以采取遍历的方式
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
// 3.如果页面中只有一个li 返回的是伪数组的形式
// 4.如果页面中没有这个元素 饭蝴蝶是空的伪数组的形式
// 5.element.getElementsByTagName('标签名'); 父元素必须是指定的单个元素
//var ol = document.getElementsByTagName('ol'); //[ol]
//console.log(ol[0].getElementsByTagName('li');
var ol = document.getElementById('ol');
console.log(ol.getElementsByTagName('li'));
</script>
</body>
2.4 通过HTML5 新增的方法获取
1. document.getElementsByClassName('类名'); //根据类名返回元素对象集合
2. document.querySelector('选择器'); //根据指定选择器返回第一个元素对象
3. document.querySelectorAll('选择器'); //根据指定选择器返回
<body>
<div class="box">盒子1</div>
<div class="box">盒子2</div>
<div id="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
// 1.getElementsByClassName 根据类名获得某些元素集合
var boxs = document.getElementsByClassName('box');
console.log(boxs);
// 2.querySelector() 返回指定选择器的第一个元素对象 切记 里面的选择器需要加符号
var firstBox = document.querySelector('.box'); // 类选择器前加.
console.log(firstBox);
var nav = document.querySelector('#nav'); // id选择器前加#
console.log(nav);
var li = document.querySelector('li'); // 标签选择器不用额外添加
// 3.querySelectorAll() 返回指定选择器的所有元素对象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
</script>
</body>
2.5 获取特殊元素(body,html)
获取body元素
document.body //返回body元素对象
获取html元素
document.documentElement //返回html元素对象
3.事件基础
3.1 事件概述
事件是可以被JS监测到的行为,简单理解为:事件是触发的响应机制。
网页中每个元素都可以产生某些可以触发JS的事件,例如,我们可以在用户点击某个按钮时产生一个事件,然后去执行某些操作。
3.2 执行时间的步骤
- 获取事件源
- 注册时间(绑定事件)
- 添加事件处理程序(采取函数赋值形式)
3.3 常见的鼠标事件
鼠标事件 | 触发条件 |
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
4.操作元素
JS 的 DOM 操作可以改变网页内容、结构和样式,我们可以利用 DOM 操作元素来改变元素里面的内容、属性等。注意以下都是属性
4.1 改变元素内容
element.innerText
从起始位置到终止位置的内容,但它去除 html 标签(不会识别 html 标签),同时空格和换行也会去掉
element.innerHTML
起始位置到终止位置的全部内容,包括 html 标签(识别 html 标签),同时保留空格和换行
4.2 操作元素-修改元素内容
4.3 常用元素的属性操作
- innerText、innerHTML 改变元素内容
- src、href
- id、alt、title
4.4 案例:不同时间问候
4.5 表单元素的属性操作
利用 DOM 可以操作如下表单元素的属性:
type、value、checked、selected、disabled
4.6 案例:显示隐藏密码
案例分析
- 核心思路:点击眼睛按钮,把密码框类型改为文本框就可以看见里面的密码
- 一个按钮两个状态,点击一次,切换为文本框,继续点击一次切换为密码框
- 算法:利用一个flag变量,来判断flag的值,如果是1就切换为文本框,flag设置为0,如果是0就切换为密码框,flag设置为1
4.7 样式属性操作
我们可以通过JS修改元素的大小、颜色、位置等样式。
1. element.style 行内样式操作
2. element.className 类名样式操作
注意:
1. JS 里面的样式采取驼峰命名法 比如 fontSize、backgroundColor
2. JS 修改 style 样式操作,产生的是行内样式,JS 权重比较高
element.className 类名样式操作
注意:
1. 如果样式修改较多,可以采取操作类名方式更改元素样式
2. class 因为是个保留字,因此使用 className 来操作元素类名属性
3. className 会直接更改元素的类名,会覆盖原先的类名
(先在 CSS 里添加一个类的样式)
(再进行操作)
4.7.1 案例:关闭二维码
4.7.2 案例:循环精灵图
- 首先精灵图图片排序有规律的
- 核心思路:利用 for 循环 修改精灵图片的 背景位置 background-position
- 剩下的就是考验你的数学功底了
- 让循环里面的i索引号 *44 就是每个图片的y坐标
4.7.3 案例:显示隐藏文本框内容
- 首先表单需要2个新事件,获得焦点 onfocus 失去焦点 onblur
- 如果获得焦点,判断表单里面的内容是否为默认文字,如果是默认文字,就清空表单内容
- 如果失去焦点,判断表单内容是否为空,如果为空,则表单内容改为默认文字
4.7.4 案例:密码框验证信息
(先设置好一些 CSS 样式)
(再进行操作)
4.8 总结
4.9 排他思想(算法)
4.10 案例:百度换肤效果
(省略 CSS 样式)
4.11 表格隔行变色效果
为了第一行不变色,将表单分为 thead 和 tbody 。
4.12 表单全选取消全选案例![](https://i-blog.csdnimg.cn/blog_migrate/23965c9feb7a6f5ab9217e1b91575e9e.png)
分成两部分完成:
1. 表头 thead 的全选按钮和全不选按钮控制所有按钮
2. 如果 tbody 全勾选了则全选按钮也会被勾选,反之全不勾选
(表单)
(第一部分)
(第二部分)
4.13 自定义属性的操作
1.获取属性值
- element.属性 获取属性值
- element.getAttribute('属性');
区别:
- element.属性 获取内置属性值(元素本身自带的属性)
- element.getAttribute('属性'); 主要获得自定义的属性(标准)我们程序员自定义的属性
2. 设置属性值
- element.属性 = '值' 设置内置属性值
- element.getAttribute('属性','值');
区别:
- element.属性 获取内置属性值
- element.getAttribute('属性'); 主要设置自定义的属性(标准)
3. 移除属性
- element.removeAttribute('属性');
4.14 tab栏切换操作
- Tab栏切换有2个大的模块
- 上的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想)修改类名的方式
- 下面的模块内容,会跟随上面的选项卡变化。所以下面模块变化写到点击事件里面。
- 规律:下面的模块显示内容和上面的选项卡——对应,相匹配。
- 核心思路:给上面的tab_list里面的所有小l添加自定义属性.属性值从0开始编号。
- 当我们点击tab_list里面的某个小li,让tab_con里面对应序号的内容显示,其余隐藏(排他思想)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style-type: none;
}
.tab {
width: 978px;
margin: 100px auto;
}
.tab_list {
height: 39px;
border: 1px solid #ccc;
background-color: #f1f1f1;
}
.tab_list li {
float: left;
height: 39px;
line-height: 39px;
padding: 0 20px;
text-align: center;
cursor: pointer;
}
.tab_list .current {
background-color: #c81623;
color: #fff;
}
.item_info {
padding: 20px 0 0 20px;
}
.item {
display: none;
}
</style>
</head>
<body>
<div class="tab">
<div class="tab_list">
<ul>
<li class="current">商品介绍</li>
<li>规格与包装</li>
<li>售后保障</li>
<li>商品评价(50000)</li>
<li>手机社区</li>
</ul>
</div>
<div class="tab_con">
<div class="item" style="display: block;">
商品介绍模块内容
</div>
<div class="item">
规格与包装模块内容
</div>
<div class="item">
售后保障模块内容
</div>
<div class="item">
商品评价(50000)模块内容
</div>
<div class="item">
手机社区模块内容
</div>
</div>
</div>
<script>
// 获取元素
var tab_list = document.querySelector('.tab_list');
var lis = tab_list.querySelectorAll('li');
var items = document.querySelectorAll('.item');
// for循环绑定点击事件
for (var i = 0; i < lis.length; i++) {
// 开始给5个小li 设置索引号
lis[i].setAttribute('index', i); //给所有的li设置一个自定义属性index
lis[i].onclick = function() {
// 1. 上的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想) 修改类名的方式
// 干掉所有人 其余的li清除 class 这个类
for (var i = 0; i < lis.length; i++) {
lis[i].className = '';
}
// 留下我自己
this.className = 'current';
// 2. 下面的显示内容模块
var index = this.getAttribute('index');
console.log(index);
// 干掉所有人 让其余的item 这些div 隐藏
for (var i = 0; i < items.length; i++) {
items[i].style.display = 'none';
}
// 留下我自己 让对应的item 显示出来
items[index].style.display = 'block';
}
}
</script>
</body>
</html>
4.15 H5自定义属性
自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
自定义属性获取是通过getAttribute('属性')获取。
但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。
H5给我们新增了自定义属性:
1. 设置H5自定义属性
H5规定自定义属性data-开头作为属性名并且赋值。
比如<div data-index = "1" ></div>
或者使用JS 设置
element.setAttribute('data-index',2)
2. 获取H5自定义属性
1. 兼容性获取 element.getAttribute('data-index');
2. H5新增 element.dataset.index 或者 element.dataset['index'] ie 11 才开始支持
<body>
<div getTime="20" data-index="2" data-list-name="andy"></div>
<script>
var div = document.querySelector('div');
// console.log(div.getTime); //undefind 因为getTime是自定义属性
console.log(div.getAttribute('getTime'));
div.setAttribute('data-time', 20);
console.log(div.getAttribute('data-index'));
console.log(div.getAttribute('data-list-name'));
// h5新增的获取自定义属性的方法 它只能获取data-开头的
// dataset 是一个集合里面存放了所有以data开头的自定义属性
console.log(div.dataset);
console.log(div.dataset.index);
console.log(div.dataset['index']);
// 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
console.log(div.dataset.listName);
console.log(div.dataset['listName']);
</script>
</body>
5. 节点操作
1.为什么使用节点操作
获取元素通常使用两种方式:
(1)利用 DOM 提供的方法获取元素
- document.getElementByld()
- document.getElementByTagName()
- document.querySelector 等
- 逻辑性不强、繁琐
(2)利用节点层级关系获取元素
- 利用父子兄节点关系获取元素
- 逻辑性强,但是兼容性稍差
这两种方式都可以获取元素节点,后面都会使用,但是节点操作更简单
2.节点概述
一般情况下,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
- 元素节点 nodeType 为 1
- 属性节点 nodeType 为 2
- 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)
我们在实际开发中,节点操作主要操作的是元素节点
3. 节点层级
利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
3.1 父级节点
node.parentNode
- parentNode 属性可返回某节点的父节点,注意事项最近的一个父节点
- 如果指定的节点没有父节点则返回 null
<body>
<!-- 节点的优点 -->
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<div class="demo">
<div class="box">
<span class="erweima">×</span>
</div>
</div>
<script>
// 1. 父节点 parentNode
var erweima = document.querySelector('.erweima');
// var box = document.querySelector('.box');
// 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
console.log(erweima.parentNode);
</script>
</body>
3.2 子节点
1.parentNode.chilNodes (标准)
parentNode.chilNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合。
注意:返回值里面包含了所有的子节点,包括元素节点,文本节点等。
如果只想要获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用 childNodes
var ul = documentquerySelector ('ul');
for(var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes [i].nodeType == 1) {
// ul.childNodes [i] 是元素节点
console.log (ul.chilNodes [i]);
}
}
上述方法太麻烦了,于是出现了另一种解决方式:
2. parentNode.children (非标准)
parentNode.children 是一个只读属性,返回所有的子元素及诶单。它值返回子元素节点,其余节点不返回(这个是我们要重点掌握的)。
虽然 children 是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用
示例:
<body>
<!-- 节点的优点 -->
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<ol>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ol>
<div class="demo">
<div class="box">
<span class="erweima">×</span>
</div>
</div>
<script>
// DOM 提供的方法(API)获取
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 1. 子节点 childNodes 所有的子节点 包含 元素节点 文本节点等等(此处的文本节点是 回车)
console.log(ul.childNodes);
console.log(ul.childNodes[0].nodeType); //输出3 此处序列号为0的子节点是文本节点,文本节点的nodeType是3
console.log(ul.childNodes[1].nodeType); //输出1 此处序列号为1的子节点是文本节点,元素节点的nodeType是1
// 2. children 获取所有的子元素节点 也是我们实际开发常用的
console.log(ul.children);
</script>
</body>
返回第一个/最后一个子节点
3. parentNode.firstChild
firstChild 返回第一个字及诶单,找不到则返回null。同样,也是包含所有的节点。
4. parentNode.lastChild
lastChild 返回第一个子节点,找不到则返回null。同样,也是包含所有的节点。
5. parentNode.firstElementChild
firstElementChild 返回第一个子元素节点,找不到则返回null。
6. parentNode.lastElementChild
lastElementChild 返回最后一个子元素节点,找不到则返回null。
注意:这两个方法有兼容性问题,IE9 以上才支持。
解决方案:
1.如果想要第一个子元素节点,可以使用 parentNode.children[0]
示例:
<body>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
<li>我是li5</li>
</ol>
<script>
var ol = document.querySelector('ol');
// 1. firstChild 第一个子节点 不管是文本节点还是元素节点
console.log(ol.firstChild); //输出 text 文本节点
console.log(ol.lastChild); //输出 text 文本节点
// 2. firstElementChild 返回第一个子元素节点 ie9才支持
console.log(ol.firstElementChild); //输出第一个li
console.log(ol.lastElementChild); //输出最后一个li
// 3. 实际开发的写法 既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]); //因为 ol 是个伪数组
console.log(ol.children[ol.children.length - 1]);
</script>
</body>
案例:新浪下拉菜单
分析布局:
1. 一个大盒子里面装多个菜单栏,每个菜单栏是一个<li>
2. <li>里面是<a>标签,其下拉菜单是<ul>
3.<ul>里面有几个<li>为详情
4.把<ul>定位到<a>下面
5.一开始把<ul>隐藏起来
分析:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style-type: none;
}
a {
text-decoration: none;
font-size: 14px;
}
.nav {
margin: 100px;
}
.nav>li {
position: relative;
float: left;
width: 80px;
height: 41px;
text-align: center;
}
.nav li a {
display: block;
width: 100%;
height: 100%;
line-height: 41px;
color: #333;
}
.nav>li>a:hover {
background-color: #eee;
}
.nav ul {
display: none;
position: absolute;
top: 41px;
left: 0;
width: 100%;
border-left: 1px solid #FECC5B;
border-right: 1px solid #FECC5B;
}
.nav ul li {
border-bottom: 1px solid #FECC5B;
}
.nav ul li a:hover {
background-color: #FFF5DA;
}
</style>
</head>
<body>
<ul class="nav">
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
</ul>
<script>
// 1. 获取元素
var nav = document.querySelector('.nav');
var lis = nav.children; // 得到4个小li
// 2.循环注册事件
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function () { //注册鼠标经过事件
this.children[1].style.display = 'block'; //菜单栏li 的第二个孩子 下拉菜单ul 显示
}
lis[i].onmouseout = function () { //鼠标离开
this.children[1].style.display = 'none';
}
}
</script>
</body>
3.3 兄弟节点
body>
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
// 1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点等等
console.log(div.nextSibling);
console.log(div.previousSibling);
// 2. nextElementSibling 得到下一个兄弟元素节点
console.log(div.nextElementSibling);
console.log(div.previousElementSibling);
</script>
</body>
4. 创建节点
document.createElement('tagName')
document.createElement() 方法创建由 tagName 指定的 HTML 元素。因为这些元素院线不存在,是根据我们的需求动态生成的,所以我们也称之为动态创建元素节点。(例如评论区的更新)
添加节点
1. node.appendChild (child)
node.appendChild () 方法将一个节点添加到父节点的子节点列表末尾。类似于CSS 里面的 after 伪元素。其中,node 是父级元素。
2. node.insertBefore (child,指定元素)
node.insertBefore() 方法将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before 伪元素。
<body>
<ul>
<li>123</li>
</ul>
<script>
// 1. 创建节点元素节点
var li = document.createElement('li');
// 2. 添加节点 node.appendChild(child) node 父级 child 是子级 后面追加元素 类似于数组中的push
var ul = document.querySelector('ul');
ul.appendChild(li);
// 3. 添加节点 node.insertBefore(child, 指定元素);
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
// 4. 我们想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
</script>
</body>
案例:简易版发布留言
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
padding: 100px;
}
textarea {
width: 200px;
height: 100px;
border: 1px solid pink;
outline: none;
resize: none;
}
ul {
margin-top: 50px;
}
li {
width: 300px;
padding: 5px;
background-color: rgb(245, 209, 243);
color: red;
font-size: 14px;
margin: 15px 0;
}
</style>
</head>
<body>
<textarea name="" id=""></textarea>
<button>发布</button>
<ul>
</ul>
<script>
// 1. 获取元素
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
// 2. 注册事件
btn.onclick = function() {
if (text.value == '') {
alert('您没有输入内容');
return false;
} else {
// console.log(text.value);
// (1) 创建元素
var li = document.createElement('li');
// 先有li 才能赋值
li.innerHTML = text.value;
// (2) 添加元素
// ul.appendChild(li);
ul.insertBefore(li, ul.children[0]);
}
}
</script>
</body>
5. 删除节点
nide.removeChild(child)
node.removeChild() 方法从 DOM 中删除一个子节点,返回删除的节点。
<body>
<button>删除</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光头强</li>
</ul>
<script>
// 1.获取元素
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
// 2. 删除元素 node.removeChild(child)
// ul.removeChild(ul.children[0]);
// 3. 点击按钮依次删除里面的孩子
btn.onclick = function () {
if (ul.children.length == 0) { //当没有<li>可以删除时
this.disabled = true; //禁用删除按钮
} else {
ul.removeChild(ul.children[0]);
}
}
</script>
</body>
案例:删除留言
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
padding: 100px;
}
textarea {
width: 200px;
height: 100px;
border: 1px solid pink;
outline: none;
resize: none;
}
ul {
margin-top: 50px;
}
li {
width: 300px;
padding: 5px;
background-color: rgb(245, 209, 243);
color: red;
font-size: 14px;
margin: 15px 0;
}
li a {
float: right;
}
</style>
</head>
<body>
<textarea name="" id=""></textarea>
<button>发布</button>
<ul>
</ul>
<script>
// 1. 获取元素
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
// 2. 注册事件
btn.onclick = function() {
if (text.value == '') {
alert('您没有输入内容');
return false;
} else {
// console.log(text.value);
// (1) 创建元素
var li = document.createElement('li');
// 先有li 才能赋值
li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
// (2) 添加元素
// ul.appendChild(li);
ul.insertBefore(li, ul.children[0]);
// (3) 删除元素 删除的是当前链接的li 它的父亲
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
// node.removeChild(child); 删除的是 li 当前a所在的li this.parentNode;
ul.removeChild(this.parentNode);
}
}
}
}
</script>
</body>
6. 复制/克隆 节点(重点)
node.cloneNode ()
node.cloneNode ()方法返回调用该方法的结点的一个副本,也成为克隆节点/拷贝节点
注意:
- 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
- 如果括号参数为 true 。则是深度拷贝,恢复至节点本身以及里面所有的子节点。
7. 案例:动态生成表格
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
table {
width: 500px;
margin: 100px auto;
border-collapse: collapse;
text-align: center;
}
td,
th {
border: 1px solid #333;
}
thead tr {
height: 40px;
background-color: #ccc;
}
</style>
</head>
<body>
<table cellspacing="0">
<thead>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
// 1.先去准备好学生的数据
var datas = [{
name: '魏璎珞',
subject: 'JavaScript',
score: 100
}, {
name: '弘历',
subject: 'JavaScript',
score: 98
}, {
name: '傅恒',
subject: 'JavaScript',
score: 99
}, {
name: '明玉',
subject: 'JavaScript',
score: 88
}, {
name: '大猪蹄子',
subject: 'JavaScript',
score: 0
}];
// 2. 往tbody 里面创建行: 有几个人(通过数组的长度)我们就创建几行
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) { // 外面的for循环管行 tr
// 1. 创建 tr行
var tr = document.createElement('tr');
tbody.appendChild(tr); //把创建的tr行 放进tbody里面
// 2. 行里面创建单元格(跟数据有关系的3个单元格) td 单元格的数量取决于每个对象里面的属性个数 for循环遍历对象 datas[i]
for (var k in datas[i]) { // 里面的for循环管列 td
// 创建单元格
var td = document.createElement('td');
// 把对象里面的属性值 datas[i][k] 给 td
// console.log(datas[i][k]);
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
// 3. 创建有删除2个字的单元格
var td = document.createElement('td');
td.innerHTML = '<a href="javascript:;">删除 </a>';
tr.appendChild(td);
}
// 4. 删除操作 开始
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function () {
// 点击a 删除 当前a 所在的行(链接的爸爸的爸爸) node.removeChild(child)
tbody.removeChild(this.parentNode.parentNode)
}
}
// for(var k in obj) {
// k 得到的是属性名
// obj[k] 得到是属性值
// }
</script>
</body>
8. 三种动态创建元素区别 (内含面试重点)
- document.write ()
- element.innerHTML
- document.createElement ()
区别:
- document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
- innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
- innerHTML 创建多个元素效率更高(不要凭借字符串,采取数组形式拼接),结构稍微复杂
- createElement () 创建多个元素效率稍低一点点,但是结构更清晰
总结:不同浏览器下, innerHTML 效率要比 createElement 高
<body>
<button>点击</button>
<p>abc</p>
<div class="inner"></div>
<div class="create"></div>
<script>
// window.onload = function() {
// document.write('<div>123</div>');
// }
// 三种创建元素方式区别
// 1. document.write() 创建元素 如果页面文档流加载完毕,再调用这句话会导致页面重绘
// var btn = document.querySelector('button');
// btn.onclick = function() {
// document.write('<div>123</div>');
// }
// 2. innerHTML 创建元素
var inner = document.querySelector('.inner');
// for (var i = 0; i <= 100; i++) {
// inner.innerHTML += '<a href="#">百度</a>'
// }
var arr = [];
for (var i = 0; i <= 100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join('');
// 3. document.createElement() 创建元素
var create = document.querySelector('.create');
for (var i = 0; i <= 100; i++) {
var a = document.createElement('a');
create.appendChild(a);
}
</script>
</body>
三、 DOM 重点核心
( 我们获取过来的 DOM 元素是一个对象(boject),所以称为文档对象模型 )
关于 dom 操作,我们主要针对元素的操作。主要有创建、增、删、改、查、属性操作、事件操作
1. 创建
- document.write ()
- element.innerHTML
- document.createElement ()
2. 增
-
appendChild 在后面添加
-
insertBefore 在前面添加
3. 删
- removeChild
4. 改
主要修改 dom 的元素属性,dom 元素的内容、属性,表单的值等
- 修改元素属性:src、href、title 等
- 修改普通元素内容:innerHTML、innerText
- 修改表单元素:value、type、disabled 等
- 修改元素样式:style、className
5. 查
主要获取查询 dom 的元素
- DOM 提供的 API 方法:getElementByld、getElementsByTagName 古老用法 不推荐
- H5 提供的新方法:querySelector、querySelectorAll 提倡
- 利用节点操作获取元素:父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling)提倡
6. 属性操作
主要针对于自定义属性
- setAttribute:设置 dom 的属性值
- getAttribute:得到 dom 的属性值
- removeAttribute 移除属性
7. 事件操作
给元素注册时间,采取 事件源.事件类型 = 事件处理程序
鼠标事件 | 触发条件 |
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
四、事件高级
1. 注册事件(绑定事件)
1.1 注册事件概述
给元素添加事件,称为注册事件或者绑定事件。
注册事件有两种方式:传统方式和方法监听注册方式
传统注册方式
- 利用 on 开头的时间 onclick
- <button onclick = "alert('hi~')" ></button>
- btn.onclick = function() {}
- 特点:注册事件的唯一性
- 同一个元素同一个时间只能设置值一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数
方法监听注册方式
- w3c 标准 推荐方式
- addEleventListenter() 他是一个方法
- IE9 之前的 IE 不支持此方法,可使用 attachEvent() 代替
- 特点:同一个元素同一个事件可以注册多个监听器(即监听函数)
- 按注册顺序依次执行
1.2 addEventListener 事件监听方式
eventTarget.addEventListenter (type, listener [, useCapture])
eventTarget.addEventListenter () 方法将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定的时间时,就会执行事件处理函数。
该方法接收三个参数:
- type :事件类型字符串,比如 click、mouseover,注意这里不要带 on (因为是字符串,记得带引号)
- listener :事件处理函数,事件发生时,会调用该监听函数
- useCapture : 可选参数,是一个布尔值,默认是 false、学完 DOM 事件流后,我们再进一步学习
(第三个方法 ie9 之前的版本才适用,作为了解就行,此处省略,代码中展示了)
<body>
<button>传统注册事件</button>
<button>方法监听注册事件</button>
<button>ie9 attachEvent</button>
<script>
var btns = document.querySelectorAll('button');
// 1. 传统方式注册事件
btns[0].onclick = function() {
alert('hi');
}
btns[0].onclick = function() {
alert('hao a u');
}
// 2. 事件侦听注册事件 addEventListener
// (1) 里面的事件类型是字符串 必定加引号 而且不带on
// (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
btns[1].addEventListener('click', function() { //function() 称为监听器 或 监听函数
alert(22);
})
btns[1].addEventListener('click', function() {
alert(33);
})
// 3. attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function() {
alert(11);
})
</script>
</body>
1.3 注册事件兼容性解决方案
兼容性处理的原则:首先照顾大多数浏览器。再处理特殊浏览器
2. 删除事件(解绑事件)
传统注册方式解绑
eventTarget.onclick = null;
方法监听注册方式
eventTarget.removeEventListener (type, listener [, useCapture]) ;
(第三个方法 ie9 之前的版本才适用,作为了解就行,此处省略,代码中展示了)
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div {
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var divs = document.querySelectorAll('div');
divs[0].onclick = function() {
alert(11);
// 1. 传统方式删除事件
divs[0].onclick = null;
}
// 2. removeEventListener 删除事件
divs[1].addEventListener('click', fn) // 里面的fn 不需要调用加小括号
function fn() {
alert(22);
divs[1].removeEventListener('click', fn);
}
// 3. detachEvent
divs[2].attachEvent('onclick', fn1);
function fn1() {
alert(33);
divs[2].detachEvent('onclick', fn1);
}
</script>
</body>
删除事件兼容性解决方案:
3. DOM 事件流
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.father {
overflow: hidden;
width: 300px;
height: 300px;
margin: 100px auto;
background-color: pink;
text-align: center;
}
.son {
width: 200px;
height: 200px;
margin: 50px;
background-color: purple;
line-height: 200px;
color: #fff;
}
</style>
</head>
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// dom 事件流 三个阶段
// 1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
// 2. onclick 和 attachEvent(ie) 只能得到冒泡阶段。
// 3. 捕获阶段 如果addEventListener 第三个参数是 true 那么则处于捕获阶段 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);
// 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');
})
</script>
</body>
</html>
4. 事件对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div {
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div>123</div>
<script>
// 事件对象
var div = document.querySelector('div');
div.onclick = function(e) {
// console.log(e); //传统方法
// console.log(window.event); //ie678 使用的方法
// e = e || window.event;
console.log(e);
}
// div.addEventListener('click', function(e) {
// console.log(e);
// })
// 1. event 就是一个事件对象 写到我们侦听函数的 小括号里面 当形参来看
// 2. 事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
// 3. 事件对象 是 我们事件的一系列相关数据的集合 跟事件相关的 比如鼠标点击里面就包含了鼠标的相关信息,鼠标坐标啊,如果是键盘事件里面就包含的键盘事件的信息 比如 判断用户按下了那个键
// 4. 这个事件对象我们可以自己命名 比如 event 、 evt、 e
// 5. 事件对象也有兼容性问题 ie678 通过 window.event 兼容性的写法 e = e || window.event;
</script>
</body>
</html>
4.1 什么是事件对象
官方解释:event 对象代表事件的状态,比如键盘按钮的状态、鼠标的位置、鼠标按钮的状态。
简单理解:事件发生后,跟时间相关的一系列信息数据的集合都放到这个对象卡里面,这个对象就是事件对象 event ,它有很多属性和方法。
比如:
1. 谁绑定了这个事件
2. 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
4.2 事件对象的使用语法
这个 event 是个形参,系统帮我们设定为事件对象,不需要传递实参过去。
当我们注册事件时,event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
4.3 事件对象的兼容性方案
事件对象本身的获取存在兼容问题:
1. 标准浏览器中是浏览器给方法传递的参数,只需要定义形参 e 就可以获取到。
2. 在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找。
解决:
e = e || window.event;
4.4 事件对象的常见属性和方法
4.4.1 返回触发事件对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div {
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div>123</div>
<ul>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>
<script>
// 常见事件对象的属性和方法
// 1. e.target 返回的是触发事件的对象(元素) this 返回的是绑定事件的对象(元素)
// 区别 : e.target 点击了那个元素,就返回那个元素 this 那个元素绑定了这个点击事件,那么就返回谁
var div = document.querySelector('div');
div.addEventListener('click', function(e) {
console.log(e.target);
console.log(this);
})
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 我们给ul 绑定了事件 那么this 就指向ul
console.log(this);
console.log(e.currentTarget);
// e.target 指向我们点击的那个对象 谁触发了这个事件 我们点击的是li e.target 指向的就是li
console.log(e.target);
})
// 了解兼容性
// div.onclick = function(e) {
// e = e || window.event;
// var target = e.target || e.srcElement;
// console.log(target);
// }
// 2. 了解 跟 this 有个非常相似的属性 currentTarget ie678不认识
</script>
</body>
</html>
4.4.2 事件对象阻止默认行为
<body>
<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
<input type="submit" value="提交" name="sub">
</form>
<script>
// 常见事件对象的属性和方法
// 1. 返回事件类型
var div = document.querySelector('div');
div.addEventListener('click', fn);
div.addEventListener('mouseover', fn);
div.addEventListener('mouseout', fn);
function fn(e) {
console.log(e.type);
}
// 2. 阻止默认行为(事件) 让链接不跳转 或者让提交按钮不提交
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 标准写法
})
// 3. 传统的注册方式
a.onclick = function(e) {
// 普通浏览器 e.preventDefault(); 方法
// e.preventDefault();
// 低版本浏览器 ie678 returnValue 属性
// e.returnValue;
// 我们可以利用return false 也能阻止默认行为 没有兼容性问题 特点: return 后面的代码不执行了, 而且只限于传统的注册方式
return false;
alert(11);
}
</script>
</body>
5. 阻止事件冒泡(重点)
5.1 阻止事件冒泡的两种方式
事件冒泡:开始时由最具体的元素接受,然后逐级向上传播到 DOM 最顶层节点。
事件冒泡本身的特性,会带来的坏处和好处,需要我们灵活掌握。
阻止事件冒泡
- 标准写法:利用事件对象里面的 stopPropagation () 方法
- 非标准写法:IE6~8 利用事件对象 cancelBubble 属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.father {
overflow: hidden;
width: 300px;
height: 300px;
margin: 100px auto;
background-color: pink;
text-align: center;
}
.son {
width: 200px;
height: 200px;
margin: 50px;
background-color: purple;
line-height: 200px;
color: #fff;
}
</style>
</head>
<body>
<div class="father">
<div class="son">son儿子</div>
</div>
<script>
// 常见事件对象的属性和方法
// 阻止冒泡 dom 推荐的标准 stopPropagation()
var son = document.querySelector('.son');
son.addEventListener('click', function(e) {
alert('son');
e.stopPropagation(); // stop 停止 Propagation 传播
e.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
</script>
</body>
</html>
5.2 阻止事件冒泡的兼容性解决方案
6. 事件委托(代理、委派)(重点)
事件委托
事件委托也称为事件代理,在 jQuery 里面称为事件委派。
事件委托的原理
不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
以上案例:给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li ,因为点击 li ,事件会冒泡到 ul 上,ul 有注册事件,就会触发事件监听器。
事件委托的作用
操作了依次 DOM ,提高了程序的性能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// alert('知否知否,点我应有弹框在手!');
// e.target 这个可以得到我们点击的对象
e.target.style.backgroundColor = 'pink';
})
</script>
</body>
</html>
7. 常用鼠标事件对象
7.1 取消鼠标右键菜单 / 禁用
<body>
我是一段不愿意分享的文字
<script>
// 1. contextmenu 我们可以禁用右键菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
})
// 2. 禁止选中文字 selectstart
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
</script>
</body>
7.2 鼠标事件对象
event 对象代表事件的状态,跟时间相关的一系列信息的集合。现阶段我们主要是用鼠标事件对象MouseEvent 和键盘事件对象 KeyboardEvent。
案例:跟随鼠标移动的天使
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
img {
position: absolute;
top: 2px;
}
</style>
</head>
<body>
<img src="images/angel.gif" alt="">
<script>
var pic = document.querySelector('img');
document.addEventListener('mousemove', function(e) {
// 1. mousemove只要我们鼠标移动1px 就会触发这个事件
// console.log(1);
// 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';
});
</script>
</body>
</html>
8. 常用的键盘事件
8.1 常用键盘事件
事件除了使用鼠标触发,还可以使用键盘触发。
注意:
1. 如果使用 addEventListenter 不需要加 on
2. onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头,shift 等
3. 三事件的执行顺序是:keydown -- keypress -- keyup
<body>
<script>
// 常用的键盘事件
//1. keyup 按键弹起的时候触发
// document.onkeyup = function() {
// console.log('我弹起了');
// }
document.addEventListener('keyup', function() {
console.log('我弹起了');
})
//3. keypress 按键按下的时候触发 不能识别功能键 比如 ctrl shift 左右箭头啊
document.addEventListener('keypress', function() {
console.log('我按下了press');
})
//2. keydown 按键按下的时候触发 能识别功能键 比如 ctrl shift 左右箭头啊
document.addEventListener('keydown', function() {
console.log('我按下了down');
})
// 4. 三个事件的执行顺序 keydown -- keypress -- keyup
</script>
</body>
8.2 键盘事件对象
注意:
onkeydown 和 onkeyup 不区分字母大小写, onkeypress 区分字母大小写
在我们实际开发中,我们更多的使用 keydown 和 keyup ,它能识别所有的键(包括功能键)
keypress 不识别功能键,但是 keyCode 属性能区分大小写,返回不同的 ASCII 值
<body>
<script>
// 键盘事件对象中的keyCode属性可以得到相应键的ASCII码值
// 1. 我们的keyup 和keydown事件不区分字母大小写 a 和 A 得到的都是65
// 2. 我们的keypress 事件 区分字母大小写 a 97 和 A 得到的是65
document.addEventListener('keyup', function(e) {
// console.log(e);
console.log('up:' + e.keyCode);
// 我们可以利用keycode返回的ASCII码值来判断用户按下了那个键
if (e.keyCode === 65) {
alert('您按下的a键');
} else {
alert('您没有按下a键')
}
})
document.addEventListener('keypress', function(e) {
// console.log(e);
console.log('press:' + e.keyCode);
})
</script>
</body>
案例:模拟京东按钮输入内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.search {
position: relative;
width: 178px;
margin: 100px;
}
.con {
display: none;
position: absolute;
top: -40px;
width: 171px;
border: 1px solid rgba(0, 0, 0, .2);
box-shadow: 0 2px 4px rgba(0, 0, 0, .2);
padding: 5px 0;
font-size: 18px;
line-height: 20px;
color: #333;
}
.con::before {
content: '';
width: 0;
height: 0;
position: absolute;
top: 28px;
left: 18px;
border: 8px solid #000;
border-style: solid dashed dashed;
border-color: #fff transparent transparent;
}
</style>
</head>
<body>
<div class="search">
<div class="con">123</div>
<input type="text" placeholder="请输入您的快递单号" class="jd">
</div>
<script>
// 快递单号输入内容时, 上面的大号字体盒子(con)显示(这里面的字号更大)
// 表单检测用户输入: 给表单添加键盘事件
// 同时把快递单号里面的值(value)获取过来赋值给 con盒子(innerText)做为内容
// 如果快递单号里面内容为空,则隐藏大号字体盒子(con)盒子
var con = document.querySelector('.con');
var jd_input = document.querySelector('.jd');
jd_input.addEventListener('keyup', function() {
// console.log('输入内容啦');
if (this.value == '') {
con.style.display = 'none';
} else {
con.style.display = 'block';
con.innerText = this.value;
}
})
// 当我们失去焦点,就隐藏这个con盒子
jd_input.addEventListener('blur', function() {
con.style.display = 'none';
})
// 当我们获得焦点,就显示这个con盒子
jd_input.addEventListener('focus', function() {
if (this.value !== '') {
con.style.display = 'block';
}
})
</script>
</body>
五、BOM
1. BOM 概述
1.1 什么是BOM
BOM (Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。
BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。
BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA ,DOM 的标准化组织是 W3C,BOM 最初网景公司 Netscape 浏览器标准的一部分。
1.2 BOM 的构成
BOM 比 DOM 更大,它包含 DOM。
window 对象是浏览器的顶级对象,它具有双重角色。
1. 它是 JS 访问浏览器窗口的一个接口。
2. 它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。
在调用的时候可以省略 window ,前面学习的对话框都属于 window 对象方法,如 alert()、prompt()等、
注意:window下的一个特殊属性 window.name
2. window 对象的常见事件
2.1 窗口加载事件
window.onload 是窗口(页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等),就调用的处理函数。
注意:
- 有了 window.onload 就可以把 JS 代码写到页面元素上方,因为 onload 是等页面内容全部加载完毕,再去执行处理函数。
- window.onload 传统注册事件方式只能写一次,如果有多个,会以最后一个 window.onload 为准。
- 如果使用addEventListener 则没有限制
另一种窗口加载事件的方法:
- DOMContentLoaded 事件触发时,仅当 DOM 加载完成,不包括样式表,图片,flash 等等
- IE9+ 才支持
- 如果页面的图片很多的话,从用户访问到 onload 触发可能需要较长的时间,交互效果就不能实现,必然影响用户的体验,此时用 DOMContentLoaded 事件比较合适。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
// window.onload = function() {
// var btn = document.querySelector('button');
// btn.addEventListener('click', function() {
// alert('点击我');
// })
// }
// window.onload = function() {
// alert(22);
// }
window.addEventListener('load', function() {
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
alert('点击我');
})
})
window.addEventListener('load', function() {
alert(22);
})
document.addEventListener('DOMContentLoaded', function() {
alert(33);
})
// load 等页面内容全部加载完毕,包含页面dom元素 图片 flash css 等等
// DOMContentLoaded 是DOM 加载完毕,不包含图片 falsh css 等就可以执行 加载速度比 load更快一些
</script>
</head>
<body>
<button>点击</button>
</body>
</html>
2.2 调整窗大小事件
window.onresize 是调整窗口大小加载事件,当触发时就调用的处理函数。
注意:
- 只要窗口大小发生像素变化,就会触发这个事件。
- 我们经常利用这个事件完成响应式布局。window.innerWidth 当前屏幕的宽度
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
</head>
<body>
<script>
window.addEventListener('load', function() {
var div = document.querySelector('div');
window.addEventListener('resize', function() {
console.log(window.innerWidth);
console.log('变化了');
if (window.innerWidth <= 800) {
div.style.display = 'none';
} else {
div.style.display = 'block';
}
})
})
</script>
<div></div>
</body>
</html>
3. 定时器
3.1 两种定时器
window 对象给我们提供了2个非常好用的方法-定时器。
- setTimeout()
- setInterval()
3.2 setTimeout() 定时器
setTimeout() 用于设置一个定时器,该定时器到期后执行调用函数。
注意:
- window 可以省略
- 这个调用函数可以直接写函数,或者写函数名或者采取字符串 '函数名()' 三种形式。第三种不推荐
- 延迟的毫秒数省略默认是0,如果写,必须是毫秒。
- 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。
<body>
<script>
// 1. setTimeout
// 语法规范: window.setTimeout(调用函数, 延时时间);
// 1. 这个window在调用的时候可以省略
// 2. 这个延时时间单位是毫秒 但是可以省略,如果省略默认的是0
// 3. 这个调用函数可以直接写函数 还可以写 函数名 还有一个写法 '函数名()'
// 4. 页面中可能有很多的定时器,我们经常给定时器加标识符 (名字)
// setTimeout(function() {
// console.log('时间到了');
// }, 2000);
function callback() {
console.log('爆炸了');
}
var timer1 = setTimeout(callback, 3000);
var timer2 = setTimeout(callback, 5000);
// setTimeout('callback()', 3000); // 我们不提倡这个写法
</script>
</body>
案例:5s 后自动关闭的广告
<body>
<img src="images/ad.jpg" alt="" class="ad">
<script>
var ad = document.querySelector('.ad');
setTimeout(function() {
ad.style.display = 'none';
}, 5000);
</script>
</body>
3.3 清除定时器 clearTimeout()
clearTimeout() 方法取消了先前通过调用 setTimeout() 建立的定时器。
注意:
- window 可以省略。
- 里面的参数就是定时器的标识符。
<body>
<button>点击停止定时器</button>
<script>
var btn = document.querySelector('button');
var timer = setTimeout(function() {
console.log('爆炸了');
}, 5000);
btn.addEventListener('click', function() {
clearTimeout(timer);
})
</script>
</body>
3.4 setInterval() 定时器
setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。
注意:
- window 可以省略
- 这个调用函数可以直接写函数,或者写函数名或者采取字符串 '函数名()' 三种形式。第三种不推荐
- 间隔的毫秒数省略默认是0,如果写,必须是毫秒。
- 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。
<body>
<script>
// 1. setInterval
// 语法规范: window.setInterval(调用函数, 延时时间);
setInterval(function() {
console.log('继续输出');
}, 1000);
// 2. setTimeout 延时时间到了,就去调用这个回调函数,只调用一次 就结束了这个定时器
// 3. setInterval 每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数
</script>
</body>
案例:倒计时效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div {
margin: 200px;
}
span {
display: inline-block;
width: 40px;
height: 40px;
background-color: #333;
font-size: 20px;
color: #fff;
text-align: center;
line-height: 40px;
}
</style>
</head>
<body>
<div>
<span class="hour">1</span>
<span class="minute">2</span>
<span class="second">3</span>
</div>
<script>
// 1. 获取元素
var hour = document.querySelector('.hour'); // 小时的黑色盒子
var minute = document.querySelector('.minute'); // 分钟的黑色盒子
var second = document.querySelector('.second'); // 秒数的黑色盒子
var inputTime = +new Date('2019-5-1 18:00:00'); // 返回的是用户输入时间总的毫秒数
countDown(); // 我们先调用一次这个函数,防止第一次刷新页面有空白
// 2. 开启定时器
setInterval(countDown, 1000);
function countDown() {
var nowTime = +new Date(); // 返回的是当前时间总的毫秒数
var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数
var h = parseInt(times / 60 / 60 % 24); //时
h = h < 10 ? '0' + h : h;
hour.innerHTML = h; // 把剩余的小时给 小时黑色盒子
var m = parseInt(times / 60 % 60); // 分
m = m < 10 ? '0' + m : m;
minute.innerHTML = m;
var s = parseInt(times % 60); // 当前的秒
s = s < 10 ? '0' + s : s;
second.innerHTML = s;
}
</script>
</body>
</html
3.5 停止 setInterval() 定时器
clearInterval () 方法取消了先前通过调用 setInterval () 建立的定时器。
注意:
- window 可以省略。
- 里面的参数就是定时器的标识符。
<body>
<button class="begin">开启定时器</button>
<button class="stop">停止定时器</button>
<script>
var begin = document.querySelector('.begin');
var stop = document.querySelector('.stop');
var timer = null; // 全局变量 null是一个空对象
begin.addEventListener('click', function() {
timer = setInterval(function() {
console.log('ni hao ma');
}, 1000);
})
stop.addEventListener('click', function() {
clearInterval(timer);
})
</script>
</body>
案例: 发送短信
<body>
手机号码: <input type="number"> <button>发送</button>
<script>
// 按钮点击之后,会禁用 disabled 为true
// 同时按钮里面的内容会变化, 注意 button 里面的内容通过 innerHTML修改
// 里面秒数是有变化的,因此需要用到定时器
// 定义一个变量,在定时器里面,不断递减
// 如果变量为0 说明到了时间,我们需要停止定时器,并且复原按钮初始状态
var btn = document.querySelector('button');
var time = 3; // 定义剩下的秒数
btn.addEventListener('click', function() {
btn.disabled = true;
var timer = setInterval(function() {
if (time == 0) {
// 清除定时器和复原按钮
clearInterval(timer);
btn.disabled = false;
btn.innerHTML = '发送';
} else {
btn.innerHTML = '还剩下' + time + '秒';
time--;
}
}, 1000);
})
</script>
</body>
3.6 this(重点)
this 的指向在函数定义的时候是确定不了的,只有函数执行的时候才能 this 到底指向谁,一般情况下 this 的最终指向的是那个调用它的对象
现阶段,我们先了解一下几个 this 指向
- 全局作用域或者普通函数中 this 指向全局对象 window(注意定时器里面的 this 指向window)
- 方法调用中谁调用 this 指向谁
- 构造函数中 this 指向构造函数的实例
4. JS执行机制
4.1 JS 是单线程
JS 语言的一大特点就是单线程,也就是说,同一个时间只能做同一件事。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样导致的问题是:如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载堵塞的感觉。
4.2 同步和异步
为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 次奥准,允许 JS 创建多个县城。于是,JS 中出现了同步和异步。
同步
前一个任务结束之后再执行下一个任务,程序的执行顺序和任务的排列顺序是一致的、同步的。
异步
在做这件事情的同时,可以去处理其他事情。
本质区别:这条流水线上各个流程的执行顺序不同。
4.3 JS 执行机制
由于主线程不断地重复获得任务、执行任务、再获取任务、再执行,这种机制被称为事件循环
5. location 对象
5.1 什么是 location 对象
window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析URL。因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。
5.2 URL
统一资源定位符(Uniform Resource Locator,URL) 是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL 的一般语法格式为:
5.3 location 对象的属性
重点记住:href 和 search
案例: 5s 后 跳转页面
<body>
<button>点击</button>
<div></div>
<script>
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.addEventListener('click', function() {
// console.log(location.href);
location.href = 'http://www.itcast.cn';
})
var timer = 5;
setInterval(function() {
if (timer == 0) {
location.href = 'http://www.itcast.cn';
} else {
div.innerHTML = '您将在' + timer + '秒钟之后跳转到首页';
timer--;
}
}, 1000);
</script>
</body>
案例:获取 URL 参数
负责传递参数的页面的代码:
<body>
<form action="index.html">
用户名: <input type="text" name="uname">
<input type="submit" value="登录">
</form>
</body>
负责接收参数并运用的另一个页面代码:
<body>
<div></div>
<script>
console.log(location.search); // ?uname=andy
// 1.先去掉? substr('起始的位置',截取几个字符);
var params = location.search.substr(1); // uname=andy substr(1)表示从第二个开始截取到最后
console.log(params);
// 2. 利用=把字符串分割为数组 split('=');
var arr = params.split('=');
console.log(arr); // ["uname", "ANDY"]
var div = document.querySelector('div');
// 3.把数据写入div中
div.innerHTML = arr[1] + '欢迎您';
</script>
</body>
5.4 location 对象的方法
<body>
<button>点击</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
// 记录浏览历史,所以可以实现后退功能
// location.assign('http://www.baidu.com');
// 不记录浏览历史,所以不可以实现后退功能
// location.replace('http://www.baidu.com');
location.reload(true);
})
</script>
</body>
6. navigator 对象
navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent ,该属性可以返回客户机发送的 user-agent 头部的值。
下面前端代码可以判断用户哪个终端打开页面,实现跳转
7. history 对象
window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。
history 对象一般在实际开发中比较少用,但是会在一些 OA 办公系统中见到。