day03 js 路由跳转 选项卡 变量提升问题 自定义标签 对DOM对象的操作 定时器 js中的面向对象 BOM对象的操作
一.小知识
1.什么是动态页面和静态页面?
动态: 指的是有数据交互的, 有数据驱动的页面叫动态页面
2.库和框架的区别
库: 小而精, 实现了js的一部分功能, 主要是DOM的操作,如jQuery
框架: 大而全,如VUE
3.路由的跳转
3.1.用来做单页面应用:
通过a标签, 给url加锚点实现
整个页面没有跳转: 页面中原来的标题栏不变, 只是下面的页面渲染在切换
3.2.如何获取url里面的锚点(#/home), 通过BOM对象获取,浏览器对象模型
window.onhashchange = function () { //点击a标签时,url栏会变化: 这时用window.onhashchange就可以检测url栏是否有变化
switch(window.location.hash){ //用window.location.hash获取锚点
case '#/home': ... ; break; //不同的锚点对应不同的页面切换
case '#/course': ... ; break;
default: break;
}
}
<body>
<a href="#/home">首页</a>
<a href="#/course">课程</a>
<div class="app"></div> //这个内容区域不应该这样写,应该使用DOM的操作,动态地创建和销毁,(此处需做缓存,以后会说到)
<script>
window.onhashchange = function () {
switch(window.location.hash){ //window可以省略, hash可以获取url中的'#/home'
case '#/home':
document.getElementsByClassName('app')[0].innerHTML = '<h2>我是首页</h2>';
break;
case '#/course':
document.getElementsByClassName('app')[0].innerHTML = '<h2>我是课程</h2>';
break;
default:
break;
}
}
</script>
</body>
4.tab栏选项卡
点击的选项卡变色, 其他不变色; 下面的内容区域与上面的选项对应切换
点一个按钮: 如何让其他按钮不变, 可以利用排他思想: 先让所有的按钮都不变, 然后选中的变化
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
*{ /*清除默认样式*/
margin: 0;
padding: 0;
}
#tab{ /*给大盒子设置固定宽,高不用设置,让子盒子撑起高度,搞个外边框*/
width: 480px;
margin: 20px auto;
border: 1px solid red;
}
ul{ /*标题栏ul的宽和大盒子的宽一样,给下面的li清除浮动,去掉默认的小圆点*/
width: 100%;
overflow: hidden;
list-style: none;
}
ul li{ /*把所有的li浮动起来,实现并排显示,给每个li设置固定的宽高,让文字水平居中,垂直居中,给li搞个灰色*/
float: left;
width: 160px;
height: 60px;
text-align: center;
line-height: 60px;
background-color: #cccccc;
}
ul li a{ /*给a标签设置字体为黑色,去掉下划线*/
text-decoration: none;
color: #000;
}
li.active{ /*动态设置: li的颜色,为红色*/
background-color: #ff0000;
}
p{ /*内容区p,初始隐藏,设置为红色,固定一个高度,文字水平居中,垂直居中*/
display: none;
height: 200px;
text-align: center;
line-height: 200px;
background-color: red;
}
p.active{ /*动态设置: 把内容区显示出来*/
display: block;
}
</style>
</head>
<body>
<div id="tab"> <!--大盒子里,两个小盒子,第一个小盒子为ul选项区,里面是li; 第二个小盒子是p内容区-->
<ul>
<li class="active">
<a href="script:void(0);">首页</a>
</li>
<li>
<a href="script:void(0);">新闻</a>
</li>
<li>
<a href="script:void(0);">图片</a>
</li>
</ul>
<p class="active">首页内容</p>
<p>新闻内容</p>
<p>图片内容</p>
</div>
<script>
//先做标题栏效果, 在做内容区效果
var objlis = document.getElementsByTagName('li'); //获取所有选项卡li
var objps = document.getElementsByTagName('p'); //获取所有内容区p
//var i; //变量的提升: for里面的var i=0;提到了for的外面var i;
for (var i=0; i<objlis.length; i++) { //这个循序for用于检测所有li的点击事件: 就不用创建三个点击事件了
objlis[i].index = i; //排除变量提升带来的影响: 把i保存起来,谁能存?不同的标签对象objlis[i]可以,把i存到'对象.属性'里面
objlis[i].onclick = function () { //这里的i没问题: 是绑定事件时给i的值
//因为还没有点击,所以驱动程序还没开始执行,因为是全局的变量i,所以没有点击时: 驱动里的i没有问题
//但是当有点击事件的时候, for已经完事了,这时驱动里面的i是全局的变量,是固定值: 3
for (var j=0; j<objlis.length; j++){ //这个循环for用于重置所有的li和p: 就不用写三个重置li和三个重置p了
objlis[j].className = ''; //利用排他思想,先把所有li重置: 重置颜色为灰色
objps[j].className = ''; //利用排他思想,先把所有p重置: 重置所有内容区为隐藏
}
this.className = 'active'; //单独设置点击的li
//objlis[i].className = 'active'; //这里的this用objlis[i]不行,因为js的变量提升,点击事件事,驱动里的i是全局的 3
//console.log(i); //测试验证: 驱动里的i的结果是for完事后全局变量i的结果, 是3
objps[this.index].className = 'active'; //单独设置点击li对应的内容区域
}
}
</script>
5.变量的提升(坑)
5.1.如何理解: 这是因为js会把文档中所有的var定义的变量,提升到文档的开头,叫做'变量的提升'
5.2.带来的问题: 作用域的问题: 会把局部的变量作用域搞成全局的, 也就是说没有js中没有了局部变量
5.3.如何解决:
5.3.1.方式一: 把变量i保存起来,用不同的不变的标签对象的属性来保存: 即'对象.属性'来保存变量的值
console.log(a); //如果单独这样打印会报错: a is not defined
var a = 2; //如果有了这句,上面就会打印: undefined
{
var a = 6; //由于js的变量提升: 这句实际是两句: 第一句变量提升:是在文档的最开头声明变量 var a; 第二句是在这里赋值变量 a = 6;
console.log(a); //打印6; 全局的a的值
}
console.log(a); //打印6; 全局的a的值
5.3.2.方式二: 使用es6的let解决选项卡的问题: 而且var都可以用let替换, 不会有任何问题
console.log(a); //直接报错
{
let a = 3;
console.log(a);
}
console.log(a); //直接报错
5.3.3.方式二: 用let替换var,实现选项卡功能,更简单
<script>
let objlis = document.getElementsByTagName('li');
let objps = document.getElementsByTagName('p');
for (let i=0; i<objlis.length; i++) {
objlis[i].onclick = function () {
for (let j=0; j<objlis.length; j++){
objlis[j].className = '';
objps[j].className = '';
}
objlis[i].className = 'active';
objps[i].className = 'active';
}
}
</script>
6.自定义标签
自定义标签名,当系统的东西没有你写的好的时候, 那么就自定义
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
abc{
width: 100px;
height: 100px;
background-color: red;
display: block;
}
</style>
</head>
<body>
<div id="box">
<p class="child1">bajie</p>
</div>
<script>
var objDiv = document.getElementById('box');
var objP = document.createElement('abc');
objDiv.appendChild(objP);
</script>
</body>
</html>
二.DOM的操作:
对DOM对象的操作:
增
var objP2 = document.createElement('p'); //创建新子节点
objDiv.insertBefore(objP2,objP1); //插入子节点,bojP1是新子节点的参照的兄弟节点
<body>
<div id="box">
<p class="child1">bajie</p>
</div>
<script>
var objDiv = document.getElementById('box');
var objP1 = document.getElementsByClassName('child1')[0];
var objP2 = document.createElement('p');
objDiv.insertBefore(objP2,objP1); //插入到参考元素之前, 没有After
objP2.innerText = 'wukong';
</script>
</body>
三.定时器(两种)
1.一次性定时器
作用: 可以用来做异步
垃圾回收机制: js和python一样都有,但是不包括定时器对象
开定时器: var timer = setTimeout(fn, 1000) //一秒之后调用fn函数, 只调用一次就结束
关定时器: clearTimeout(timer)
<body>
<button id="start">开启定时器</button>
<button id="clear">清除定时器</button>
<script>
var timer = null; //全局变量,还是要事先定义, 不要暨希望变量提升来做这个事
document.getElementById('start').onclick = function () {
timer = setTimeout(function () { //未来数据交互的时候, 如果数据阻塞了, 可以考虑加一个一次性定时器来处理
console.log(111) //异步体现在哪? 不等待: 定时器外面的代码不用等我定时器里的代码执行完再往下走
},3000);
console.log(222);
//clearTimeout(timer); //清除的时机: 如果在开始计时后的3秒内'清理', 是可以阻止 111 的打印的
};
document.getElementById('clear').onclick = function () {
clearTimeout(timer);
}
</script>
</body>
2.循环周期定时器
作用: 可以做倒计时
可以做动画: 比如每一秒改动值
垃圾回收机制: js和python一样都有,但是不包括定时器对象
开定时器: var timer = setInterval(fn, 1000) //每一秒执行一次fn (interval 间隔), 循环调用
关定时器: clearInterval(timer)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#box{
width: 100px;
height: 100px;
background-color: red;
}
</style>
</head>
<body>
<button id="start">开启定时器</button>
<button id="clear">清除定时器</button>
<div id="box"></div>
<script>
var timer = null; //timer对象需要事先定义好
var count = 0;
document.getElementById('start').onclick = function () {
var objDiv = document.getElementById('box');
clearInterval(timer); //每次点击时,清除之前的定时器, 防止多次按钮的时候, 产生加速效果
timer = setInterval(function () {
count += 10;
objDiv.style.marginLeft = count + 'px';
},200)
}
</script>
</body>
</html>
四.js中的面向对象
1.创建对象的5种常用方式
1.1.使用Object()函数: 内置的构造函数来创建对象
var person = new Object();
person.name = 'bajie';
person.age = 18;
person.fav = function () {
alert(person.name);
}
person.fav();
1.2.字面量方式创建对象: 推荐这种方式创建对象
var person = {
name: 'bajie',
age: 18,
fav: function () {
alert(this.name);
}
};
person.fav();
1.1.和1.2.两种方式创建
优点: 非常简单
缺点: 但是有个问题, 当创建多个类似的对象时, 代码会重复多次
如何解决? 使用"工厂模式"
1.3.工厂模式创建对象: 把重复创建对象的过程写进函数: 函数里面使用1.1.或1.2.的方式
function createPerson() {
var person = {
name: 'bajie',
age: 18,
fav: function () {
alert(this.name);
}
};
return person;
}
var p1 = createPerson();
var p2 = createPerson();
console.log(p1.name)
console.log(p2.name)
console.log(p1 === p2); //测试验证: 两个对象不是一个内存地址
console.log(p1 instanceof Object); //使用instanceof虽然可以检测出p1是属于object类型,但是不能检测出是属于person
1.3.方式创建
优点: 解决了代码冗余的问题
缺点: 不能判断实例对象到底属于哪个类,只能判断对象是属于Object类型
如何解决: 使用"自定义的构造函数方式创建对象"
1.4.自定义的构造函数方式创建对象
1.4.1.普通函数
创建: function person() { ... }
调用: person()
1.4.2.构造函数
创建: function Person() { ... } //创建和普通函数并无不同: 构造函数名第一个字母大写,为了和普通函数区分
调用: new Person() //区别在于调用: 任意一个函数,包括普通函数,只要是用new来调用,那么就是构造函数
1.4.3.使用 new Person()时经历的四个阶段
1).创建一个新的对象
2).将构造函数的作用域赋给新的对象,使this指向新对象
3).执行构造函数代码
4).返回新对象
function Person(name, age) { //自定义一个构造函数
this.name = name;
this.age = age;
this.fav = function () {
alert(this.name);
};
}
var p1 = new Person('八戒', 18);
console.log(p1); //和原生的有一点不同: 原生的方法都在 __proto__里, 而自定义的方法直接在对象里
p1.fav();
function Fruit(name, age) { //自定义另一个构造函数
this.name = name;
this.age = age;
this.fav = function () {
alert(this.name);
};
}
var f1 = new Fruit('wukong', 100);
console.log(p1 instanceof Object);
console.log(f1 instanceof Object);
console.log(p1 instanceof Person);
console.log(f1 instanceof Fruit); //测试验证: 四个结果都是true, 而且能区分对象是属于哪个类了
1.4.方式创建
优点: 可以区分对象的类型了
缺点: 现在有个问题: 上面的Person中有一个fav的方法, 当我们进行多次调用的时候, 无疑是内存的消耗
如何解决: 原型模式创建对象
1.5.原型模式创建对象: 把方法放到他们的父类(原型)中,以后用的时候希望使用这个
自定义的构造函数Person的父类: 是已经存在的了,是Person.prototype
只需往父类上设置方法即可: Person.prototype.showName = function(){ ... }
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.showName = function () { //把相同的方法设置在父类上
console.log(this.name); //this是谁? 是Person
};
var p1 = new Person('datang', 100);
var p2 = new Person('bajie', 99);
p1.showName();
console.log(p1); //测试验证: 此时对象的方法就在__proto__里面了, 和原生的构造函数一样了
console.log(p1.showName === p2.showName); //测试验证: 此时p1和p2两个对象共享同一个函数: showName()
四.BOM 浏览器对象模型 (borwser object model)
1.没有太多操作: 因为浏览器都给我们做好了, 比如 前进 后退 刷新按钮 标题栏上面的叉 滚动 地址栏上的信息
2.BOM是什么: 操作浏览器部分功能的api
3.BOM的结构: BOM是包含DOM的.
BOM
window
DOM-document history location screen (screen 屏幕)
五.BOM中的操作
1.弹出系统对话框,下面这些都是window.xxx的简写,因为它是window的子方法
alert() 不同浏览器外观不一样
confirm() 兼容不好
prompt() 不推荐
2.打开窗口,关闭窗口
window.open(url,target)
url: 要打开的地址
target: 新窗口的位置: _self _blank _parent
需求: 3秒后, 打开百度网页,未来做完某些事,直接跳到另外一个网址时用这个
<script>
setTimeout(function () {
window.open('http://www.baidu.com/','_self'); //在自己当前窗口打开百度网址
},3000);
</script>
3.location对象
3.1.window.location对象中有很多方法和属性
属性:
location.hash :url中的锚点 :"#asdf"
location.host :主机(主机名+端口号) :"www.baidu.com:80"
location.hostname :主机名 :"www.baidu.com"
location.port :端口 :"80"
location.protocol :协议 :https :是把用户名密码先打包再传输, s = ssl
location.href :url完整地址 :"https://www.baidu.com:80/home/a.jpg?srt=info&asd"
location.origin :原始地址 :"https://www.baidu.com:80"
location.pathname :路径名(也叫路由地址) :"/home/a.jpg"
location.search :查询,get时才有的 :"?srt=info&asd"
方法:
location.reload() :就是浏览器上的: 刷新
location.toString()
3.2.需求: 跳转网页, 注意: 这个是在当前窗口打开
window.location.href = 'http://www.baidu.com'; //也可以进行网页的跳转
4.navigator对象 (navigator 领航员)
window.navigator.userAgent: 用户代理: 用的什么系统的浏览器
window.navigator.platform: 浏览器支持的系统
5.history
history.go(-1): 就是浏览器上的: 后退 :history.back()
history.go(0): 就是浏览器上的: 刷新 :location.reload()
history.go(1): 就是浏览器上的: 前进 :history.forward()
这几个都是全局刷新, 尽量少用; 局部刷新比较好(必用ajax)