第一章
1.什么是DOM
DOM: Document Object Model(文档对象模型)
是JavaScript的方法,仅当与特殊文档对象配合使用时(如选择器),才能发挥作用
什么是: 专门操作网页内容的API标准
W3C指定的标准,所有浏览器厂商遵照实现
何时: 今后只要操作网页内容,就必须用DOM提供的API
为什么: 为了统一操作网页内容的API
用DOM标准操作网页内容几乎100%兼容
分为: 核心 DOM 和 HTML DOM
核心DOM: 万能,但繁琐
HTML DOM: 对核心DOM中部分常用API的简化,专门操作HTML文档的API
不是万能,但简洁
总结: 实际开发中不必区分核心DOM和HTML DOM,优先使用简单的API,如果实现不了,用复杂的补充
DOM树:
什么是: 网页中的一切内容在内存中都是以树形结构存储在一起的,每项内容(元素,属性,文本)都是树上的一个节点对象
为什么: 树形结构是保存不确定层级深度的上下级包含关系最好的结构
根节点:树结构都有唯一的一个根节点:document节点,所有网页内容,都是document的子节点
节点对象: 网页中的每一项内容都是一个节点对象,节点对象封装了节点可用的属性和功能
如何:三大属性(返回的值):
.nodeType: 节点类型
什么是: 定义了节点的类型
何时: 区分节点的类型时
为什么: 不同类型的节点可用的属性和可执行的操作不同
值是整数:4个: document: 9 根节点
element: 1 元素节点,如<html>、<body>、<p>
attribute: 2 属性节点,元素的属性,如a标签的链接属性
text: 3 文本节点,标签中的纯文本
document.createTextNode("..."): 创建文本节点(无标签),括号内为文本值
问题: nodeType无法进一步区分具体的元素名
解决:nodeName
.nodeName: 节点名(元素的标签名)
什么是: 保存节点名称的属性
何时: 进一步区分具体的元素名
为什么: 不同的元素拥有的属性和可执行的操作都不一样
包括: document: #document
element: 元素的标签名(全大写)
attribute: 属性名
text: #text(文本节点)
其实, nodeName可代替nodeType来鉴别节点类型
.nodeValue: 节点值(了解)
什么是: 保存节点的值(几乎不用)
包括: document: null
element: null
attribute: 属性值
text: 文本内容
_______________________________________________________________________________________________
DOM 操作流程:(增删改查+事件处理)
查找触发事件的元素 → 绑定事件处理函数 → 查找要修改的元素 → /添加/删除/修改:内容/属性/样式
_______________________________________________________________________________________________
2.查找:4种:
1. 不需要查找可直接获得的元素:
document.documentElement: html
document.head: head
document.body: body
2. 按节点间关系查找:
何时: 如果已经获得一个元素,要找周围元素时
包括: 2大类关系:
节点树: 包含所有节点的完整树结构
父子:elem.parentNode: 获得一个节点的父节点
elem.childNodes: 获得父节点下的所有直接子节点
elem.firstChild: 获得父节点下的第一个直接子节点
elem.lastChild: 获得节点下的最后一个直接子节点
兄弟: elem.nextSibling: 获得一个节点相邻的下一个兄弟节点
elem.previousSibling: 获得一个节点相邻的前一个兄弟节点
问题: 受看不见的空字符文本节点的干扰,可用元素树解决
元素树: 仅包含元素节点的树结构(父子关系中与CSS有差异)
何时: 只要仅关心元素,不关心其它类型的节点时
父子: elem.parentElement: 获得一个节点的父元素
elem.children: 获得父元素(elem)下的所有直接子元素
elem.firstElementChild:获得父元素(elem)下的第一个直接子元素
elem.lastElementChild:获得父元素(elem)下的最后一个直接子元素
兄弟:elem.nextElementSibling: 获得一个元素相邻的下一个兄弟元素
elem.previousElementSibling:获得一个元素相邻的前一个兄弟元素
说明: 元素树不是一棵新树,仅是节点树的一个子集
优: 不受空文本的干扰
缺: IE8不兼容
比较:childNodes和children返回的不是数组,而是类数组对象
类数组对象: 长的像数组的对象
VS 数组: 相同: 1.下标;2. .length;3.用for循环遍历
不同: 类型不同,API不通用
非动态/动态集合可以直接在选择器后面加数字下标使用: elem.children[i]
其实,childNodes和children都返回动态集合
动态集合: 不实际存储数据,每次访问集合,都重新查找DOM树
问题: 反复访问集合,会导致反复查找DOM树
遍历动态集合: 错误: for(var i=0;i<children.length;i++){...}
正确:for(var i=0,len=children.length;i<len;i++){...}(按值传递)
用节点间关系,遍历查找一个父元素下所有后代节点
1. 递归遍历: 2步:
① 定义函数仅遍历直接子节点
② 对每个碰到的子节点,调用和父节点完全相同的函数
算法:深度优先: 每当同时有子元素和兄弟元素时,总是先遍历子元素。子元素遍历完,才返回遍历兄弟元素
问题: 递归的执行效率极低,可用循环代替
arguments.callee: 专门指代当前正在调用的函数自己
何时: 只要递归,在函数内调用自己,必须加arguments.callee()代替
为什么: 避免修改外层函数名后,还要重复修改内部调用的函数名
强调: 因为递归算法效率极低,所以ES6或新版浏览器中已经禁止使用arguments.callee,意味着不再推荐使用递归算法
caller 返回一个对函数的引用,该函数调用了当前函数
functionName.caller: functionName对象是所执行函数的名称
callee 返回正被执行的Function对象,也就是所指定的Function对象的正文
[function.]arguments.callee: 可选项function参数是当前正在执行的Function对象的名称
2. 用循环代替递归:
节点迭代器对象(NodeIterator): 专门按照深度优先遍历的顺序依次访问每个子元素的对象
内置"深度优先"算法
如何用节点迭代器对象:2步:
1. 创建节点迭代器对象:
var iterator=document.createNodeIterator( //传递四个参数
parent,NodeFilter.SHOW_ALL,null,false //NodeFilter.SHOW_ALL: 遍历所有节点
SHOW_ELEMENT //NodeFilter.SHOW_ELEMENT: 只遍历元素节点
);
2. 用循环反复调用NodeIterator的nextNode()函数,直到返回null退出:
do{
var node=iterator.nextNode(); //返回当前节点(同时会首先返回parent元素)
if(node!=null){
console.log(node.nodeType!=3?node.nodeName:node.nodeValue); //输出node,跳到下一个节点
}else break; //如果返回null,就退出循环
}while(true);
3.按HTML查找:4种:
① 按id查找一个元素:var elem=document.getElementById("id值");
强调:1.必须用document调用;2.只返回一个元素
② 按标签名查找多个元素: var elems=parent.getElementsByTagName("标签名");
强调:1. 可在任意父元素上调用(标签名为*时,可查找所有后代元素)
2. 不但找直接子元素,而且查找所有(符合条件的)后代元素
3.返回多个元素组成的动态集合
③ 按name属性查找(了解): var elems=document.getElementsByName("name的值");
何时: 查找拥有name属性的表单元素时
强调:1. 只能用document调用
2. 返回动态集合
④ 按class属性查找: var elems=parent.getElementsByClassName("class的值");
强调:1. 可在任意父元素上调用(IE8以下不支持)
2. 不但找直接子元素,而且找所有(符合条件的)后代元素
3.返回动态集合
4.只要包含指定的类名,就选择该元素,不必完全匹配
问题: 每次只能按一种条件查找,如果条件复杂,代码会很繁琐
解决: 当查找条件复杂时,要用选择器查找
4.按选择器查找: Selector API:2个:
① 只找一个元素:
var elem=parent.querySelector("selector");
② 找多个元素:
var elems=parent.querySelectorAll("selector");
强调:1. 可在任意父元素上调用
2. 不仅查找直接子元素,且查找所有(符合条件的)后代元素
3.返回非动态集合
非动态集合: 实际存储数据,即使反复访问,也不会导致反复查找DOM树(遍历时不用缓存length)
4. 受制于当前浏览器的兼容性要求(IE8以下不支持)
this关键字:自动获得 当前触发事件的元素对象(在函数中时,用于回调函数或有实参的函数)
if('querySelector' in document){ //检查此函数是否存在
document.querySelector("#id");
}else{
document.getElementById("id");
}
var elems = parent.querySelectorAll("selector");
elems是NodeList对象,无法使用一些属于数组的函数,可将elems转换为数组,就能使用这些函数
方法① var arrayElements = [].slice.call(elems);
方法② var arrayElements = Array.from(elems);
_______________________________________________________________________________________________
笔试:
按HTML查找 VS 按Selector API 选择器查找
① 返回值: 按HTML查找,返回动态集合
按Selector API 选择器查找,返回非动态集合
② 效率: 首次查找HTML效率高
Selector API 查找稍慢
③ 易用性: HTML查找更繁琐
Selector API 查找更简洁
总结:1. 如果已经获得一个元素,找周围元素用节点间关系查找
2. 如果通过一个条件就可找到想要的元素,用HTML查找
3. 如果查找条件复杂时,用Selector API 选择器查找
总结返回值:
1. 凡是返回一个元素的API,如果没找到,都返回null
2. 凡是返回多个元素的API,如果没找到,都返回空元素的集合
_______________________________________________________________________________________________
过渡动画: 不要用display
① js中: 用支持过渡的css属性修改样式:width、height、opacity...
② css中: 起始样式;过渡的新属性;transition:all .5s linear...
凡是带transition的元素,无论以任何手段修改样式属性值,都自带过渡效果
测速比较:
console.time("getChildren1"); //开始:time() 和 结束:timeEnd()里的参数必须相同才能计算时间
getChildren1(document.body);
console.timeEnd("getChildren1");
console.time("getChildren2");
getChildren2(document.body);
console.timeEnd("getChildren2");
第二章*****************************************************************************************
修改内容:
.innerHTML: 获取或设置元素 开始标签到结束标签之间的HTML代码片段(设置会清除旧元素)
单标签无返回值,没有标签的文本节点返回undefined
不包括本身的标签,但包括子标签及其属性(转义字符和正文会相互转化)
.textContent:获取或设置元素 开始标签到结束标签之间的纯文本内容,单标签无返回值
不包括任何标签、转义字符
① 去掉了内嵌的标签
② 将所有转义字符翻译为正文(而不是标签)
强调: IE8不兼容,用.innerText代替
.value: 专门获得或设置 表单元素的输入内容
注意: var a=input.value;变量string(原始类型),a="";无法改变input的输入内容
_______________________________________________________________________________________________
修改属性:
标准属性: 2种方式访问:
1. 核心DOM: 4个API: 所有属性节点都保存在元素的attributes集合中 ———繁琐
获取指定属性值: elem.getAttribute("属性名") →attribute.nodeValue 属性的值
修改指定属性值: elem.setAttribute("属性名","属性值")
是否包含指定属性: elem.hasAttribute("属性名")
移除指定属性: elem.removeAttribute("属性名")
elem.getAttributeNode("属性名"); →text.nodeValue 返回属性的节点内容,表示指定的属性和值
.value: 返回属性的值
指元素的第几个属性(从0开始): elem.attributes[i];
elem.getAttributeNode(elem.attributes[i]); →elem2.nodeValue? 返回null
2. HTML DOM: 所有标准属性都被提前封装在了元素对象中,可直接用.访问 ———简洁,首选
获取属性值: elem.属性名
修改属性值: elem.属性名="属性值"
是否包含: elem.属性名!="" /*是否为空字符串*/
移除属性: elem.属性名="" /*设置为空字符串*/
注意: div没有标准属性width/height,所以要用style.css属性名
特殊: class属性: class是JS语言的保留字,所以DOM中的class属性被迫改名为className
状态属性: .selected .checked .disabled 属性为boolean类型,无值
不能用核心DOM访问,只能用HTML DOM访问,访问的结果都是boolean类型
比如: checked 属性设置或返回checkbox是否应被选中:elem.checked = true / false
注意: preventDefault 即使是在checked属性被切换的情况下也会阻止浏览器改变输入选择框的选择
自定义扩展属性: 如: data-toggle="dropdown" //大写字母会自动转为小写
1. 核心DOM: ———繁琐
自定义扩展属性不能用HTML DOM访问
2.HTML5语法: ———简洁
定义自定义属性: data-属性名="值"
获取/修改属性值: elem.dataset.属性名="属性值" /*与HTML DOM类似*/
elem.classList 属性返回元素的类名,该属性用于在元素中添加,移除及切换 CSS 类
elem.classList 属性只读,但可用.add("class1","class2")和.remove("myStyle")方法 添加/删除 类名
可添加/删除多个,若已存在则不会添加,删除不存在的类名也不会报错
但不要加空格
elem.classList 返回元素的类名组成的数组
elem.classList.length 属性返回类列表中类的数量(只读)
elem.classList.toggle(class1, true/false) 在元素中切换类名(移除返回false,添加返回true)
第二个参数可选,设置元素是否强制添加或移除类名,不管该类名是否存在(false移除,true添加)
_______________________________________________________________________________________________
修改样式: 2种:
单独修改一个css属性
1. 内联: 写在元素的style属性中的样式
获取或设置内联样式: elem.style.css属性名="值"
注意:都要先找到元素,在使用elem.style.属性="值"来修改属性值,要分成两条语句
强调: ①css属性名都要去横线变驼峰:
比如: background-color->backgroundColor
list-style-type->listStyleType
② 所有css属性名的值都是字符串(需要带单位)
计算前都要先去单位,转数字再计算
问题: 实际开发中可能几乎不包含内联样式,用elem.style可能无法获得任何样式
解决: 凡是读取样式,都要读取计算后的样式: getComputedStyle(elem)
ComputedStyle:计算后的样式: 最终应用到元素上的所有样式的综合,并将相对单位计算为绝对单位
如何:① var style=getComputedStyle(elem) //获得计算后的所有css属性
//无法直接获得img的原尺寸宽高,要等图片加载完成(.onload)后才可以获得
② style.css属性名 //从style中获得想要的css属性
简写为:getComputedStyle(elem).display //获取,不建议修改
强调: 计算后的css属性都是只读,不允许修改,而应使用elem.style.css属性名,进行修改
因为计算后的css属性可能原来是共用的,一旦修改,牵一发而动全身
elem.style.cssText 批量修改元素的style属性
elem.style.cssText += elem.style.cssText + '; 样式'; //避免之前的属性被覆盖,在前面加分号避免浏览器会自动去除最后一个属性的分号
2. 内部/外部样式表: ———危险
3步:1. 找样式表对象: var sheet=document.stylesheets[i]
2. 找cssRule对象: var cssRule=sheet.cssRules[i]
如果是keyframes,就需要继续找下级cssRule
3. 修改cssRule的style下的css属性值: cssRule.style.css属性=值
问题: 一次只能修改一个css属性
解决: 批量修改一个元素的多个class属性
2步: ① 先在css中将多个属性定义为一个class
② 在js中修改元素的className为指定的class
_______________________________________________________________________________________________
2.添加/删除/替换:
添加: 3步:
1.创建空元素: var elem=document.createElement("标签名")
比如: var a=document.createElement("a"),相当于 <a></a>
2.设置关键属性:
比如:a.href="http://tmooc.cn"; a.innerHTML="go to tmooc";
相当于 <a href="http://tmooc.cn "> go to tmooc </a>
3. 将新元素添加到 DOM 树指定父元素下:
一个新元素,必须追加到 DOM 树下指定的父元素下,才能显示出来
3种:末尾追加:parent.appendChild(elem) //最常用
//把页面上现有元素追加到另一个parent中,原先的元素会移动到那个parent中
中间插入:parent.insertBefore(elem,child) /*将新elem 插在旧child前面*/
替换: parent.replaceChild(elem,child)
_______________________________________________________________________________________________
优化: 尽量减少操作 DOM 树(即添加、修改、删除元素)
原理: 页面加载过程: html -> DOM Tree
↓
Render Tree ->layout-> paint(绘制)
↑ 计算每个元素的绝对位置
css -> CSSRules
为什么: 每修改一次DOM树,都会反复触发layout,降低页面响应速度
解决: 2种:
1.如果同时添加父元素和子元素时, 就要先在内存中将子元素添加到父元素中,最后再将拼好的父元素整体一次性添加到DOM树
2. 如果父元素已经在页面上,要同时添加多个平级子元素,就要用文档片段
文档片段: 内存中临时保存多个子节点的虚拟父节点
何时: 要添加多个平级子元素
如何:3步:
① 创建文档片段: var frag=document.createDocumentFragment();
② 将子元素临时追加到frag中: frag.appendChild(elem);(在这之前先创建子元素,并设置关键元素)
//比如: 可以先设置left/top、canvas先画好
③ 将frag整体追加到 DOM 树: parent.appendChild(frag);
说明: frag不会成为实际页面元素,将子元素添加到DOM树后,frag自动释放,不占用页面空间
删除:parent.removeChild(child);
第三章*****************************************************************************************
1.HTML DOM常用对象:
Image: 指代页面上一个img元素
创建: var img=new Image();
指定属性:img.src="banner_01.jpg";
追加: parent.appendChild(img);
图片加载完成再进行操作:
方法① img.onload = function(){ ... }
方法② img.onreadystatechange = function(){ if(this.readyState == "complete"){ ... }
Select: 指代页面上一个<select>元素
属性:sel.selectedIndex 获得当前选中项的下标位置
sel.value 获得当前sel的值(value),如果选中项没有value,则用内容(innerHTML)代替
<select>元素的值就是当前选中项的值
sel.options 获得当前sel下所有option元素的集合
sel.options.length 选项的个数 <=> sel.length
sel.options.length=0 清除所有选项 <=> sel.length=0
sel.options[sel.selectedIndex].text 获取选中项的文本
方法:① sel.add(option) 向sel末尾追加一个option
强调: 不支持文档片段(frag)(用frag时不用.add)
② sel.remove(i) 移除i位置的选项
事件:sel.onchange 当选中项发生改变时触发此事件函数
Option: 指代页面上一个option元素
创建/设置属性:var opt=new Option(text,value) /*内容和值一起创建*/
属性:opt.text代替opt.innerHTML
_______________________________________________________________________________________________
Table:指代一个table元素: 管着行分组: 创建,删除,获取
创建行分组: var thead=table.createTHead();
var tbody=table.createTBody();
var tfoot=table.createTFoot();
删除行分组: table.deleteTHead();
table.deleteTFoot();
获取行分组: table.tHead
table.tBodies[i]
table.tFoot
行分组:tHead tBody tFoot: 管着行tr
创建行: var tr=行分组.insertRow(i);
说明: 如果i位置有行,则原位置的行向后顺移
固定套路: 1.末尾追加新行: 行分组.insertRow();
2.开头插入新行: 行分组.insertRow(0);
删除行: 行分组.deleteRow(i) /*删除当前行分组中下标为i的行*/
获取所有行: 行分组.rows
行:管着格:
创建格: var td=tr.insertCell(i)
固定套路:末尾追加: tr.insertCell()
强调: insertCell只能创建td
删除格: tr.deleteCell(i)
获取格: tr.cells
删除行:2种:
1. 行分组.deleteRow(i)
i是行在行分组内的相对下标位置, 无法直接获得
2. table.deleteRow(i)
i是行在整个table中的绝对下标位置,可直接获得: tr.rowIndex 表示tr在整个表格中的位置
固定套路:删除行: table.deleteRow(tr.rowIndex)
_______________________________________________________________________________________________
三大对话框: 弹出的是窗口(对话框,css/js改变的是body),无法改变标题、样式等
alert(): 警告框
prompt(str1,str2): 输入框
str1: 输入框的提示信息
str2: 文本框的默认值,默认为空
返回值: 点击确定返回文本框中的内容,点击取消返回null
confirm(): 确认框(有确认和取消操作的对话框)
何时:只要执行危险的操作前(提交更新、删除)都要先确认再执行操作
如何:var bool=confirm("提示消息");
如果点确定,就返回true,执行操作
否则,就返回false,不执行操作
Form: 代表页面上一个form元素
获取:var form=document.forms[i/'id'] 获取页面上的第i个form元素或为此id的form元素
属性: form.elements 获得表单中的所有表单元素: input select button textarea
form.elements.length 获得表单中表单元素的个数 <=> form.length
方法:form.submit(); 用程序手动提交表单(定义一个变量,提交前修改其值,防止重复提交)
何时: 只要希望通过自定义的验证去决定是否提交表单: 2种
1. <input type="button"/>(代替input->submit: 自动提交,不验证)在<input type="button"/>的单击事件中自己手动调用form.submit();
2.<input type="submit"/> 用 e.preventDefault() 阻止表单默认提交
事件: form.onsubmit=function(){ }
获取表单中的表单元素:var elem=form.elements[i/id/name]
简写:如果表单元素有name属性: form.name
如果表单元素没有name属性: form.elements[form.length-2] 获得倒数第2个按钮(元素)
方法:elem.focus(); 让elem获得焦点
elem.blur(); 让elem失去焦点
_______________________________________________________________________________________________
2.什么是BOM:Browser Object Model(浏览器对象模型)
BOM: 专门操作浏览器窗口/软件的API(没有标准,浏览器不同)
window:2个角色:
1. 代替ES、NodeJS中的Global充当全局作用域对象
2. 封装所有浏览器内置的/DOM/BOM 的API
window的功能: 打开和关闭窗口,弹出对话框...
包括: history: 保存当前窗口打开后,成功访问过的url的历史记录栈,控制前进后退
location: 保存浏览器正在打开的url的地址
document: 封装页面内容和 DOM API的根对象
navigator: 保存浏览器的配置信息
screen: 保存显示设备的信息
event: 定义事件对象
_______________________________________________________________________________________________
3.打开和关闭窗口:
打开一个新窗口: window.open("url","name")
所有在window下,都可省略window.
name: 内存中每个窗口都有一个唯一的name属性
浏览器规定: 同一个name属性的窗口只能打开1个
后打开的同名窗口会覆盖先打开的
预定义name: _self: 用当前窗口的name打开新窗口
结果: 新窗口替换当前窗口
_blank: 不指定name属性,用空name打开新窗口,让浏览器随机分配name
结果: 每个窗口的name都不一样,就可打开多个
第三个参数可指定窗口样式:
如: var newWindow = window.open("url","_blank",'top=100,left=100,width=300,height=300');
top/left: 窗口顶部距离屏幕顶部/左端的像素数(指定宽高时有效)
width/height: 窗口的宽/高,单位像素
可使用newWindow.close()关闭打开的窗口
打开和关闭窗口: 4种:
1. 在当前窗口打开,可后退:
HTML: <a href="url" target="_self"></a>
js: open("url","_self")
2. 在当前窗口打开,不可后退:
当前窗口每打开一个新url,都会将新url保存在history中
如果新url是追加进history中,则可后退;如果新url将当前url替换掉,则无法后退到此页面(其它正常)
HTML: 无法实现
js:location.replace("url")
3. 在新窗口打开,可打开多个:
HTML: <a href="url" target="_blank"></a>
js: open("url","_blank")
4. 在新窗口打开,只能打开一个:
HTML: <a href="url" target="自定义name"></a>
js: open("url","自定义name")
close(): 关闭页面
窗口大小: 文档显示区的大小:
宽: window.innerWidth 高: window.innerHeight
body的宽、高(最短,不包括border、滚动条): document.body.clientWidth/clientHeight;
屏幕可用工作区宽度(实际宽度): window.screen.availHeight/Width;
_______________________________________________________________________________________________
4.定时器:2种:
周期性定时器:(尽量不要嵌套定时器)
若想要使等待的时间不断变化,只能在回调函数中,先清除定时器,再启动定时器(不要用var)
var reqParam = {
arr: [0, 10000, 60000, 180000, 420000], //请求间隔(0表示倒计时结束立即请求一次)
max: [1, 10, 10, 10, 10], //每种间隔最大请求次数
index: 0, //请求数组的当前下标
count: 0, //当前请求次数
timer: null, //初始化请求次数定时器
countTimer: null, //初始化倒计时定时器
ajaxType: 'jsonp', //发起ajax请求的方式(可在单独页面中定义)
jsonp: 'jsonp_callback', //jsonp跨域函数名
countdown: true, //是否倒计时
desc: '等待开奖', //不显示分、秒时的字段
jumpIssue: true //是否允许跳期(在render中使用)
};
var req= function(url1, url2){ //计算请求次数的函数
reqParam.count++;
if(reqParam.count >= reqParam.max[reqParam.index]){ //请求次数到达限定次数,此间隔结束
clearInterval(reqParam.timer);
requestData(url1, url2); //清理定时器之后,立即请求一次(本轮的最后一次)
console.log("请求数组的当前下标:"+reqParam.index+"\n"+
"当前请求次数:"+reqParam.count+"\n"+"当前间隔时间:"+reqParam.arr[reqParam.index]);
reqParam.index++;
if(!reqParam.arr[reqParam.index]) return; //reqParam.arr中的请求完毕后,不再进行请求
reqParam.count = 0;
reqParam.timer= setInterval(function(){ //清除周期性定时器后,需要重新调用
req(url1, url2); //回调自己,进行请求
}, reqParam.arr[reqParam.index]);
}else{
requestData(url1, url2); //只在此处和阶段循环结束时请求数据
console.log("请求数组的当前下标:"+reqParam.index+"\n"+
"当前请求次数:"+reqParam.count+"\n"+"当前间隔时间:"+reqParam.arr[reqParam.index]);
}
}
function requestData(url1, url2){ //url1: 获取多期,url2: 获取一期
$.ajax({
url: url1,
data: window.param, //传入相应请求参数
dataType: reqParam.ajaxType,
jsonp: reqParam.jsonp,
success: function(res){
if(typeof res !== 'object'){
res = $.parseJSON(res);
}
var countTimes = res.data.issueTime.recentDrawTime; //倒计时
if(countTimes < 0 && window.param.num === 1)
return false; //后续请求时,请求到的倒计时小于0,相当于没请求成功
clearInterval(reqParam.timer); //请求成功后,需要第一时间清除定时器
reqParam.index = 0;
reqParam.count = 0;
varrand= 1000; //用于随机数
if(typeof render === 'function'){ //渲染数据(并在其中防止数据重复)
render(res.data);
}
reqParam.countTimer= setInterval(function(){
countTimes--; //每秒减1进行倒计时
if(countTimes > 0){
if(reqParam.countdown){
var minute = parseInt(countTimes / 60);
var seconds = parseInt(countTimes % 60);
minute = minute >= 10 ? minute : '0' + minute;
seconds = seconds >= 10 ? seconds : '0' + seconds;
$('#timing').text(minute+':'+seconds);
}
}elseif(countTimes <= 0){
clearInterval(reqParam.countTimer); //倒计时结束,清除定时器
$('#timing').text(reqParam.desc);
rand*= parseInt(Math.random()*(10 + 1)); //随机0-10秒
rand+= res.data.issueTime.queryTime*1000; //倒计时完毕后的查询时间
setTimeout(function(){
console.log('随机和查询共:'+rand/1000+'秒');
if(window.param.num){
window.param.num = 1; //当url1和url2相同时,修改window.param里的参数即可
}
req(url2, url2); //请求一次成功后,将url1替换为url2
}, rand)
}
}, 1000)
}
})
}
$(function(){
requestData(url1, url2); //第一次请求数据不用定时器,所有js加载完毕后开始请求
})
什么是: 让程序每隔一段时间间隔反复执行一项任务(可在定时器中改变某个变量)
何时: 只要让程序每隔一段时间间隔反复执行一项任务
如何: 3件事:
1.任务函数: function task(){...}
定义了定时器每次要执行的任务,可用if判断临界值,执行操作(如停止定时器)
停止当前定时器要把函数放在定时器内,不能单独定义
2.启动定时器: var timer=setInterval(task,间隔(ms)) //不要多次声明(var),可以直接用(=)
强调:task是回调,不用加(),要传参数时可以把要执行的函数放在回调函数内
其中: timer是定时器在内存中唯一标识的一个整数序号(从1开始)
何时: 只要一个定时器可能被停止,都要在启动时先保存序号(var timer=...)
3.停止定时器: clearInterval(timer)
其中: timer是要停止的定时器序号
问题: 停止定时器不会自动清空timer变量中的序号,有可能影响下次启动
解决: 建议先停止定时器时,再手动清空timer(timer=null)
2种方式:① 手动停止: 用户通过操作来停止定时器(用timer!=null判断启动/停止)
② 自动停止: 在任务函数内反复判断临界值
只要达到临界值,就自动停止定时器
一次性定时器:
什么是: 让程序先等待一段时间,再自动执行一次任务,执行一次后自动停止
何时: 只要让程序先等待一段时间,再自动执行一次任务
如何: 3件事: 1. 任务函数: function task(){...}
2. 启动定时器:var timer=setTimeout(task,等待ms)
3. 停止定时器:clearTimeout(timer);
用于在任务执行前,取消等待,不再执行
window.onload: 当页面中所有内容(html,css,js)都加载完,才自动触发onload事件
原理: 定时器中的回调函数(task)只能在主程序所有语句执行完才能开始执行
笔试:
var a=10;
setTimeout(function(){console.log(a);},0); //先算a++,再输出11
a++;
注意:js中所有的异步回调函数(如定时器、ajax)都在正常代码(非异步代码)执行完之后才执行
原因: JavaScript是单线程执行的
注意:① 定时器有一个最小执行时间,IE8及以下最小15.6ms,其它最少4ms
② 定时器中的this默认指向window,可使用bind()改变回调函数中的this
③ 定时器的第3、4...个参数表示第一个参数(回调函数)传入的参数
④ 微信内置浏览器中,等待时间为0时,定时器可能会失效,建议改为10ms