20-27
约 29452 字大约 98 分钟
2025-01-20
20,Web浏览器中的Javascript
20-1 Web浏览器中的Javascript
通常也称为客户端的JavaScript,就是JavaScript运行在浏览器中;
从内容上来看,它是包括BOM和DOM;
从形式上可以分为Web文档和Web应用两种形式;
20-2 Web文档里的Javascript
Javascript可以通过document对象和它包含的element对象遍历和管理文档内容;它可以通过操纵CSS样式和类,修改文档内容的呈现;并且可以通过注册适当的事件处理程序来定义文档元素的行为;
Web文档里应当少量地应用Javascript,因为Javascript真正的作用是增强用户的浏览体验,使信息的获取和传递更容易;用户的体验不应依赖于Javascript,但Javascript可以增强体验,如:
- 创建动画和其他视觉效果,引导和帮助用户进行页面导航;
- 对表格的列进行分组,让用户更容易找到所需要的内容;
- 隐藏某些内容,当用户需要了解更详细内容时,再逐渐展示详细信息;
20-3 window对象
客户端Javascript中最重要的对象之一是window对象,window对象是所有客户端Javascript特性和API的主要接入点;它表示Web浏览器的一个窗口或窗体;Window对象定义了一些属性,比如,指定当前窗口中的URL的location属性,其还可以允许脚本在窗口中载入新的URL;
window.location = "https://www.zeronetwork.cn/";
window对象还定义了一些方法,如alert(),可以弹出一个对话框用来显示一些信息;比如:setTimeout(),可以注册一个函数,在给定的一段时间之后触发一个回调,如:
setTimeout(function(){
alert("零点网络");
},2000);
在客户端Javascript中,window对象也是全局对象,也就是window对象处于作用域链的最顶部,它的属性和方法实际上是全局变量和全局函数,所以,window.setTimeout()可以直接使用setTimeout(),也就是说,如果想引用全局窗口或全局对象的属性,通常并不需要用到window;
window还定义了很多其它重要的属性、方法和构造函数;其中最重要的属性是document,它引用Document对象,表示的是在窗口中的文档;Document对象有一些重要的方法,比如getElementById(),可以基于元素id的值返回单一的HTML元素,如:
var mydiv = document.getElementById("mydiv");
getElementById()方法返回的Element对象,也拥有一些重要的属性和方法,比如允许脚本获取它的内容、设置属性值等,如:
var mydiv = document.getElementById("mydiv");
// 如果元素为空,则往里面插入当前的日期和时间
if(mydiv.firstChild == null){
mydiv.appendChild(document.createTextNode(new Date().toString()));
}
每个Element对象才有style和className属性,允许脚本指定文档元素的CSS样式,或修改应用到元素上的CSS的类名,如:
var mydiv = document.getElementById("mydiv");
mydiv.style.height = "200px";
mydiv.style.backgroundColor = "yellow";
mydiv.className = "mydiv";
window对象、document对象和element对象还有一个重要的属性集合是事件处理程序相关的属性;可以在脚本中为之绑定一个函数,这个函数会在某个事件发生时以异步的方式调用;事件处理程序可以让Javascript代码修改窗口、文档和组成文档的元素的行为;事件处理程序的属性名是以单词“on”开头的,如:
mydiv.onclick = function(){
this.innerHTML = "<h2>零点网络</h2>";
}
window对象的onload处理程序是最重要的事件处理程序之一;当显示在窗口中的文档内容稳定并可以操作时可以触发它;Javascript代码通常封装在onload事件处理程序里;比如,可以在onload事件中,查询文档元素、修改CSS类和定义事件处理程序,如:
<style>
.newslist *{display: none;}
.newslist h1{display: block;}
.newslist_show *{display: block;}
</style>
<script>
window.onload = function(){
var elements = document.getElementsByClassName("newslist");
for(var i=0; i<elements.length; i++){
var elt = elements[i];
var title = elt.getElementsByTagName("h1")[0]
showHandler(title,elt);
}
function showHandler(title,elt){
title.onclick = function(){
if(elt.className == "newslist")
elt.className = "newslist_show";
else
elt.className = "newslist";
}
}
}
</script>
<div class="newslist">
<h1>零点网络</h1>
<p>零点网络是一家科技公司</p>
</div>
20-4 Web应用里的Javascript
在Web文档中使用的Javascript特性在Web应用中都会用到,对于Web应用来说,除了内容、呈现和操作API之外,还依赖Web浏览器环境提供的各种基础的服务;
现代浏览器,已经不仅仅是作为显示文档的工具了,而渐渐变成了一个简易的操作系统;
Web应用就是用Javascript访问浏览器提供的各种服务,这些服务有很多都是在HTML5中定义的,HTML5和相关的标准为Web应用定义了很多其他重要的API,这些API包括以上所说的网络、图像和数据存储,还包含地理位置信息、历史管理和后台线程等,这些都是典型的Web应用;例如XMLHttpRequest对象,其可以发出HTTP请求,可以从服务器端获取新信息,而不用重新载入整个页面,这样的Web应用称为Ajax应用;并且它们可以离线操作,以及保存数据到本地,以便再次访问时进行状态恢复;
Javascript在Web应用里会比在Web文档里显得更加重要;Javascript增强了Web文档,但是设计良好的文档需要 在禁用Javascript后还能继续工作;Web应用本质上就是Javascript程序,只不过使用了Web浏览器提供的服务,如果禁用了Javascript,Web应用就运行不了;
在真实的场景中,并不是完全分离Web文档和Web应用的这两种形式,而是结合了两者的特性;
21 ,浏览器对象模型BOM
BOM(Browser Object Model)浏览器对象模型;其提供了独立于内容而与浏览器窗口进行交互的对象;
// 没有BOM标准:不同的浏览器按照各自的想法实现及扩展BOM,于是,它们之间共有的对象成为了事实上的标准;近年来,W3C为了把浏览器中Javascript最基本的部分标准化,已经将BOM的主要方面纳入了HTML5的规范中。
// BOM由一系列相关的对象构成;DOM中各对象之间是层次关系;window对象是整个BOM的核心,所有对象和集合都以某种方式回接到window对象
21-1 window对象
// 其是BOM的核心对象,也是顶级对象,表示浏览器的一个实例;
// 浏览器窗口对文档提供一个显示的容器,是每一个加载文档的父对象;window对象表示整个浏览器窗口,但不表示其中所包含的内容;可以用于移动或调整浏览器的大小,或者产生其他的影响;
21-2 全局作用域
在浏览器中,window对象具有双重角色,即是通过Javascript访问浏览器的一个接口,又是ES中的Global对象;
既然window是Global对象,所以window对象是所有其他对象的顶级对象,在网页中定义的任何对象、变量和函数,window对象都有权访问;即所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法;因此window前缀可以省略;
var age = 18;
function sayAge(){
alert(this.age);
}
alert(window.age);
sayAge();
window.sayAge();
定义全局变量与在window对象上直接定义属性还是有一点差别:全局变量不能通过delete操作符删除,而直接在window对象上的定义的属性可以,如:
var age = 18;
window.color = "green";
console.log(delete window.age); // false
console.log(delete window.color); // true
console.log(window.age); // 18
console.log(window.color); // undefined
age属性的特性中的[[Configurable]],值为false,因此其不能通过delete删除;
console.log(Object.getOwnPropertyDescriptor(window,"age"));
console.log(Object.getOwnPropertyDescriptor(window,"color"));
尝试访问未声明的变量会抛出错误,但通过查询window对象,可以知道某个可能未声明的变量是否存在,如:
var age = oldAge; // 抛出错误
var age = window.oldAge; // 不会抛出错误,因为这是一次属性查询
21-3 文档元素
如果在HTML文档中用id属性来元素命名,并且如果window对象没有此名字的属性,window对象会赋予一个属性,它的名字是id属性的值,而它们的值指向表示文档元素的HTMLElement对象,这称为全局变量的隐式应用;
在实际场景中,很少使用这种方式来访问HTML元素,它是Web浏览器发展过程中遗留的一个现象,是现代浏览器向后兼容性的考虑,如:
<button id="okay">按钮</button>
<input id="myinput" value="input" />
<div id="mydiv">mydiv</div>
<script>
var ui = ["okay","myinput"];
ui.forEach(function(id){
ui[id] = document.getElementById(id);
});
console.log(ui.okay);
console.log(ui.myinput);
// 定义一个更简单的方法
var $ = function(id){
return document.getElementById(id);
};
$("mydiv").innerHTML = "零点网络";
console.log($("mydiv"));
</script>
21-4 窗口位置
获取窗口(视口)的位置(即相对于屏幕左边和上边的位置);各浏览器都实现了screenLeft和screenTop属性表示窗口的位置;但是之前的firefox并不支持,现代firefox返回-8;只有IE是文档区相对于主显示器屏幕的位置;
Firefox使用了screenX和screenY属性获取窗口位置信息;各浏览器也实现了这两个属性,但并不统一:
chrome与Opera实现了screenLeft、screenTop与screenX、screenY的统一;且最大化时,值为0;Firefox与IE实现了统一,但最大化时,值为-8;且Firefox中的screenLeft、screenTop与这两个属性实现了对应;但IE的screenLeft、screenTop与这两个属性并不对应;screenLeft、screenTop是文档区域的左上角的坐标,screenX、screenY是窗口的左上角坐标;
console.log(window.screenLeft);
console.log(window.screenTop);
console.log(window.screenX);
console.log(window.screenY);
// 为了兼容老的Firefox
var leftPos = (typeof window.screenLeft == "number") ? window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ? window.screenTop : window.screenY;
console.log("leftPos:",leftPos, ",topPos:",topPos);
pageXOffset及pageXOffset:设置或返回当前页面相对于可视区域的左及上的位置;但貌似只有Chrome支持,同时,如果是设置的话,没有效果;
注:目前,无法在跨浏览器的条件下取得窗口左边和上边的精确坐标值;如果在框架中使用,除了IE,其他浏览器的值都与以上统一;
21-5 移动浏览器窗口
- moveBy(dx,dy):相对移动,dx,dy可以为负;
- moveTo(x,y):移动,x,y可以为负;
window.moveTo(50,100);
window.moveBy(100,200);
window.moveBy(-50,0);
注:一般很少用;这两个方法有可能会被浏览器禁用;这两个方法都不适用框架,只能对最外层的window对象使用;
21-6 窗口大小
如果要获取浏览器窗口大小信息,各浏览器并不统一;但各浏览器都已实现了以下四个方法,但返回值并不一定相同:
- innerWidth和innerHeight // 属性获取视口大小(注:包括body的margin);
- outerWidth和outerHeight // 属性获取浏览器窗口大小(无论是从最外层的window对象还是从某个框架访问);
console.log(window.innerWidth);
console.log(window.innerHeight);
console.log(window.outerWidth);
console.log(window.outerHeight);
在各浏览器的实现中,可以通过使用DOM提供的页面视口的相关信息:
document.documentElement.clientWidth和document.documentElement.clientHeight中保存了页面视口的信息;其返回值与innerWidth和innerHeight一致;
在低版本的IE中,必须通过document.body.clientWidth和 document.body.clientHeight属性获取视口信息(实际上是实际内容的尺寸,但不包括border值),但不标准;
同时,document.body.offsetWidth和 document.body.offsetHeight也能获取视口相关信息,同clientWidth和clientHeight类似,只不过其包括border的宽度;
跨浏览器取得页面视口的大小:
var pageWidth = window.innerWidth,
pageHeight = window.innerHeight;
if(typeof pageWidth != "number"){
// 判断页面是否处于标准模式
if(document.compatMode == "CSS1Compat"){
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
}else{
pageWidth = document.body.clientWidth;
pageHeight = document.body.clientHeight;
}
}
console.log(pageWidth);
console.log(pageHeight);
21-7 调整浏览器窗口的大小:
- resizeBy(dw,dh):相对缩放,dw,dh可以为负;
- resizeTo(w,h):缩放到, w,h不能为负;
注:一般很少用;也有可能被禁用,同移动窗口类似
window.resizeTo(400,300);
window.resizeBy(200,100);
21-8 滚动窗口(如果有滚动条)
- scrollBy(x, y):按照指定的像素值相对来滚动的内容(第一个参数是滚动条向右滚动,第二个参数是滚动条向下滚动,方法执行重复执行,值会累加);(可直接在控制台上演示)
- scrollTo(x, y):把内容滚动到指定的坐标;
- scroll(x, y):把内容滚动到指定的坐标;
- scrollX及scrollY:
21-9 对话框:
window对象通过alert()、confirm()和prompt()三个方法可以调用系统对话框向用户显示消息;
系统对话框与在浏览器中显示的网页没有关系,也不包括HTML;它们的外观由操作系统或浏览器设置决定的,而不是由CSS决定的;此外,通过这几个方法打开的对话框都是同步和模态的,也就是说,显示这些对话框的时候代码会停止执行,而关掉这些对话框后代码又会恢复执行;
一般来说,不会使用,都是自己实现一个效果;
警告对话框window.alert(string):
警告对话框是一个带感叹图标的小窗口,通常用来输出一些简单的文本信息;该方法接受一个字符串并将其显示给用户,并包含一个OK或确定的按钮等待用户关闭对话框;
通过alert()生成的警告对话框,用户是无法控制的,比如,显示的消息内容,用户只能看完消息后关闭对话框;
确认对话框window.confirm(string):
确认对话框也是向用户显示消息的对话框,但与警告对话框不同的是,其具有OK与Cancle按钮,根据用户的选择,该方法返回不同的值(true或false);程序可以根据不同的值予以不同的响应,实现互动的效果;通常放在网页中,对用户进行询问并根据其选择而做不同的控制;
if(window.confirm("确定删除吗?")){
alert("已经删除");
}else{
alert("未删除");
}
输入对话框window.prompt():
用于提示用户输入的对话框;
语法: window.prompt(提示信息,默认值);// 要显示的文本提示和文本输入域的默认值,该默认值可以是一个空字符串;
prompt("请输入你的名字","王唯");
如果用户单击了OK,则prompt()返回文本域中的值,如果单击了Cancel或使用其他方式关闭对话框,则该方法返回null,如:
var result = prompt("请输入密码:","");
if(result == "8888"){
alert("登录成功");
}
var result = prompt("请输入你的名字","王唯");
if(result !== null){
document.write("欢迎你:" + result);
}else{
alert("你没有输入任何内容");
}
综上所述,这些系统对话框很适合向用户显示消息并由用户作出决定;由于不涉及HTML、CSS或JS,因此它们是增强Web应用程序的一种便捷方式;
do{
var name = prompt("输入你的名字:");
var correct = confirm("你输入的是:" + name + ".\n" + "你可以单击确定或取消");
}while(!correct)
alert("你好," + name);
在弹出对话框时,还有一个特性:如果当前脚本在执行过程中会打开两个或多个对话框,那么从第二个对话框开始,每个对话框中都会显示一个复选框,以便用户阻止后续的对话框显示,除非用户刷新页面;后续被阻止的对话框包括警告框、确认框和显示输入框;
该特性最初是由Chrome实现了,后续其他浏览器也实现该特性;其工作原理就是使用了一个叫对话框计数器,可以跟踪对话框;但是浏览器没有就对话框是否显示向开发人员提供任何信息;
在实际的场景中,这三个方法是很少用的,因为它们显示的文本是纯文本,不是HTML格式的文本,只能使用空格、换行符和各种标点符号,所以往往满足不了页面设计需要,并且有可能会破坏用户的浏览体验;常见的就是使用alert()方法进行调试,用来查看某个变量的输出结果;
这三个方法都会产生阻塞,也就是说,在用户关掉它们之前,它们不会返回,后面的代码不会执行;如果当前载入文档,也会停止载入,直到用户要求的输入进行响应为止;
可以自定义一个对话框:
<style>
#alert_box{
position: absolute; display: none; width: 400px; height:300px; border-radius: 3px;
box-shadow: 0 0 5px rgba(0, 0, 0, .5);
}
#alert_box h1{
margin:0; padding: 0; font-size: 1.5em; line-height: 60px;
height: 60px;
text-align: center; background-color: rgba(255,255,255,1);
}
#alert_box div{
height: 240px;
padding: 1em; line-height: 1.8em; background-color: rgba(255,255,255,.8);
}
#alert_box span{
position: absolute; width: 30px; height: 30px;
top:-15px; right:-15px; background-color:#000; border-radius: 50%;;
}
</style>
<script>
window.alert = function(title,info){
var box = document.createElement("div");
box.id = "alert_box";
box.style.left = ((window.innerWidth - 400) / 2) + "px";
box.style.top = ((window.innerHeight - 300) / 2) + "px";
var h1 = document.createElement("h1");
h1.innerText = title;
box.appendChild(h1);
var innerBox = document.createElement("div");
innerBox.innerHTML = info;
box.appendChild(innerBox);
var closeSpan = document.createElement('span');
box.appendChild(closeSpan);
closeSpan.addEventListener("click",function(e){
document.body.removeChild(box);
},false);
box.style.display = "block";
document.body.appendChild(box);
};
window.alert("零点网络","哪些是这样的?");
</script>
Javascript还有两个工具性的对话框,即查找window.find()和打印window.print();
这两个对话框都是异步显示的,能够将控制权立即交还给脚本;其与浏览器菜单中的查找和打印命令是相同的;
这两个方法同样不会就用户对对话框中的操作给出任何信息,因此它们的用处有限;另外,既然这两个对话框是异步显示的,对话框计数器就不会将它们计算在内,所以它们也不会受用户禁用后续对话框显示的影响;
21-10 状态栏
// 浏览器的状态栏通常位于窗口的底部,用于显示一些任务状态信息等。在通常情况下,状态显示当前浏览器的工作状态或用户交互提示信息; 具有status和defaultStatus属性;
// 默认状态栏信息:
// defaultStatus属性可以用来设置在状态栏中的默认文本,是一个可读写的字符串;
// 状态栏瞬间信息:
// status属性,在默认情况下,将鼠标放在一个超链接上时,状态栏会显示该超链接的URL,此时的状态栏信息就是瞬间信息;当鼠标离开超链接时,状态栏就会显示默认的状态栏信息,瞬间信息消失 。
// 浏览器已经关闭了状态栏的功能;这是出于安全的考虑,防止隐藏了超链接真正目的的钓鱼攻击
22,计时器setTimeout和setInterval
22-1 计时器
Javascript是单线程语言,但它允许通过设置超时和间歇时间值来调度代码在特定的时刻执行;其是通过setTimeout()和setInterval()两个window对象的全局函数实现的,用来注册在指定的时间之后单次或重复调用的函数;
22-1-1 setTimeout()
延迟代码执行(也叫超时调用):用来实现一段代码在指定的毫秒之后运行;
语法:window.setTimeout(code,delay),code要执行的代码,可以是一个包含Javascript的代码字符串,也可以是一个函数,delay等待的毫秒数;
// 不建议传递字符串
setTimeout("alert('zeronetwork')",3000);
// 推荐的使用方式
setTimeout(function(){
alert('zeronetwork');
},3000);
// 推荐的使用方式
setTimeout(show,3000);
function show(){
alert('zeronetwork');
}
因为历史原因,第一个参数可以传递字符串,但有可能导致性能损失,因为这个字符串会在指定的超时时间之后进行求值,相当于执行eval(),因此不推荐使用字符串的形式;
// 能达到无限循环的目的
var n = 0;
function fun(){
n++;
console.log(n);
setTimeout(fun, 1000);
}
fun(); // 直接执行
setTimeout(fun,3000);
第二个参数是一个表示等待多长时间的毫秒数,但经过该时间后指定的代码并不一定会执行;Javascript是一个单线程的解释器,因此一定时间内只能执行一段代码;为了控制要执行的代码,就有一个Javascript任务队列,这些任务会执照将它们添加到队列的顺序执行;这个参数实际上是告诉Javascript再过多长时间把当前任务添加到队列中,如果队列是空的,那么添加的代码会立即执行,如果队列不是空的,那么它就要等前面的代码执行完毕后再执行;
另外,如果该参数为0,也并不一定会立即执行,因为也需要将它放到队列中,等待前面的任务全部执行完后,才会“立即”执行;
setTimeout()方法会返回一个数字ID,ID本质上是要延迟进程的ID,是计划执行代码的唯一标识符;可以使用clearTimeout()方法,调用此ID,达到取消超时调用的目的;
var timeoutid = setTimeout(function(){
alert("zeronetwork");
},3000);
console.log(timeoutid);
clearTimeout(timeoutid);
只要是在指定的时间尚未过去之前调用clearTimeout(),就可以完全取消超时调用;
<input type="button" value="开始" onclick="showClock()" />
<input type="button" value="取消" onclick="window.clearTimeout(ident)" />
<div id="showtime">time</div>
<script>
function showClock(){
var d = new Date();
var showtime = document.getElementById("showtime");
showtime.innerHTML = d.toLocaleString();
// ident = setTimeout(showClock(), 1000);
ident = setTimeout("showClock()", 1000);
}
</script>
示例:可以利用 clearTimeout() 方法在特定条件下清除延迟处理代码。例如,当鼠标指针移过某个元素,停留半秒钟之后才会弹出提示信息,一旦鼠标指针移出当前元素,就立即清除前面定义的延迟处理函数,避免干扰;
<h1>零点网络</h1>
<div>零点教育是从事IT教育</div>
<p>主讲:零点网络</p>
<script>
var o = document.getElementsByTagName('body')[0].childNodes;
for(var i=0; i<o.length; i++){
o[i].onmouseover = function(i){
return function(){
f(o[i]);
}
}(i);
o[i].onmouseout = function(i){
return function(){
clearTimeout(o[i].out);
}
}(i);
}
function f(o){
o.out = setTimeout(function(){
console.log(o.tagName + ":" + o.innerText);
},500);
}
</script>
除前两个参数之外,HTML5规范还允许setTimeout()传入额外的参数,并在调用函数时把这些参数传递过去;
setTimeout(function(str,age){
alert(str + "age:" + age);
},3000,"wangwei",18);
22-1-2 setInterval()
代码延迟执行机制在执行一次后就失效,而在应用中,有时希望某个程序能反复执行,比如说倒计时等,需要每秒执行一次;为此可以使用window方法的setInterval方法,其会按照指定的时间间隔重复执行代码,直到取消或页面被卸载;其与setTimeout()类似,参数也一致;
// 不建议使用字符串
setInterval("console.log('zero')", 3000);
// 推荐的方式
setInterval(function(){
console.log('zero');
},3000);
function timer(){
var d = new Date();
document.getElementById("result").innerText = d.toLocaleTimeString();
}
setInterval(timer,1000);
// 输出的时间并不精确,并不是整1000毫秒
var firstTime = new Date().getTime();
setInterval(function(){
var lastTime = new Date().getTime();
console.log(lastTime - firstTime);
// alert("ok"); // 会暂停
firstTime = lastTime;
},1000);
同setTimeout()一样,setInterval()也支持第三个参数;
取消间隔性执行代码:
使用setInterval()方法同样会返回一个间隔调用ID,该ID可用于在将来某个时间取消间隔调用;可以使用clearInterval方法移除间隔调用,其接收一个计时器ID作为参数;
// 如果不使用它的返回值,可以直接使用数字1、2...
var i = 0;
setInterval(function(){
console.log(i++);
if(i>10)
clearInterval(1);
},1000);
取消间隔调用的重要性要远远高于取消超时调用,因为在不取消的情况下,间隔调用将会一直执行到页面卸载;
var num = 0, max = 10;
var intervalId = null;
function incNum(){
num++;
console.log(num);
// 如果执行次数达到了max设定的值,则取消后续的调用
if(num == max){
clearInterval(intervalId);
alert("结束");
}
}
intervalId = setInterval(incNum, 1000);
// 另外
var mInput = document.getElementsByTagName('input')[0];
var sInput = document.getElementsByTagName('input')[1];
var m = 4,s = 52;
var timer = setInterval(function(){
s++;
if(s == 60){
s = 0;
m++;
}
sInput.value = s;
mInput.value = m;
if(m == 5)
clearInterval(timer);
},1000);
setTimeout()与setInterval()同时使用时,其返回的id也会按顺序返回;
在某些时候 setTimeout()与 setInterval() 可以实现同样的效果;
var num = 0, max = 10;
function incNum(){
num++;
console.log(num);
// 如果执行次数达到了max设定的值,则取消后续的调用
if(num < max){
setTimeout(incNum, 1000)
}else{
alert("结束");
}
}
setTimeout(incNum, 1000);
在使用超时调用时,没有必要使用超时调用ID,因为每次执行代码之后,如果不再设置另一次超时调用,调用就会自动停止;
一般认为,使用延迟代码来模拟时间间隔是一种最佳方式;在开发环境中,很少使用时间间隔,因为时间间隔可能会在前一个间隔调用结束之前启动,而延迟代码完全可以避免这一点;
<div id="loadBar" style="border: red 1px solid;"></div>
<script>
var num = 0;
var colors = ['#494949','#646464','#747474','#888888','#969696','#A8A8A8','#B6B6B6','#C6C6C6','#D7D7D7','#E1E1E1','#F0F0F0','#F9F9F9'];
function loading(){
num++;
var loadBar = document.getElementById("loadBar");
loadBar.style.color = colors[num-1];
loadBar.innerHTML = loadBar.innerHTML + "■";
if(num < 12){
setTimeout(loading, 1000);
}else{
loadBar.style.display = "none";
window.open("https://www.zeronetwork.cn/","new");
}
}
window.onload = loading;
</script>
/*
定时器应用函数 invoke
如果只传递f,start,则使用setTimeout
如果没有传递end,则永久循环执行f,否则在end后停止
*/
function invoke(f, start, interval, end){
if(!start) start = 0; // 默认设置为0毫秒
if(arguments.length <= 2) // 单次调用模式
setTimeout(f, start);
else{ // 多次调用模式
setTimeout(repeat, start); // 若干秒后调用repeat()
function repeat(){
var h = setInterval(f, interval); // 循环调用f()
// 在end毫秒后停止调用,前提是end已经定义了
if(end){
setTimeout(function(){
clearInterval(h);
}, end);
}
}
}
}
invoke(function(){
console.log("wangwei");
},1000,2000,5000);
23, location、history对象
23-1 location对象
// location对象,是BOM最有用的对象之一了,是window的属性(子对象),它提供了与当前窗口中加载文档的URL,还提供一些导航及载入文档的方法;
// location对象很特别,即是window对象的属性,也是document对象的属性,即window.location与document.location引用的是同一个对象;
location对象的特点是,它不仅保存着当前文档的信息,它还将URL解析为独立的片段,让开发人员可以通过不同的属性访问这些片段;
23-1-1 属性
- hash:// 返回URL中的hash,即井号 (#)后跟的多个字符,即URL中的锚,如果没有,则返回空字符串;如:#name;
- host:// 返回主机名和端口号(如果有),如:www.zeronetwork.cn:80;
- hostname:// 返回当前主机名不包括端口号,如:www.zeronetwork.cn;
- origin:// 只读,返回当前协议、主机名和端品号,如:https://www.zeroentwork.cn:80;
- href:// 返回当前加载页面的完整URL,location对象的toString()也返回这个值,如:https://www.zeronetwork.cn;
- pathname:// 返回当前 URL 的路径部分,即目录和文件名,如:/edu/index.html;
- port:// 返回当前 URL中的端口号,如果没有,则返回空字符串,如:8080;
- protocol:// 返回当前 URL 的协议,通常是http或https;
- search:// 返回从问号 (?) 开始的查询字符串,如:?q=wangwei
23-1-2 方法
- assign():// 加载新的文档;
- reload():// 重新加载当前文档;
- replace():// 用新的文档替换当前文档;
23-1-3 解析URL
location对象的href属性是一个字符串,包含URL的完整文本;location对象的toString()方法返回href属性的值,因此在会隐匿调用toString()的情况下,可以使用location代替location.href;
console.log(location);
console.log(location.href);
console.log(location.toString());
console.log(location + ""); // 字符串
// 可以直接赋值,即为location.href = url
location = "https://www.zeronetwork.cn";
<!-- 页面跳转 -->
<p>页面会在<span id="result"></span>秒后跳转到:https://www.zeronetwork.cn</p>
<script>
var n = 5;
result.innerText = n;
setInterval(function(){
n--;
if(n==0)
location.href = "https://www.zeronetwork.cn/";
else
result.innerText = n;
},1000);
</script>
其他的属性都表示URL的各个部分,它们被称为“URL分解”属性,同时被Link对象(html中的<a>和<area>元素)支持;这些属性都是 可写的;
// 获取页面名
var pathname = location.pathname;
var pagename = pathname.substring(pathname.lastIndexOf('/')+1);
console.log(pagename);
23-1-4 查询字符串参数
获取参数可以通过Location对象的search属性,获得从URL中传递过来的参数和参数值,但不能逐个访问参数,因此需要单独解析查询字符,用以处理需要获取的参数和参数值;
function getQueryString(){
// 取得查询字符串并去掉开头的问
var qs = location.search.length > 0 ? location.search.substring(1) : "";
// 保存数据的对象
var args = {};
// 取得每一项
var items = qs.length ? qs.split("&") : [];
var item = null, name=null, value = null;
var i=0, len=items.length;
// 逐个将每一项添加到args对象中
for(i=0; i<len; i++){
item = items[i].split("=");
name = decodeURIComponent(item[0]);
value = decodeURIComponent(item[1]);
if(name.length){
args[name] = value;
}
}
return args;
}
// 应用,假定为:?q=zeronetwork&num=100
var args = getQueryString();
alert(args["q"]); //zeonetwork
alert(args["num"]); // 100
23-1-5 加载新文档
在实际场景中,时常会用到加载一个新的网页的情况;此时可以用Location对象的href属性就可以轻松完成这一功能,该属性返回值为当前文档的URL,如果将该属性值设置为新的URL,那么浏览器会自动加载该URL的内容,从而达到加载一个新的网页的目的;
console.log(location.href);
location.href = "https://www.zeronetwork.cn/";
// 或者直接为location赋新的URL值
window.location = "https://www.zeronetwork.cn/";
如此,就能立即打开新的URL并在浏览器的历史记录中生成一条记录;
修改location对象的其他属性也可以改变当前加载的页面,如:
// 注意先后顺序
location.pathname = "/edu/index.html";
location.hash = "#selection1";
location.search = "?q=wangwei";
location.port = "8080";
location.hostname = "www.zeronetwork.cn";
function gotoUrl(){
window.location.href = "http://www.zeronetwork.cn";
}
// 传递参数
function gotoUrl(url,catalogid){
var url = url + "?catalogid=" + catalogid;
window.location = url;
}
function gotoUrl(url,catalogid){
if(catalogid <= 0)
location = url;
else
location = url + "?catalogid=" + catalogid;
}
每次修改location的属性(hash除外),页面都会以新的URL重新加载;
修改hash有可能不会在历史记录中生成一条记录;
<p id="info"></p>
<script>
var s = 5;
var info = document.getElementById("info");
function go(){
if(s==0)
window.location.href = "https://www.zeronetwrok.cn/";
else
info.innerHTML = "浏览器将在" + s + "后跳转";
s--;
}
setInterval(go, 1000);
</script>
Document对象也有一个URL属性,是文档首次载入后保存该文档的URL的字符串;
console.log(location.href == document.URL);
23-1-6 装载新文档与重新装入当前文档
文档的装载在应用中也是比较常见的,有三个方法:assign、replace和reload;
// 使用按钮演示
var mybtn = document.getElementById("mybtn");
mybtn.addEventListener("click",function(){
// ...
});
23-1-7 assign()方法
使窗口载入并显示指定的URL中的文档;
// window.location=URL与location.href=URL本质上都是调用了assign()方法;三者是等同的用法;
// location.assign("https://www.zeronetwork.cn/");
23-1-8 replace()方法
以上的方式修改URL后,在浏览器的历史记录中会生成一条新记录,因此用户通过单击“后退”按钮,都会导航到前一个页面;要禁用这种行为,可以使用replace()方法;该方法只接受一个URL参数,并不会在历史记录中生成新记录;
location.replace("https://www.zeronetwork.cn/");
// 如果浏览器不支持XMLHttpRequest对象,则重定向一个不需要Ajax的页面
if(!XMLHttpRequest) location.replace("noajax.html");
注:relace()的URL参数可以是绝对或相对的URL;
23-1-9 reload()方法
该方法用于根据浏览器reload按钮定义的策略重新加载当前窗口的文档;
reload()方法直接有可能从缓存中重新加载,如果加入参数true,会从服务器重新加载;
window.location=URL与location.href=URL本质上都是调用了assign()方法;三者是等同的用法;
location.assign("https://www.zeronetwork.cn/");
位于reload()方法调用之后的代码可能会也可能不会执行,这要取决于网络延迟或系统资源等因素,所以,reload()方法最好放到代码的最后一行;
23-2 history历史对象
history浏览历史对象并不常用,是window对象的属性,其保存着用户浏览的历史记录,每个窗口,标签页以及每个框架,都有自己的history对象与特定的window对象关联;
当页面的URL改变时,就会生成一条历史记录;
但通过使用history对象可以获知浏览器窗口近来访问过的网页个数,还可以实现从一个页面跳到另一个页面,在实际应用中,如涉及到页面的跳转问题,可以用这个对象来解决;
23-2-1 go(n)方法
表示前进或后退; 参数是一个整数值,为正则前进,为负为后退; 如:
history.go(-1); // 后退一页
history.go(1); // 前进一页
history.go(2); // 前进2页
也可以传历史记录中的字符串,如:
history.go("baidu.com");
此时浏览器会跳转到历史记录中包含该字符串的第一个记录,有可能后退,也可能前进,具体要看哪个位置最近;如果历史记录中不包含该字符串,此时该方法什么也不做;
23-2-2 back()和forward()方法
前进和后退,是go()的简写方法,如:
history.back(); // 后退一页
history.forward(); // 前进一页
也可以创建自定义的前进和后退功能;
23-2-3 length属性
获取历史记录中的数量;对于加载到窗口、标签页或框架中的第一个页面而言,其值为0;
alert(history.length);
// 判断用户是不是打开窗口后第一个加载此页面
if(history.length == 0){
alert("第一个打开的页面");
}
如果窗口包含多个子窗口,子窗口的浏览历史会按时间顺序穿插在主窗口的历史中;这意味着在主窗口调用history.back()等方法可能会导致其中一个子窗口跳转,但主窗口保留当前状态不变;
23-2-4 history.pushState()和history.replaceState()
HTML5为history对象添加了两个新方法:history.pushState()和history.replaceState(),用来在浏览历史中添加和修改记录;
state属性用来保存记录对象,而popstate事件用来监听history对象的变化;
history.pushState()方法向浏览器历史添加了一个状态;其可以传三个参数:一个状态对象、一个标题(现在被忽略了)以及一个可选的URL地址,如:history.pushState(state, title, url);
state:状态对象是一个由pushState()方法创建的、与历史纪录相关的javascript对象;如果不需要这个对象,此处可以填null;
title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null;
URL:这个参数提供了新历史纪录的地址;新URL必须和当前URL在同一个域,否则,pushState()将丢出异常。这个参数可选,如果它没有被特别标注,会被设置为文档的当前URL;
var stateObj = {foo:'bar'}
history.pushState(stateObj,"new page 1", 'one.html');
pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏的显示地址发生变化;
如果pushState的url参数,设置了一个新的锚点值(即hash),并不会触发hashchange事件,即使新的URL和旧的只在hash上有区别;
如果设置了一个跨域网址,则会报错;这是由于同源策略限制;
history.replaceState方法的参数与pushState方法一模一样,不同之处在于replaceState()方法会修改当前历史记录条目而并非创建新的条目;
history.pushState({page:1},"title 1", '?page=1');
history.pushState({page:2},"title 2", '?page=2');
history.replaceState({page:3},"title 3", '?page=3');
// 显示http://127.0.0.1:5500/sub/client.html?page=1
history.back();
// 显示http://127.0.0.1:5500/sub/client.html
history.back();
// 显示http://127.0.0.1:5500/sub/client.html?page=3
history.go(2);
history.state属性返回当前页面的state对象
history.pushState({page:3},'title 3','?page=3');
console.log(history.state);
24,navigator、screen对象
24-1 navigator对象
通过navigator对象,可以获取当前使用的是上面浏览器,浏览器的版本号,浏览器是否支持Java,当前浏览器有哪些插件(Plug-ins)可用等信息,根据这些信息可以定制一些自定义行为或进行相应的处理;
navigator对象是window对象的属性;
最早由Navigator2.0引入的navigator对象,现已成为识别客户端浏览器的事实标准;一般用来检测浏览器及操作系统;
其他浏览器也通过其他方式提供了相同或相似的信息,如,IE中的window.clientInformation和Opera的window.opera,但navigator对象是所有支持javascript的浏览器所共有的;但不同浏览器中的navigator对象都有一些不同的属性;
var sum=0;
for(var p in navigator){
sum++;
document.write(p + "<br/>");
}
document.write(sum);
24-1-1 属性或方法
- **appCodeName:**浏览器的名称;通常是Mozilla,即使在非Mozilla也是如此
- **appMinorVersion:**次版本信息
- **appName:**完整的浏览器名称;
- **appVersion:**浏览器的版本;一般不与实际的浏览器版本对应;
- **builddID:**浏览器编译版本
- **cookieEnabled:**表示cookie是否启用
- **cpuClass:**客户端计算机中使用的cpu类型(x86、68K、Alpha、PPC或Other)
- **javaEnabled():**表示当前浏览器是否启用了java
- **language:**浏览器的主语言
- **mimeTypes:**在浏览器中注册的MIME类型数组
- **online:**表示浏览器是否连接到了因特网
- **opsProfile:**已不使用
- **oscpu:**客户端计算机的操作系统或使用的CPU
- **platform:**浏览器所在的系统平台
- **plugins:**浏览器中安装的插件信息的数组
- **preference():**设置用户的首选项
- **product:**产品名称,如Gecko
- **productSub:**关于产品的次要信息,如Gecko的版本
- **register-ContentHandler():**已经废弃;针对特定的MIME类型将一个站点注册为处理程序
- **register-ProtocolHandler():**针对特定的协议将一个站点注册为处理程序
- **securityPolicy:**已经废弃;安全策略的名称
- **systemLanguage:**操作系统的名称
- **taintEnabled():**已经废弃;表示是否允许变量被修改
- **userAgent:**浏览器的用户代理字符串
- **userLanguage:**操作系统的默认语言
- **userProfile:**借以访问用户个人信息的对象
- **vendor:**浏览器的品牌
- **verdorSub:**有关供应商的次要信息,这些属性通常用于检测显示网页的浏览器类型;
浏览器嗅探在有些时候还是必要的,比如,当需要解决存在于某个特定的浏览器的特定版本中的特殊的bug时,此时就可以使用navigator某些属性来获取当前浏览器的版本信息,比如:
appName:Web浏览器的全称;在低版本的IE中,返回“Microsoft Internet Explorer”;在Firefox等其它浏览器中,返回“Netscape”;
appVersion:此属性通常以数字开始,并跟着包含浏览器厂商和版本信息字符串;字符串前面的数字通常是4.0或5.0,表示它是第4或第5代兼容的浏览器;appVersion字符串没有标准的格式,所以,没有办法直接用它来判断浏览器的类型;
userAgent:浏览器在HTTP头部中发送的字符串,该属性通常包含appVersion中的所有信息,并且常常也可能包含其他的细节;它也没有标准格式;但由于它包含浏览器的绝大部分信息,因此浏览器嗅探通常使用它;
platform:操作系统平台的字符串;
24-1-2 检测浏览器内核及版本号
// 检测浏览器内核及版本号
var brower = (function(){
var s = navigator.userAgent.toLowerCase();
var match = /(webkit)[\/]([\w.]+)/.exec(s) ||
/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(s) ||
/(msie) ([\w.]+)/.exec(s) ||
!/compatible/.test(s) &&
/(mozilla)(?:.*? rv:([\w.]+))?/.exec(s) ||
[];
return {name: match[1] || "", version: match[2] || "0"};
}());
console.log(navigator.userAgent);
console.log(brower);
24-1-3 检测操作系统
所有 Windows 版本的操作系统都会包含 “Win”字符串,所有 Macintosh 版本的操作系统都会包含“Mac”字符串,所有 Unix 版本的操作系统都会包含有“X11”,而 Linux 操作系统会同时包含“X11”和“Linux”;
["Win","Mac","X11","Linux"].forEach(function(t){
(t == "X11") ? t = "Unix" : t; // 处理Unix系统
// 为navigator对象扩展专用系统检测方法
navigator["is" + t] = function(){
return navigator.userAgent.indexOf(t) != -1;
};
});
console.log(navigator.isWin());
console.log(navigator.isMac());
console.log(navigator.isLinux());
console.log(navigator.isUnix());
24-1-4 特征检测法
特征检测法就是根据浏览器是否支持特定的功能来决定相应操作的方式;这是一种非精确判断法,但却是最安全的检测方法,因为准确检测浏览器的类型和型号是一件很困难的事情,而且很容易存在误差;如果不关心浏览器的身份,仅仅在意浏览器的执行能力,那么使用特征检测法就完全可以满足需要;
if(document.getElementsByName)
var a = document.getElementsByName("test");
else if(document.getElementsByTagName)
var a = document.getElementsByTagName("a");
console.log(a);
function getXMLHttpRequest(url){
var xhr = null;
if(window.XMLHttpRequest)
xhr = new XMLHttpRequest();
else
xhr = new ActiveXObject("Microsoft.XMLHTTP");
if(xhr != null){
// 开始请求提交
alert("提交成功");
}else{
alert("你的浏览器不支持XMLHTTP");
}
}
getXMLHttpRequest();
当使用一个对象、方法或属性时,先判断它是否存在;如果存在,则说明浏览器支持该对象、方法或属性,那么就可以放心使用;这是目前最主流的一种检测方式;
除了浏览器厂商和版本信息的属性之外,navigator对象还包含一些杂项的属性和方法,如以下这些属性,已经得到广泛的应用但未标准化;
- onLine:表示浏览器当前是否连接到网络;应用程序可能希望在离线状态下把状态保存到本地;
- geolocation:用于确定用户地理位置信息的API;
- javaEnabled():一个非标准的方法,当浏览可以运行Java小程序时返回ture;
- cookieEnable():非标准的方法,如果浏览器可以保存永久的cookie时,返回true,其它返回false;
24-1-5 检测插件
检测浏览器中是否安装了特定的插件是一种最常见的检测例程;
对于非低版本的IE可以使用plugins数组来达到目标;该数组中的每一项包含以下属性:
- name:插件的名字;
- description:插件的描述;
- filename:插件的文件名;
- length:插件所要处理的MIME类型数量;
一般来说,name属性中会包含检测插件必需的所有信息,但有时也完全如此;在检测插件时,需要迭代plugins数组中的每个插件,如:
// 检测非低版本的IE插件
function hasPlugin(name){
name = name.toLowerCase();
for(var i=0,len=navigator.plugins.length; i<len; i++){
if(navigator.plugins[i].name.toLowerCase().indexOf(name) > -1)
return true;
}
return false;
}
// 检测Flash
alert(hasPlugin("Flash"));
// 检测QuickTime
alert(hasPlugin("QuickTime"));
// 检测Java
alert(hasPlugin("Java"));
每个插件本身也是一个MimeType对象的数组,这些对象可以通过方括号语法来访问,共有四个属性:MIME类型描述description、回指插件对象enabledPlugin、表示与MIME类型对应的文件扩展名的字符串suffixes、表示完整MIME类型字符串type;
检测IE中的插件比较麻烦,因为IE不支持Netscape式的插件;在IE中检测插件的唯一方式就是使用专有的ActiveXObject类型,并尝试创建一个特定插件的实例;IE是以COM对象的方式实现插件的,而COM对象使用唯一标识符来标识;因此,要想检查特定的插件,就必须知道其COM标识符,如:Flash的标识符是: ShockwaveFlash.ShockwaveFlash;
// 检测IE中的插件
function hasIEPlugin(name){
try{
new ActiveXObject(name);
return true;
}catch(e){
return false;
}
}
// 检测Flash
alert(hasIEPlugin("ShockwaveFlash.ShockwaveFlash"));
// 检测QuickTime
alert(hasIEPlugin("QuickTime.QuickTime"));
使用了try、catch,因为创建未知的COM对象会导致抛出错误;
两种检测方法差别太大,典型的作法是针对每个插件分别创建测试函数;如:
// 检测所有浏览器中的Flash
function hasFlash(){
var result = hasPlugin("Flash");
if(!result)
result = hasIEPlugin("ShockwaveFlash.ShockwaveFlash");
return result;
}
// 检测所有浏览器中的QuickTime
function hasQuickTime(){
var result = hasPlugin("QuickTime");
if(!result)
result = hasIEPlugin("QuickTime.QuickTime");
return result
}
alert(hasFlash()); // 检测Flash
alert(hasQuickTime()); // 检测QuickTime;
plugins集合中有个refresh() 方法,用于刷新plugins以反映最新安装的插件,这个方法接收一个参数:表示是否应该重新加载页面的一个布尔值;如果为true,重新加载包含插件的所有页面,否则,只更新plugins集合,不重新加载页面;
24-1-6 注册处理程序
registerProtocolHandler()方法接收三个参数:要处理的协议(mailto或ftp)、处理该协议的页面的URL和应用程序名称;如:将一个应用程序注册为默认的邮件客户端:
navigator.registerProtocolHandler("mailto","http://127.0.0.1:5500/?cmd=%s",
"Some Mail Client"); // %s表示原始的请示
IE不支持,并且在生产环境中,几乎没有什么用途;
24-2 screen对象
screen对象提供了获取显示器信息的功能,显示器信息的主要用途是确定网页在客户机是所能达到的最大显示空间;此对象的用处不大,其只是用来获取客户端的能力,其中包括显示器的信息,如宽和高或颜色数量的信息;每个浏览器中的screen对象都包含着各不相同的属性;
24-2-1 属性
- availHeight 和 availWidth:只读,屏幕减去系统部件(比如任务栏)的高度和屏幕减去系统部件的宽度;即实际可用的大小;
- colorDepth:只读,返回颜色位数,如24,多数为32位
- pixelDepth:只读,屏幕的位深(FF)
- width 和 height:屏幕的宽度和屏幕的高度
- left 和 top:当前屏幕距左边的距离和距顶边的距离(FF支持)
- availLeft 和 availTop:只读,未被系统占用的最左侧的和最上方的像素值 (FF)
- bufferDepth:读、写用于呈现屏外位图的位数(IE)
- deviceXDPI与deviceYDPI:只读,实际的水平与垂直DPI(IE)
- logicalXDPI与logicalYDPI:只读,屏幕逻辑的水平与垂直DPI (IE)
- fontSmoothingEnabled:只读,是否启用字体平滑(IE)
- updateInterval:读、写,以毫秒表示的屏幕刷新时间间隔(IE)
这些信息经常出现在测定客户端能力的站点跟踪工具中,但通常不会用于影响功能;不过,有时候也可能会用到其中的信息来调整浏览器窗口的大小,使其占据屏幕的可用空间,如:
// 网页全屏,非IE会禁用调整窗口的能力,因此是无效的
window.moveTo(0,0);
window.resizeTo(screen.availWidth, screen.availHeight);
// 弹出窗口居中
function center(url){
var w = screen.availWidth / 2;
var h = screen.availHeight / 2;
// 计算居中显示时左侧坐标
var l = (screen.availWidth - w) / 2;
// 计算居中显示时顶部坐标
var t = (screen.availHeight - h) / 2;
// 计算坐标参数字符串
var p = "top=" + t + ",left=" + l + ",width=" + w + ",height=" + h;
var win = window.open(url, "newin", p);
win.focus();
}
center("https://www.zeronetwork.cn");
涉及移动设备的屏幕大小时,情况有所不同;运行iOS的设备始终会返回设备竖着方向的尺寸768X1024,而Android会相应调用screen.width和screen.height的值;
// 网页开屏
var x=0, y = window.screen.availHeight, dx=5;
var newWin, intervalID;
function showPage(){
if(x < screen.availWidth)
x += dx;
else
clearInterval(intervalID);
newWin.resizeTo(x, y);
}
function showWin(){
newWin = window.open("","newWin","menubar=no,toolbar=no");
newWin.moveTo(0,0);
newWin.resizeTo(x,y);
intervalID = window.setInterval(showPage, 100);
}
showWin();
// 网页布局
function loadCSS(){
var iWidth = screen.availWidth;
var sCSSUrl;
switch(iWidth){
case 1024:
sCSSUrl = "style1.css";
break;
case 1280:
sCSSUrl = "style2.css";
break;
default:
sCSSUrl = "default.css";
break;
}
var oCSS = document.createElement("link");
oCSS.setAttribute("rel","stylesheet");
oCSS.setAttribute("type","text/css");
oCSS.setAttribute("href",sCSSUrl);
document.getElementsByTagName("head")[0].appendChild(oCSS);
}
window.onload = loadCSS;
24-2-2 错误处理
window对象的onerror属性是一个事件处理程序,当未捕获的异常传递到调用栈上时就会调用它,并把错误的消息输出到浏览器的Javascript控制台上;
window.onerror的第一个参数是描述错误的一条消息,第二个参数是一个字符串,它存放引发错误的Javascript代码所在的文档的URL,第三个参数是文档中发生错误的行数;
onerror处理程序也有一个返回值,如果返回false,它通知浏览器事件处理程序已经处理了错误,不需要其他操作(换句话说,浏览器不应该显示它自己的错误消息;
onerror处理程序是早期的JavaScript的产物,那时语言核心不包括try/catch异常处理语句;现在实际开发中,虽然很少使用它,但有些项目还在使用它,如:
// 在一个对话中弹出错误消息,但不超过三次
window.onerror = function(msg,url,line){
if(onerror.num++ < onerror.max){
alert("ERROR: " + msg + "\nurl: " + url + "\nline: " + line);
return true;
}
}
onerror.max = 3;
onerror.num = 0;
function show(a,b){
return sum(a,b);
}
console.log(show(3,0));
25,面向对象
25-1 ES6 中的类和对象
25-1-1 类的创建
// 语法
class name {
// class body
}
// 创建实例
var xx = new name();
25-1-2 类 constructor 构造函数
constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new 命令生成对象实例时,自动调用该方法。如果没有显示定义, 类内部会自动给我们创建一个constructor()
class Person {
constructor(name,age) { // constructor 构造方法或者构造函数
this.name = name;
this.age = age;
}
}
var ldh = new Person('刘德华', 18);
console.log(ldh.name)
25-1-3 给类添加方法
class Person {
constructor(name,age) { // constructor 构造器或者构造函数
this.name = name;
this.age = age;
}
say() {
console.log(this.name + '你好');
}
}
var ldh = new Person('刘德华', 18);
ldh.say()
// 注意: 方法之间不能加逗号分隔,同时方法不需要添加 function 关键
25-2 类的继承
现实中的继承:子承父业,比如我们都继承了父亲的姓。
程序中的继承:子类可以继承父类的一些属性和方法
class Father{ // 父类
}
class Son extends Father { // 子类继承父类
}
25-2-1 继承
class Father {
constructor(surname) {
this.surname= surname;
}
say() {
console.log('你的姓是' + this.surname);
}
}
class Son extends Father{ // 这样子类就继承了父类的属性和方法
}
var damao= new Son('刘');
damao.say();
25-2-2 super 关键字
super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数
lass Person { // 父类
constructor(surname){
this.surname = surname;
}
}
class Student extends Person { // 子类继承父类
constructor(surname,firstname){
super(surname); // 调用父类的constructor(surname)
this.firstname = firstname; // 定义子类独有的属性
}
}
注意: 子类在构造函数中使用super, 必须放到 this 前面 (必须先调用父类的构造方法,在使用子类构造方法)
class Father {
constructor(surname) {
this.surname = surname;
}
saySurname() {
console.log('我的姓是' + this.surname);
}
}
class Son extends Father { // 这样子类就继承了父类的属性和方法
constructor(surname, fristname) {
super(surname); // 调用父类的constructor(surname)
this.fristname = fristname;
}
sayFristname() {
console.log("我的名字是:" + this.fristname);
}
}
var damao = new Son('刘', "德华");
damao.saySurname();
damao.sayFristname();
super关键字 用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数
class Father {
say() {
return '我是爸爸';
}
}
class Son extends Father { // 这样子类就继承了父类的属性和方法
say() {
// super.say() super 调用父类的方法
return super.say() + '的儿子';
}
}
var damao = new Son();
console.log(damao.say());
25-3 Class类
// 以手机类为例
function Phone(brand,price){
this.brand = brand
this.price = price
}
// 静态成员
//相当于 python里的staticmethod
Phone.name = '手机'
Phone.change = function(){
console.log('我可以改变世界')
}
// 相当于Python里的classmethod
Phone.ptototype.size = "5.5英寸"
Phone.prototy.call = function() {
console.log('我可以打电话')
}
ler Huawei = new Phone('华为',5999)
// ES6语法
class Phone{
// 构造函数方法
constructor(brand,price){
this.brand = brand
this.call = call
}
// 语法必须使用这个方法,不能用ES5老语法
call() {
console.log('我可以打电话')
}
}
ler Huawei = new Phone('华为',5999)
25-3-1 构造函数继承
function Phone(brand,price){
this.brand = brand
this.price = price
}
Phone.prototype.call = function(){
console.log('我可以打电话')
}
//智能手机
function SmartPhone(brand,price,color,size){
Phone.call(this,brand,price){
this.color = color
this.size = size
}
}
// 设置子级构造函数的原型
SmartPhone.prototype = new Phone
SmartPhone.prototype.constructor = SmartPhone
// 声明子类的方法
SmartPhone.prototype.photo = function(){
console.log('我可以拍照')
}
SmartPhone.prototype.playGame = function() {
console.log('我可以打游戏')
}
const chuzi = new SmartPhone("锤子",2499,'黑色','505')
25-3-2 ES6里类的继承
class Phone{
constructor(brand,price){
this.brand = brand
this.price = price
}
call(){
console.log('我可以打电话')
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size){
super(brand,price)
this.color = color
this.size = size
}
photo() {
console.log('我可以拍照')
}
playGame() {
console.log('我可以打游戏')
}
}
const xioami = new SmartPhone('xiaomi',2499,'黑色','505')
25-3-3 子类对父类方法的重写
class Phone{
constructor(brand,price){
this.brand = brand
this.price = price
}
call(){
console.log('我可以打电话')
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size){
super(brand,price)
this.color = color
this.size = size
}
photo() {
console.log('我可以拍照')
}
playGame() {
console.log('我可以打游戏')
}
// 可以对父类方法重写
call(){
console.log('我可以进行视频通话')
}
}
const xioami = new SmartPhone('xiaomi',2499,'黑色','505')
25-3-4 class中的getter和setter
class Phone{
get price() {
console.log('我被读取了')
return 'i love you '
}
set price(val){
console.log('价格修改了')
this.val = val
}
}
let s = new Phone()
console.log(s.price)
// '我被读取了'
// 'i love you ' -------------------> 这才是s.price的返回值
s.price = 'free' // '价格修改了'
26,JavaScript操作DOM
26-1 获取元素
26-1-1 根据ID获取
document.getElementById('id');
使用 console.dir() 可以打印我们获取的元素对象,更好的查看对象里面的属性和方法。
<!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>
<div id="time">2019-9-9</div>
<script>
// 1. 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面
// 2. get 获得 element 元素 by 通过 驼峰命名法
// 3. 参数 id是大小写敏感的字符串
// 4. 返回的是一个元素对象
var timer = document.getElementById('time');
console.log(timer);
console.log(typeof timer);
// 5. console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法
console.dir(timer);
</script>
</body>
</html>
26-1-2 根据标签名获取
使用 getElementsByTagName() 方法可以返回带有指定标签名的对象的集合。
document.getElementsByTagName('标签名');
注意:
// 因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历。
// 得到元素对象是动态的
// 如果获取不到元素,则返回为空的伪数组(因为获取不到对象)
还可以获取某个元素(父元素)内部所有指定标签名的子元素.
element.getElementsByTagName('标签名');
// 注意:父元素必须是单个对象(必须指明是哪一个元素对象). 获取的时候不包括父元素自己。
<!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>知否知否,应是等你好久11</li>
<li>知否知否,应是等你好久11</li>
<li>知否知否,应是等你好久11</li>
<li>知否知否,应是等你好久11</li>
</ul>
<ol id="ol">
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</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>
</html>
26-1-3 通过 HTML5 新增的方法获取
document.getElementsByClassName(‘类名’);// 根据类名返回元素对象集合
document.querySelector('选择器'); // 根据指定选择器返回第一个元素对象
document.querySelectorAll('选择器'); // 根据指定选择器返回
注意: // querySelector 和 querySelectorAll里面的选择器需要加符号,比如:document.querySelector('#nav');
<!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>
<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 返回指定选择器的第一个元素对象 切记 里面的选择器需要加符号 .box #nav
var firstBox = document.querySelector('.box');
console.log(firstBox);
var nav = document.querySelector('#nav');
console.log(nav);
var li = document.querySelector('li');
console.log(li);
// 3. querySelectorAll()返回指定选择器的所有元素对象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
var lis = document.querySelectorAll('li');
console.log(lis);
</script>
</body>
</html>
26-1-4 获取特殊元素(body,html)
4-1 获取body元素
doucumnet.body // 返回body元素对象
4-2 获取html元素
document.documentElement // 返回html元素对象
26-2 事件基础
26-2-1 事件的三要素
1. 事件源 (谁)
2. 事件类型 (什么事件)
3. 事件处理程序 (做啥)
26-2-2 案例:点击按钮弹出警示框
// 获取事件源(按钮)
// 注册事件(绑定事件),使用 onclick
// 编写事件处理程序,写一个函数弹出 alert 警示框
代码实现
var btn = document.getElementById('btn');
btn.onclick = function() {
alert('你好吗');
};
26-2-3 执行事件的步骤
1,// 获取事件源
2. // 注册事件(绑定事件)
3. // 添加事件处理程序(采取函数赋值形式)
26-2-4 常见的鼠标事件
*鼠标事件* | *触发条件* |
---|---|
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
26-3 操作元素
26-3-1 改变元素的内容
element.innerText
// 从起始位置到终止位置的内容, 但它去除 html 标签, 同时空格和换行也会去掉
element.innerHTML
// 起始位置到终止位置的全部内容,包括 html 标签,同时保留空格和换行
<!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>
<div></div>
<p>
我是文字
<span>123</span>
</p>
<script>
// innerText 和 innerHTML的区别
// 1. innerText 不识别html标签 非标准 去除空格和换行
var div = document.querySelector('div');
// div.innerText = '<strong>今天是:</strong> 2019';
// 2. innerHTML 识别html标签 W3C标准 保留空格和换行的
div.innerHTML = '<strong>今天是:</strong> 2019';
// 这两个属性是可读写的 可以获取元素里面的内容
var p = document.querySelector('p');
console.log(p.innerText);
console.log(p.innerHTML);
</script>
</body>
</html>
<!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,
p {
width: 300px;
height: 30px;
line-height: 30px;
color: #fff;
background-color: pink;
}
</style>
</head>
<body>
<button>显示当前系统时间</button>
<div>某个时间</div>
<p>1123</p>
<script>
// 当我们点击了按钮, div里面的文字会发生变化
// 1. 获取元素
var btn = document.querySelector('button');
var div = document.querySelector('div');
// 2.注册事件
btn.onclick = function() {
// div.innerText = '2019-6-6';
div.innerHTML = getDate();
}
function getDate() {
var date = new Date();
// 我们写一个 2019年 5月 1日 星期三
var year = date.getFullYear();
var month = date.getMonth() + 1;
var dates = date.getDate();
var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
var day = date.getDay();
return '今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day];
}
// 我们元素可以不用添加事件
var p = document.querySelector('p');
p.innerHTML = getDate();
</script>
</body>
</html>
26-3-2 常用元素的属性操作
1. innerText、innerHTML 改变元素内容
2. src、href
3. id、alt、title
演示
<!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 {
width: 300px;
}
</style>
</head>
<body>
<button id="ldh">刘德华</button>
<button id="zxy">张学友</button> <br>
<img src="images/ldh.jpg" alt="" title="刘德华">
<script>
// 修改元素属性 src
// 1. 获取元素
var ldh = document.getElementById('ldh');
var zxy = document.getElementById('zxy');
var img = document.querySelector('img');
// 2. 注册事件 处理程序
zxy.onclick = function() {
img.src = 'images/zxy.jpg';
img.title = '张学友思密达';
}
ldh.onclick = function() {
img.src = 'images/ldh.jpg';
img.title = '刘德华';
}
</script>
</body>
</html>
2-1 分时间显示不同图片案例
根据不同时间,页面显示不同图片,同时显示不同的问候语。 如果上午时间打开页面,显示上午好,显示上午的图片。 如果下午时间打开页面,显示下午好,显示下午的图片。 如果晚上时间打开页面,显示晚上好,显示晚上的图片。
// 根据系统不同时间来判断,所以需要用到日期内置对象
// 利用多分支语句来设置不同的图片
// 需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
// 需要一个div元素,显示不同问候语,修改元素内容即可
<!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 {
width: 300px;
}
</style>
</head>
<body>
<img src="images/s.gif" alt="">
<div>上午好</div>
<script>
// 根据系统不同时间来判断,所以需要用到日期内置对象
// 利用多分支语句来设置不同的图片
// 需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
// 需要一个div元素,显示不同问候语,修改元素内容即可
// 1.获取元素
var img = document.querySelector('img');
var div = document.querySelector('div');
// 2. 得到当前的小时数
var date = new Date();
var h = date.getHours();
// 3. 判断小时数改变图片和文字信息
if (h < 12) {
img.src = 'images/s.gif';
div.innerHTML = '亲,上午好,好好写代码';
} else if (h < 18) {
img.src = 'images/x.gif';
div.innerHTML = '亲,下午好,好好写代码';
} else {
img.src = 'images/w.gif';
div.innerHTML = '亲,晚上好,好好写代码';
}
</script>
</body>
</html>
26-3-3 表单属性设置
type、value、checked、selected、disabled
3-1 案例:仿京东显示密码
核心思路: // 点击眼睛按钮,把密码框类型改为文本框就可以看见里面的密码
一个按钮两个状态,点击一次,切换为文本框,继续点击一次切换为密码框
算法:// 利用一个flag变量,来判断flag的值,如果是1 就切换为文本框,flag 设置为0,如果是0 就切换为密码框,flag设置为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>
.box {
position: relative;
width: 400px;
border-bottom: 1px solid #ccc;
margin: 100px auto;
}
.box input {
width: 370px;
height: 30px;
border: 0;
outline: none;
}
.box img {
position: absolute;
top: 2px;
right: 2px;
width: 24px;
}
</style>
</head>
<body>
<div class="box">
<label for="">
<img src="images/close.png" alt="" id="eye">
</label>
<input type="password" name="" id="pwd">
</div>
<script>
// 1. 获取元素
var eye = document.getElementById('eye');
var pwd = document.getElementById('pwd');
// 2. 注册事件 处理程序
var flag = 0;
eye.onclick = function() {
// 点击一次之后, flag 一定要变化
if (flag == 0) {
pwd.type = 'text';
eye.src = 'images/open.png';
flag = 1; // 赋值操作
} else {
pwd.type = 'password';
eye.src = 'images/close.png';
flag = 0;
}
}
</script>
</body>
</html>
26-3-4 样式属性操作
我们可以通过 JS 修改元素的大小、颜色、位置等样式。
element.style // 行内样式操作
element.className // 类名样式操作
注意:
1.// JS 里面的样式采取驼峰命名法 比如 fontSize、 backgroundColor
2.// JS 修改 style 样式操作,产生的是行内样式,CSS 权重比较高
<!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>
<div></div>
<script>
// 1. 获取元素
var div = document.querySelector('div');
// 2. 注册事件 处理程序
div.onclick = function() {
// div.style里面的属性 采取驼峰命名法
this.style.backgroundColor = 'purple';
this.style.width = '250px';
}
</script>
</body>
</html>
4-1 案例: 淘宝点击关闭二维码
核心思路: 利用样式的显示和隐藏完成, display:none 隐藏元素 display:block 显示元素
点击按钮,就让这个二维码盒子隐藏起来即可
<!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>
.box {
position: relative;
width: 74px;
height: 88px;
border: 1px solid #ccc;
margin: 100px auto;
font-size: 12px;
text-align: center;
color: #f40;
/* display: block; */
}
.box img {
width: 60px;
margin-top: 5px;
}
.close-btn {
position: absolute;
top: -1px;
left: -16px;
width: 14px;
height: 14px;
border: 1px solid #ccc;
line-height: 14px;
font-family: Arial, Helvetica, sans-serif;
cursor: pointer;
}
</style>
</head>
<body>
<div class="box">
淘宝二维码
<img src="images/tao.png" alt="">
<i class="close-btn">×</i>
</div>
<script>
// 1. 获取元素
var btn = document.querySelector('.close-btn');
var box = document.querySelector('.box');
// 2.注册事件 程序处理
btn.onclick = function() {
box.style.display = 'none';
}
</script>
</body>
</html>
4-2 案例: 循环精灵图背景
// 首先精灵图图片排列有规律的
// 核心思路: 利用for循环 修改精灵图片的 背景位置 background-position
// 剩下的就是考验你的数学功底了
// 让循环里面的 i 索引号 * 44 就是每个图片的y坐标
<!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;
}
.box {
width: 250px;
margin: 100px auto;
}
.box li {
float: left;
width: 24px;
height: 24px;
background-color: pink;
margin: 15px;
background: url(images/sprite.png) no-repeat;
}
</style>
</head>
<body>
<div class="box">
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<script>
// 1. 获取元素 所有的小li
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
// 让索引号 乘以 44 就是每个li 的背景y坐标 index就是我们的y坐标
var index = i * 44;
lis[i].style.backgroundPosition = '0 -' + index + 'px';
}
</script>
</body>
</html>
4-3 案例:显示隐藏文本框内容
// 首先表单需要2个新事件,获得焦点 onfocus 失去焦点 onblur
// 如果获得焦点, 判断表单里面内容是否为默认文字,如果是默认文字,就清空表单内容
// 如果失去焦点, 判断表单内容是否为空,如果为空,则表单内容改为默认文字
我们可以通过 JS 修改元素的大小、颜色、位置等样式。
element.style // 行内样式操作
element.className // 类名样式操作
<!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>
input {
color: #999;
}
</style>
</head>
<body>
<input type="text" value="手机">
<script>
// 1.获取元素
var text = document.querySelector('input');
// 2.注册事件 获得焦点事件 onfocus
text.onfocus = function() {
// console.log('得到了焦点');
if (this.value === '手机') {
this.value = '';
}
// 获得焦点需要把文本框里面的文字颜色变黑
this.style.color = '#333';
}
// 3. 注册事件 失去焦点事件 onblur
text.onblur = function() {
// console.log('失去了焦点');
if (this.value === '') {
this.value = '手机';
}
// 失去焦点需要把文本框里面的文字颜色变浅色
this.style.color = '#999';
}
</script>
</body>
</html>
4-4 案例: 密码框格式提示错误信息
// 首先判断的事件是表单失去焦点 onblur
// 如果输入正确则提示正确的信息颜色为绿色小图标变化
// 如果输入不是6到16位,则提示错误信息颜色为红色 小图标变化
// 因为里面变化样式较多,我们采取className修改样式
26-3-5 操作元素总结
26-3-6 排他思想
<!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>
<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) 然后才让当前的元素背景颜色为pink 留下我自己
this.style.backgroundColor = 'pink';
}
}
//2. 首先先排除其他人,然后才设置自己的样式 这种排除其他人的思想我们成为排他思想
</script>
</body>
</html>
6-1 案例:百度换肤
这个案例练习的是给一组元素注册事件 给4个小图片利用循环注册点击事件 当我们点击了这个图片,让我们页面背景改为当前的图片 核心算法: 把当前图片的src 路径取过来,给 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;
}
body {
background: url(images/1.jpg) no-repeat center top;
}
li {
list-style: none;
}
.baidu {
overflow: hidden;
margin: 100px auto;
background-color: #fff;
width: 410px;
padding-top: 3px;
}
.baidu li {
float: left;
margin: 0 1px;
cursor: pointer;
}
.baidu img {
width: 100px;
}
</style>
</head>
<body>
<ul class="baidu">
<li><img src="images/1.jpg"></li>
<li><img src="images/2.jpg"></li>
<li><img src="images/3.jpg"></li>
<li><img src="images/4.jpg"></li>
</ul>
<script>
// 1. 获取元素
var imgs = document.querySelector('.baidu').querySelectorAll('img');
// console.log(imgs);
// 2. 循环注册事件
for (var i = 0; i < imgs.length; i++) {
imgs[i].onclick = function() {
// this.src 就是我们点击图片的路径 images/2.jpg
// console.log(this.src);
// 把这个路径 this.src 给body 就可以了
document.body.style.backgroundImage = 'url(' + this.src + ')';
}
}
</script>
</body>
</html>
6-2 案例:表格隔行变色
用到新的鼠标事件 鼠标经过 onmouseover 鼠标离开 onmouseout 核心思路:鼠标经过 tr 行,当前的行变背景颜色, 鼠标离开去掉当前的背景颜色 注意: 第一行(thead里面的行)不需要变换颜色,因此我们获取的是 tbody 里面的行
<!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>
table {
width: 800px;
margin: 100px auto;
text-align: center;
border-collapse: collapse;
font-size: 14px;
}
thead tr {
height: 30px;
background-color: skyblue;
}
tbody tr {
height: 30px;
}
tbody td {
border-bottom: 1px solid #d7d7d7;
font-size: 12px;
color: blue;
}
.bg {
background-color: pink;
}
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>代码</th>
<th>名称</th>
<th>最新公布净值</th>
<th>累计净值</th>
<th>前单位净值</th>
<th>净值增长率</th>
</tr>
</thead>
<tbody>
<tr>
<td>003526</td>
<td>农银金穗3个月定期开放债券</td>
<td>1.075</td>
<td>1.079</td>
<td>1.074</td>
<td>+0.047%</td>
</tr>
<tr>
<td>003526</td>
<td>农银金穗3个月定期开放债券</td>
<td>1.075</td>
<td>1.079</td>
<td>1.074</td>
<td>+0.047%</td>
</tr>
<tr>
<td>003526</td>
<td>农银金穗3个月定期开放债券</td>
<td>1.075</td>
<td>1.079</td>
<td>1.074</td>
<td>+0.047%</td>
</tr>
<tr>
<td>003526</td>
<td>农银金穗3个月定期开放债券</td>
<td>1.075</td>
<td>1.079</td>
<td>1.074</td>
<td>+0.047%</td>
</tr>
<tr>
<td>003526</td>
<td>农银金穗3个月定期开放债券</td>
<td>1.075</td>
<td>1.079</td>
<td>1.074</td>
<td>+0.047%</td>
</tr>
<tr>
<td>003526</td>
<td>农银金穗3个月定期开放债券</td>
<td>1.075</td>
<td>1.079</td>
<td>1.074</td>
<td>+0.047%</td>
</tr>
</tbody>
</table>
<script>
// 1.获取元素 获取的是 tbody 里面所有的行
var trs = document.querySelector('tbody').querySelectorAll('tr');
// 2. 利用循环绑定注册事件
for (var i = 0; i < trs.length; i++) {
// 3. 鼠标经过事件 onmouseover
trs[i].onmouseover = function() {
// console.log(11);
this.className = 'bg';
}
// 4. 鼠标离开事件 onmouseout
trs[i].onmouseout = function() {
this.className = '';
}
}
</script>
</body>
</html>
6-3 案例:表单全选取消全选案例
- 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
- 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。
- 可以设置一个变量,来控制全选是否选中
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
.wrap {
width: 300px;
margin: 100px auto 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #c0c0c0;
width: 300px;
}
th,
td {
border: 1px solid #d0d0d0;
color: #404060;
padding: 10px;
}
th {
background-color: #09c;
font: bold 16px "微软雅黑";
color: #fff;
}
td {
font: 14px "微软雅黑";
}
tbody tr {
background-color: #f0f0f0;
}
tbody tr:hover {
cursor: pointer;
background-color: #fafafa;
}
</style>
</head>
<body>
<div class="wrap">
<table>
<thead>
<tr>
<th>
<input type="checkbox" id="j_cbAll" />
</th>
<th>商品</th>
<th>价钱</th>
</tr>
</thead>
<tbody id="j_tb">
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPhone8</td>
<td>8000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPad Pro</td>
<td>5000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPad Air</td>
<td>2000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>Apple Watch</td>
<td>2000</td>
</tr>
</tbody>
</table>
</div>
<script>
// 1. 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
// 获取元素
var j_cbAll = document.getElementById('j_cbAll'); // 全选按钮
var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); // 下面所有的复选框
// 注册事件
j_cbAll.onclick = function() {
// this.checked 它可以得到当前复选框的选中状态如果是true 就是选中,如果是false 就是未选中
console.log(this.checked);
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].checked = this.checked;
}
}
// 2. 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].onclick = function() {
// flag 控制全选按钮是否选中
var flag = true;
// 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
for (var i = 0; i < j_tbs.length; i++) {
if (!j_tbs[i].checked) {
flag = false;
break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了
}
}
j_cbAll.checked = flag;
}
}
</script>
</body>
</html>
26-3-7 自定义属性
7-1 获取属性值
element.属性 // 获取属性值。
element.getAttribute('属性');
区别:
// element.属性 获取内置属性值(元素本身自带的属性)
// element.getAttribute(‘属性’); 主要获得自定义的属性 (标准) 我们程序员自定义的属性
7-2 设置属性值
element.属性 = ‘值’ // 设置内置属性值。
element.setAttribute('属性', '值');
区别:
// element.属性 设置内置属性值
// element.setAttribute(‘属性’); 主要设置自定义的属性 (标准)
7-3 移除属性
element.removeAttribute('属性');
自定义属性演示
<!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>
<div id="demo" index="1" class="nav"></div>
<script>
var div = document.querySelector('div');
// 1. 获取元素的属性值
// (1) element.属性
console.log(div.id);
//(2) element.getAttribute('属性') get得到获取 attribute 属性的意思 我们程序员自己添加的属性我们称为自定义属性 index
console.log(div.getAttribute('id'));
console.log(div.getAttribute('index'));
// 2. 设置元素属性值
// (1) element.属性= '值'
div.id = 'test';
div.className = 'navs';
// (2) element.setAttribute('属性', '值'); 主要针对于自定义属性
div.setAttribute('index', 2);
div.setAttribute('class', 'footer'); // class 特殊 这里面写的就是class 不是className
// 3 移除属性 removeAttribute(属性)
div.removeAttribute('index');
</script>
</body>
</html>
7-4 案例:tab 栏切换(重点案例)
<!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);
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>
26-3-8 H5 自定义属性
// 自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
// 自定义属性获取是通过getAttribute(‘属性’) 获取。
// 但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。
// H5给我们新增了自定义属性:
8-1 设置H5自定义属性
// H5规定自定义属性data-开头做为属性名并且赋值。比如:
<div data-index=“1”></div>
// 或者使用 JS 设置
element.setAttribute(‘data-index’, 2)
8-2 获取H5自定义属性
// 兼容性获取
element.getAttribute(‘data-index’);
// H5新增
element.dataset.index 或者 element.dataset[‘index’] ie 11才开始支持
<!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>
<div getTime="20" data-index="2" data-list-name="andy"></div>
<script>
var div = document.querySelector('div');
// console.log(div.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>
</html>
26-4 节点操作
26-4-1 为什么学节点操作
26-4-2 节点概述
26-4-3 节点层级
3-1 父级节点
node.parentNode
- parentNode 属性可返回某节点的父节点,注意是最近的一个父节点
- 如果指定的节点没有父节点则返回 null
<!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>
<!-- 节点的优点 -->
<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>
</html>
3-2 子节点
2-1 parentNode.childNodes(标准)
parentNode.childNodes(标准)
- parentNode.childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合。
- 注意:返回值里面包含了所有的子节点,包括元素节点,文本节点等。
- 如果只想要获得里面的元素节点,则需要专门处理。 所以我们一般不提倡使用childNodes
var ul = document. querySelector(‘ul’);
for(var i = 0; i < ul.childNodes.length;i++) {
if (ul.childNodes[i].nodeType == 1) {// ul.childNodes[i] 是元素节点
console.log(ul.childNodes[i]);}
}
<!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>
<!-- 节点的优点 -->
<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);
console.log(ul.childNodes[1].nodeType);
// 2. children 获取所有的子元素节点 也是我们实际开发常用的
console.log(ul.children);
</script>
</body>
</html>
2-2 parentNode.children(非标准)
// parentNode.children 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回 (这个是我们重点掌握的)。
// 虽然children 是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用
2-3 parentNode.firstChild
/ firstChild 返回第一个子节点,找不到则返回null。同样,也是包含所有的节点。
2-4 parentNode.lastChild
// lastChild 返回最后一个子节点,找不到则返回null。同样,也是包含所有的节点。
2-5 parentNode.firstElementChild
// firstElementChild 返回第一个子元素节点,找不到则返回null。
2-6 parentNode.lastElementChild
lastElementChild 返回最后一个子元素节点,找不到则返回null。
注意:这两个方法有兼容性问题,IE9 以上才支持。
<!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>
<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);
console.log(ol.lastChild);
// 2. firstElementChild 返回第一个子元素节点 ie9才支持
console.log(ol.firstElementChild);
console.log(ol.lastElementChild);
// 3. 实际开发的写法 既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]);
console.log(ol.children[ol.children.length - 1]);
</script>
</body>
</html>
实际开发中,firstChild 和 lastChild 包含其他节点,操作不方便,而 firstElementChild 和 lastElementChild 又有兼容性问题,那么我们如何获取第一个子元素节点或最后一个子元素节点呢?
解决方案:
// 如果想要第一个子元素节点,可以使用 parentNode.chilren[0]
// 如果想要最后一个子元素节点,可以使用 parentNode.chilren[parentNode.chilren.length - 1]
2-7 案例:下拉菜单
- 导航栏里面的li 都要有鼠标经过效果,所以需要循环注册鼠标事件
- 核心原理: 当鼠标经过li 里面的 第二个孩子 ul 显示, 当鼠标离开,则ul 隐藏
var nav = document.querySelector('.nav');
var lis = nav.children; // 得到4个小li
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.children[1].style.display = 'block';
}
lis[i].onmouseout = function() {
this.children[1].style.display = 'none';
}
}
完整代码
<!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;
}
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';
}
lis[i].onmouseout = function() {
this.children[1].style.display = 'none';
}
}
</script>
</body>
</html>
3-3 兄弟节点
3-1 node.nextSibling
// nextSibling 返回当前元素的下一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点
3-2 node.previousSibling
// previousSibling 返回当前元素上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点
3-3 node.nextElementSibling
// nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回null。
3-4 node.previousElementSibling
// previousElementSibling 返回当前元素上一个兄弟节点,找不到则返回null。
注意:这两个方法有兼容性问题, IE9 以上才支持。
问:如何解决兼容性问题 ?
// 自己封装一个兼容性的函数
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if (el.nodeType === 1) {
return el;
}
}
return null;
}
兄弟节点演示
<!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>
<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>
</html>
26-4-4 节点创建
document.createElement('tagName')
document.createElement() 方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为动态创建元素节点。
26-4-5 添加节点
5-1 node.appendChild(child)
node.appendChild() // 方法将一个节点添加到指定父节点的子节点列表末尾。类似于 CSS 里面的 after 伪元素。
5-2 node.insertBefore(child, 指定元素)
node.insertBefore() // 方法将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before 伪元素。
创建和添加节点演示
<!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>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>
</html>
5-3 案例:简单版发布留言案例
- 核心思路: 点击按钮之后,就动态创建一个li,添加到ul 里面。
- 创建li 的同时,把文本域里面的值通过li.innerHTML 赋值给 li
- 如果想要新的留言后面显示就用 appendChild 如果想要前面显示就用insertBefore
<!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;
}
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>
</html>
26-4-6 删除节点
node.removeChild(child)
node.removeChild() 方法从 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>
<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) {
this.disabled = true;
} else {
ul.removeChild(ul.children[0]);
}
}
</script>
</body>
</html>
6-1 案例:删除留言案例
- 当我们把文本域里面的值赋值给li 的时候,多添加一个删除的链接
- 需要把所有的链接获取过来,当我们点击当前的链接的时候,删除当前链接所在的li
- 阻止链接跳转需要添加 javascript:void(0); 或者 javascript:;
<!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;
}
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>
</html>
26-4-7 复制节点
node.cloneNode()
// ode.cloneNode() 方法返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点
注意:
- 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
- 如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
复制节点演示
<!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>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);
ul.appendChild(lili);
</script>
</body>
</html>
7-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>
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);
// 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>
</html>
26-4-8 三种动态创建元素区别
- document.write()
- element.innerHTML
- document.createElement()
1, document.write // 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
2. innerHTML // 是将内容写入某个 DOM 节点,不会导致页面全部重绘
3. innerHTML // 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
4. createElement() // 创建多个元素效率稍低一点点,但是结构更清晰
// 总结:不同浏览器下,innerHTML 效率要比 creatElement 高
26-5 事件高级
26-5-1 注册事件
1-1 注册事件概述
1-2 addEventListener 事件监听方式
eventTarget.addEventListener(type, listener[, useCapture])
eventTarget.addEventListener()方法将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。该方法接收三个参数:
- type:事件类型字符串,比如 click 、mouseover ,注意这里不要带 on
- listener:事件处理函数,事件发生时,会调用该监听函数
- useCapture:可选参数,是一个布尔值,默认是 false。学完 DOM 事件流后,我们再进一步学习
1-3 attachEvent 事件监听方式
eventTarget.attachEvent(eventNameWithOn, callback)
两种注册事件的演示
<!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>
<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() {
alert(22);
})
btns[1].addEventListener('click', function() {
alert(33);
})
// 3. attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function() {
alert(11);
})
</script>
</body>
</html>
1-4 注册事件兼容性解决问题
function addEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 addEventListener 方法
if (element.addEventListener) {
element.addEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, fn);
} else {
// 相当于 element.onclick = fn;
element['on' + eventName] = fn;
}
兼容性处理的原则: 首先照顾大多数浏览器,再处理特殊浏览器
26-1-2 删除事件
2-1 删除事件的方式
2-2 删除事件兼容性解决方案
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;
}
2-3 删除事件代码演示
<!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>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>
</html>
26-5-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>
26-5-4 事件对象
4-1 事件对象
eventTarget.onclick = function(event) {}
eventTarget.addEventListener('click', function(event) {})
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
4-2 事件对象的使用语法
eventTarget.onclick = function(event) {
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
}
eventTarget.addEventListener('click', function(event) {
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
})
- 这个 event 是个形参,系统帮我们设定为事件对象,不需要传递实参过去。
- 当我们注册事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
4-3 事件对象的兼容性方案
4-4 事件对象的常见属性和方法
// e.target 和 this 的区别:
// this 是事件绑定的元素, 这个函数的调用者(绑定这个事件的元素)
// e.target 是事件触发的元素。
<!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-5 事件对象的常见属性和方法
*事件对象属性方法* | *说明* |
---|---|
e.target | 返回触发事件的对象 标准 |
e.srcElement | 返回触发事件的对象 非标准ie6-8使用 |
e.type | 返回事件的类型比如click mouseover不带on |
e.cancelBubble | 该属性阻止冒泡非标准ie6-8使用 |
e.returnValue | 该属性阻止默认事件(默认行为)非标准ie6-8使用比如不让链接跳转 |
e.preventDefault( | 该方法阻止默认事件(默认行为)标准比如不让链接跳转 |
e.stopPropagation( | 阻止冒泡标准 |
26-5-5 阻止事件冒泡
5-1 阻止事件冒泡的两种方式
- 事件冒泡:开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点。
- 事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要我们灵活掌握。
阻止事件冒泡
// 标准写法:利用事件对象里面的 stopPropagation()方法
e.stopPropagation()
// 非标准写法:IE 6-8 利用事件对象 cancelBubble 属性
e.cancelBubble = true;
5-2 阻止事件冒泡的兼容性解决方案
if(e && e.stopPropagation){
e.stopPropagation();
}else{
window.event.cancelBubble = true;
}
5-3 代码演示
<!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>
26-5-6 事件委托
事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要我们灵活掌握。程序中也有如此场景:
<ul>
<li>知否知否,应该有弹框在手</li>
<li>知否知否,应该有弹框在手</li>
<li>知否知否,应该有弹框在手</li>
<li>知否知否,应该有弹框在手</li>
<li>知否知否,应该有弹框在手</li>
</ul>
点击每个 li 都会弹出对话框,以前需要给每个 li 注册事件,是非常辛苦的,而且访问 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>
26-5-7 常用的鼠标事件
7-1 常用的鼠标事件
1-1 禁止鼠标右键菜单
contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
})
1-2 禁止鼠标选中(selectstart 开始选中)
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
<!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>
我是一段不愿意分享的文字
<script>
// 1. contextmenu 我们可以禁用右键菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
})
// 2. 禁止选中文字 selectstart
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
</script>
</body>
</html>
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>
body {
height: 3000px;
}
</style>
</head>
<body>
<script>
// 鼠标事件对象 MouseEvent
document.addEventListener('click', function(e) {
// 1. client 鼠标在可视区的x和y坐标
console.log(e.clientX);
console.log(e.clientY);
console.log('---------------------');
// 2. page 鼠标在页面文档的x和y坐标
console.log(e.pageX);
console.log(e.pageY);
console.log('---------------------');
// 3. screen 鼠标在电脑屏幕的x和y坐标
console.log(e.screenX);
console.log(e.screenY);
})
</script>
</body>
</html>
7-3 跟随鼠标的天使
var pic = document.querySelector('img');
document.addEventListener('mousemove', function(e) {
var x = e.pageX;
var y = e.pageY;
pic.style.top = y - 40 + 'px';
pic.style.left = x - 50 + 'px';
})
<!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>
26-5-8 常用的键盘事件
8-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>
</head>
<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>
</html>
8-2 键盘事件对象
<!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>
<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>
</html>
8-3 案例: 模拟京东按键输入内容
var search = document.querySelector('input');
document.addEventListener('keyup', function(e) {
// console.log(e.keyCode);
if (e.keyCode === 83) {
search.focus();
}
})
完整代码
<!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>
<input type="text">
<script>
// 核心思路: 检测用户是否按下了s 键,如果按下s 键,就把光标定位到搜索框里面
// 使用键盘事件对象里面的keyCode 判断用户按下的是否是s键
// 搜索框获得焦点: 使用 js 里面的 focus() 方法
var search = document.querySelector('input');
document.addEventListener('keyup', function(e) {
// console.log(e.keyCode);
if (e.keyCode === 83) {
search.focus();
}
})
</script>
</body>
</html>
8-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>
* {
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>
27,JavaScript 操作BOM
27-1 BOM概述
27-1-1 什么是 BOM
27-1-2 BOM 的构成
<!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>
<script>
// window.document.querySelector()
var num = 10;
console.log(num);
console.log(window.num);
function fn() {
console.log(11);
}
fn();
window.fn();
// alert(11);
// window.alert(11)
console.dir(window);
// var name = 10;
console.log(window.name);
</script>
</body>
</html>
27-2 window对象的常见事件
<!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>
27-2-1 窗口加载事件
window.onload = function(){}
或者
window.addEventListener("load",function(){});
window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS 文件等), 就调用的处理函数。
document.addEventListener('DOMContentLoaded',function(){})
27-2-2 调整窗口大小事件
window.onresize = function(){}
window.addEventListener("resize",function(){});
// window.onresize 是调整窗口大小加载事件, 当触发时就调用的处理函数。
注意: 1. 只要窗口大小发生像素变化,就会触发这个事件。 2. 我们经常利用这个事件完成响应式布局。 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>
27-3 定时器
27-3-1 两种定时器
window 对象给我们提供了 2 个非常好用的方法-定时器。
- setTimeout()
- setInterval()
27-3-2 setTimeout() 定时器
window.setTimeout(调用函数, [延迟的毫秒数]);
// setTimeout() 方法用于设置一个定时器,该定时器在定时器到期后执行调用函数
注意:
- window 可以省略。
- 这个调用函数可以直接写函数,或者写函数名或者采取字符串‘函数名()'三种形式。第三种不推荐
- 延迟的毫秒数省略默认是 0,如果写,必须是毫秒。
- 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。
window.setTimeout(调用函数, [延迟的毫秒数]);
<!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>
<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>
</html>
27-3-3 案例: 5秒后自动关闭的广告
<!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>
<img src="images/ad.jpg" alt="" class="ad">
<script>
var ad = document.querySelector('.ad');
setTimeout(function() {
ad.style.display = 'none';
}, 5000);
</script>
</body>
</html>
27-3-4 停止 setTimeout() 定时器
window.clearTimeout(timeoutID)
// clearTimeout()方法取消了先前通过调用 setTimeout() 建立的定时器。
注意:
- window 可以省略。
- 里面的参数就是定时器的标识符 。
window.setInterval(回调函数, [间隔的毫秒数]);
setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。
27-3-5 案例: 倒计时
<!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
27-3-6 停止 setInterval() 定时器
window.clearInterval(intervalID);
// clearInterval()方法取消了先前通过调用 setInterval()建立的定时器。
注意:
- window 可以省略。
- 里面的参数就是定时器的标识符 。
<!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>
<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>
</html>
27-3-7 案例: 发送短信
<!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>
手机号码: <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>
</html>
27-3-8 this
this指向问题案例
<!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>
<button>点击</button>
<script>
// this 指向问题 一般情况下this的最终指向的是那个调用它的对象
// 1. 全局作用域或者普通函数中this指向全局对象window( 注意定时器里面的this指向window)
console.log(this);
function fn() {
console.log(this);
}
window.fn();
window.setTimeout(function() {
console.log(this);
}, 1000);
// 2. 方法调用中谁调用this指向谁
var o = {
sayHi: function() {
console.log(this); // this指向的是 o 这个对象
}
}
o.sayHi();
var btn = document.querySelector('button');
// btn.onclick = function() {
// console.log(this); // this指向的是btn这个按钮对象
// }
btn.addEventListener('click', function() {
console.log(this); // this指向的是btn这个按钮对象
})
// 3. 构造函数中this指向构造函数的实例
function Fun() {
console.log(this); // this 指向的是fun 实例对象
}
var fun = new Fun();
</script>
</body>
</html>
27-4 JS执行机制
27-4-1 JS 是单线程
27-4-2 同步和异步
27-4-3 同步和异步任务
27-4-4 JS 执行机制
27-5 location对象
27-5-1 什么是 location 对象
window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。
27-5-2 URL
27-5-3 location 对象的属性
27-5-4 案例: 5秒钟之后自动跳转页面
<!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>
<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>
</html>
27-5-4 location 对象的方法
<!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>
<button>点击</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
// 记录浏览历史,所以可以实现后退功能
// location.assign('http://www.itcast.cn');
// 不记录浏览历史,所以不可以实现后退功能
// location.replace('http://www.itcast.cn');
location.reload(true);
})
</script>
</body>
</html>
27-6 navigator 对象
navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值。
下面前端代码可以判断用户那个终端打开页面,实现跳转
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 = ""; //电脑
}
27-7 history 对象
window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。