JS(下)

十二、JavaScript的DOM特效

在web浏览器上,window对象是一个全局对象,代表web浏览器中一个打开的窗口(包括选项卡、地址栏、收藏夹、和网页等),所以,每个打开的窗口都是一个window对象,document是window对象的一个属性,由于document属性的取值是一个对象,所以document属性是一个对象类型的属性,其取值叫做document对象,document对象是一个web浏览器宿主对象,代表当前窗口中的整个网页,保存了网页上所有的内容,通过document对象就可以操作网页上的内容了,DOM的全称是Document Object Model(文档对象模型),代表了document对象(整个网页)的树状结构的模型,DOM 定义了访问和操作 HTML文档(网页)的标准方法,所以,学习DOM就是学习如何通过document对象操作网页上的内容,在JS中,HTML标签就叫做DOM元素,使用document的时候前面可以省略window,document对象调用它的getElementById方法可以返回在此页面上所找到的传入id名称字符串所指代的DOM元素,由于id名称不可以重复,所以当找到了此DOM元素时,返回值是一个将此DOM元素包装成的对象(是被浏览器宿主对象HTMLDivElement包装的),当找不到此DOM元素时,返回值是null,例如“let oDiv = document.getElementById (“box”);”,由于类名、name值、和标签名称是可以重复的,所以document对象调用它的getElementsByClassName/getElementsByName/getElementsByTagName方法可以返回在此页面上所找到的传入类名/name值/标签名称字符串所指代的所有DOM元素所组成的伪数组,当找不到此DOM元素时,返回值是一个空数组,但getElementsByName方法在不同web浏览器中工作方式是不同的,在IE和Opera中,所返回的数组中还包括那些id属性为指定值的DOM元素,document对象调用它的querySelector/querySelectorAll方法(推荐)可以返回在此页面上所找到的传入选择器(标签/id/类/后代选择器...)字符串所指代的首个DOM元素/所有DOM元素所组成的伪数组,当找不到此DOM元素时,返回值是null/空数组,例如“let oDiv = document.querySelector (“#box”);”,“let oDiv = document.querySelector (“div>form”);”,“let oDivs = document.querySelectorAll (“.father”);”,DOM元素访问它的tagName属性可以返回此DOM元素的名称字符串,DOM元素访问它的children/childNodes属性可以返回此DOM元素内部所有子DOM元素/节点对象所组成的伪数组(节点包括DOM元素节点、属性节点、和文本节点,但这里的“子节点对象”指的是DOM元素和文本对象),例如“let oDiv = document.querySelector (“div”);  console.log (oDiv.children);”,节点对象访问它的nodeType属性可以返回此节点类型的数值代号,1代表DOM元素,3代表文本,例如“for (let node of oDiv.childNodes) {console.log (node.nodeType);}”,数值1可以用“Node.ELEMENT_NODE;(//返回值是1)”来表示,节点对象访问它的nodeName/nodeValue属性可以返回此节点的节点名称/取值字符串(nodeName属性的返回值可以是DOM元素名称/属性节点字符串/“#text”,nodeValue属性的返回值可以是null/属性节点值/“文本内容”),DOM元素访问它的firstChild/lastChild属性可以返回此DOM元素内部的首个/最后一个子节点对象,例如“let oDiv = document.querySelector (“div”);  console.log (oDiv.firstChild);”,DOM元素访问它的firstElementChild/lastElementChild属性可以返回此DOM元素内部的首个/最后一个子DOM元素,DOM元素访问它的parentElement/parentNode属性可以返回此DOM元素的父DOM元素/节点对象,但在火狐9之前,只支持parentNode属性,不支持parentElement属性,所以,当想要做兼容性处理时,示例为“let oUl = oLi.parentElement || oLi.parentNode;”,DOM元素访问它的previousSibling/nextSibling属性可以返回此DOM元素的上/下一个节点对象,DOM元素访问它的previousElementSibling/nextElementSibling属性可以返回此DOM元素的上/下一个DOM元素,document对象调用它的createElement方法可以返回所创建的传入DOM元素名称字符串所指代的DOM元素,格式为“let oDiv = document.createElement (“div”);”,DOM元素调用它的appendChild方法可以在此DOM元素内部的最后位置添加所传入的DOM元素,例如“oUl.appendChild (oLi);”,DOM元素调用它的insertBefore方法可以将参数1DOM元素添加到此DOM元素内部的参数2DOM元素的前面,格式为“oUl.insertBefore (oLi1, oLi2);”,DOM元素调用它的removeChild方法可以删除此DOM元素内部所传入的子DOM元素,格式为“oDiv.removeChild (oP);(//当不传参/删除不存在的DOM元素时,就会报错,当想要让某个DOM元素自我删除时,可以让此DOM元素的parentElement/parentNode对象调用它的removeChild方法,并传入此DOM元素)”,DOM元素调用它的cloneNode方法可以返回被克隆的此DOM元素(当传入true时包括此DOM元素内部的子DOM元素,当传入false/不传参时则不包括),由于调用querySelector/createElement方法所返回/创建的DOM元素,系统都会将它包装成一个对象,并且会将它的属性和方法都添加给这个对象,所以,可以利用DOM元素来访问/调用它的属性/方法,通过“console.dir ();”可以在控制台以目录结构的方式显示一个对象所有的属性和方法,DOM元素访问它的属性节点名称可以返回/修改/删除此DOM元素系统自带属性节点的取值(当访问的是class属性时,要改写成className,这是因为class在ES6中是一个用于定义类的声明符,所以为了避免产生冲突就改用了className作为JS属性的名称,对于取值跟属性相同的属性节点,当给它赋值true时,就代表新增此属性节点,当给它赋值false时,就代表删除此属性节点),格式为“console.log (oDiv.title);”,“oDiv.title = “hello world”;(//没有就新增)”,“oDiv.title = “”;(//只会删除取值,属性节点还在)”,DOM元素调用它的getAttribute/setAttribute/removeAttribute方法可以返回/修改/删除此DOM元素的传入系统自带/自定义属性节点字符串的取值,格式为“console.log (oDiv.getAttribute (“title”) );(//另外,当给某个DOM元素设置了data-xxx这种形式的自定义属性节点时,可以使用“oDiv.dataset.xxx;”的形式返回此属性节点的取值)”,“oDiv.setAttribute (“title”, “hello world”);(//没有就新增)”,“oDiv.removeAttribute (“title”);(//会将属性节点和取值同时删除)”,DOM元素访问它的innerHTML/innerText属性既可以返回此DOM元素内部包含/不包含两端空格和HTML标签的内容字符串,也可以给此DOM元素设置包含HTML标签/普通(即出现HTML标签时会当成普通的字符串)的内容字符串,DOM元素访问它的textContent属性既可以返回此DOM元素内部包含两端空格但不包含HTML标签的内容字符串,也可以给此DOM元素设置普通的内容字符串,DOM元素访问它的style属性可以返回此DOM元素的行内样式对象,此行内样式对象访问它的行内样式属性名称可以返回/设置此DOM元素相应行内样式属性的取值字符串(优先级最高,兼容任何浏览器,当设置时,取值别忘了加引号和单位名称,当行内样式属性的名称带有中划线时,要改成驼峰命名),格式为“oDiv.style.lineHeight = “30px”;”,或者我们可以先用一个自定义类名设置好CSS样式,再利用DOM元素访问它的className属性来给此DOM元素设置此类名即可,DOM元素访问它的classList属性可以返回一个只读的DOMTokenList 对象(包含了此DOM元素所有类名的对象),DOMTokenList对象调用它的add/remove/toggle方法可以给调用者添加/删除一/多个所传入的类名字符串/切换add和remove的状态,当调用者拥有所传入的类名时,add方法不会重复去添加,DOMTokenList对象调用它的item方法可以返回传入索引值所对应的类名字符串,当索引值在区间范围之外时,返回值是null,DOMTokenList对象调用它的contains方法可以判断调用者是否拥有所传入的类名字符串,返回值是true/false,window对象调用它的getComputedStyle方法或DOM元素访问它的currentStyle属性都可以返回此DOM元素的样式对象,格式为“getComputedStyle (oDiv);(//只支持高级浏览器)”,“oDiv.currentStyle;(//只支持低级浏览器)”,此样式对象访问此DOM元素的行内样式/CSS属性名称只可以返回(只读属性,无法设置)相应属性的取值字符串,格式为“console.log (getComputedStyle (oDiv).lineHeight);(//只支持高级浏览器)”,“console.log (oDiv.currentStyle.lineHeight);(//只支持低级浏览器)”,offset、client、和scroll类的JS属性统称为JS的三大家族,DOM元素访问它的offsetWidth/offsetHeight属性只可以返回(只读属性,无法设置)此DOM元素整体(即border+padding+内容)的宽/高度值(无论是通过行内样式还是CSS属性设置的,兼容任何浏览器),DOM元素访问它的offsetLeft/offsetTop属性可以返回此DOM元素的左/上边缘与其最近的定位流上级DOM元素的左/上边缘的距离值(即“偏移位”值),当其不存在定位流的上级DOM元素时,就以<body>元素作为默认的参考点(注意不存在offsetRight/offsetBottom属性),DOM元素访问它的offsetParent属性可以返回跟此DOM元素最近的定位流的上级DOM元素,当其不存在定位流的上级DOM元素时,返回值是body对象,DOM元素访问它的clientWidth/clientHeight属性可以返回此DOM元素除了border部分以外整体的宽/高度值,DOM元素访问它的clientLeft/clientTop属性可以返回此DOM元素border-left/top-width的数值(注意不存在clientRight/clientBottom属性),DOM元素访问它的scrollWidth/scrollHeight属性可以返回此DOM元素的clientWidth/clientHeight值与超出clientWidth/clientHeight范围的内容的宽/高度值之和,DOM元素访问它的scrollLeft/scrollTop属性可以返回此DOM元素的横/竖向滚动条滚出的距离值,document对象访问它的compatMode属性可以返回此html网页在此web浏览器上的渲染模式字符串,“CSS1Compat”代表标准模式(默认),“BackCompat”代表混杂(怪异)模式,当网页没有书写文档声明时,就会按照混杂(怪异)模式来进行渲染,window对象访问它的innerWidth/innerHeight属性可以返回此web浏览器窗口当前的宽/高度值,可随窗口大小的变化而变化,当存在滚动条时,滚动条的宽度值也算在内,支持高级浏览器的各种模式,document对象访问它的documentElement属性可以返回<html>元素(即整个网页),<html>/<body>元素访问它的clientWidth/clientHeight属性也可以返回此web浏览器窗口当前的宽/高度值,可随窗口大小的变化而变化,当存在滚动条时,滚动条的宽度值不算在内,例如“document.documentElement.clientWidth;(//支持低级浏览器的标准模式)”,“document.body.clientWidth;(//支持低级浏览器的混杂模式)”,当想要做兼容性处理时,可以自己封装一个获取此web浏览器窗口当前的宽/高度值的函数,例如“function getScreen () {let width, height;  if (window.innerWidth) {width = window.innerWidth;  height = window.innerHeight;} else if (document.compatMode === “BackCompat”) {width = document.body.clientWidth;  height = document.body.clientHeight;} else {width = document.documentElement.clientWidth;  height = document.documentElement.clientHeight;}  return {width: width, height: height} }”,window对象访问它的pageXOffset/pageYOffset属性可以返回此web浏览器窗口的横/竖向滚动条滚出的距离值(支持高级浏览器的各种模式),<html>/<body>元素访问它的scrollLeft/scrollTop属性可以返回/设置此web浏览器窗口的横/竖向滚动条滚出的距离值,例如“document.documentElement.scrollTop;(//支持低级浏览器的标准模式)”,“document.body.scrollTop;(//支持低级浏览器的混杂模式)”,当想要做兼容性处理时,可以自己封装一个获取此web浏览器窗口的横/竖向滚动条滚出的距离值的函数,例如“function getPageScroll () {let x, y;  if (window.pageXOffset) {x = window.pageXOffset;  y = window.pageYOffset;} else if (document.compatMode === “BackCompat”) {x = document.body.scrollLeft;  y = document.body.scrollTop;} else {x = document.documentElement.scrollLeft;  y = document.documentElement.scrollTop;}  return {x: x, y: y} }”,window对象调用它的scrollTo方法可以分别让此web浏览器窗口的横竖滚动条滚出所传入的距离值,格式为“window.scrollTo (x, y);(//x表示横向距离值,y表示竖向距离值)”,在谷歌浏览器中,默认情况下不允许在播放本地<audio>文件时进行播放进度的跳转,也就是在播放本地<audio>文件时设置其currentTime值是无效的,只有把应用程序部署到web服务器,然后通过web服务器的URL打开此html网页,然后再设置相应<audio>文件的currentTime值才有效(也就是说只有在http协议下才有效),默认情况下也不允许自动播放<audio>/<video>,只有用户和网页交互之后才可以播放<audio>/<video>,但我们可以通过修改谷歌浏览器的设置来自动播放<audio>/<video>,先在地址栏输入“chrome://flags/”并敲回车,然后在搜索框输入“autoplay”并敲回车,最后把“Autoplay policy”选项的设置改为“no user gesture is required”即可,或者给<video>设置muted属性也可以解决此问题(经测试只适用于<video>,<audio>无效,谷歌浏览器这么设置估计是因为网页上面的垃圾广告实在是太多了,只有这样才能最大程度的避免用户被打扰到),<audio>/<video>元素调用它的play/pause方法可以开始/暂停播放此<audio>/<video>,<audio>/<video>元素访问它的duration属性可以返回此<audio>/<video>的总秒数值(可以是小数),<audio>/<video>元素访问它的currentTime属性可以返回/设置此<audio>/<video>当前播放进度的秒数值(可以是小数),<audio>/<video>元素访问它的volume属性可以返回/设置此<audio>/<video>的音量(取值范围是0~1,0代表静音,1代表音量最大),video元素调用它的requestFullscreen方法可以将此video全屏显示,但是有兼容性问题,可以上百度搜索“requestFullscreen mdn”,然后进入此mdn官方文档,将他封装好的兼容各浏览器的函数复制粘贴过来,直接调用即可,<input>输入框元素访问它的value属性可以返回/设置此单行文本输入框中的内容字符串,<input>输入框元素调用它的focus/blur方法可以赋予/移除此单行文本输入框的光标,document对象访问它的readyState属性可以返回此html页面载入状态的字符串,“uninitialized”代表还未开始载入,“loading”代表载入中,“interactive”代表已加载,此HTML文档与用户可以开始交互,“complete”代表载入完成,用户和浏览器之间的交互行为就叫做事件,比如“点击”、“移入”、“移出”等,在JS中所有的DOM元素都可以绑定事件,当想要移除所绑定的事件时,只需再次给此事件赋值null/undefined就可以了,通过赋值的方式绑定事件的格式为“DOM元素/window对象.带有on的事件名称 = function () {可执行的代码;};(//后绑定的同名事件会覆盖先绑定的同名事件)”,当所对应的事件被触发时,就会执行后面的回调函数,由于事件就类似于一个方法,所以也可以手动通过“DOM元素.事件名称 ();”的形式去触发,DOM元素/window对象调用它的addEventListener/attachEvent方法也可以给此DOM元素/浏览器窗口绑定事件,并且后绑定的同名事件不会覆盖先绑定的同名事件,格式为“oBtn.addEventListener (“去掉on的事件名称”, function () {可执行的代码;});(//只支持IE9及以上的高级浏览器,并且当绑定多个同名事件时,触发的顺序是正向的)”,“oBtn.attachEvent (“带有on的事件名称”, function () {可执行的代码;} );(//只支持IE9以下的低级浏览器,并且当绑定多个同名事件时,触发的顺序是逆向的)”,当想要做兼容性处理时,可以自己封装一个绑定事件的函数,例如“function addEvent (ele, name, fn) {if (ele.attachEvent) {ele.attachEvent (“on”+ name, fn);} else {ele.addEventListener (name, fn); } }”,DOM元素/window对象调用它的removeEventListener方法可以移除通过调用addEventListener方法所添加的事件,但addEventListener方法的事件监听器必须使用外部函数,例如“oBtn.addEventListener (“click”, myFunction);”,“oBtn.removeEventListener (“click”, myFunction);”,一个事件被触发需要经过三个阶段,事件捕获阶段、当前目标阶段、和事件冒泡阶段,当此DOM元素的父DOM元素也绑定了此事件时,会首先捕获到此事件的触发,这就叫做事件捕获阶段,然后再将此事件传递给此DOM元素并触发,这就叫做当前目标阶段,最后再将此事件抛出去,先抛给它绑定了此事件的父DOM元素并触发,然后再向上一级抛,以此类推,IE6浏览器会一直抛到document对象为止,其他浏览器会一直抛到window对象为止,这就叫做事件冒泡阶段,有些事件是不能冒泡的,比如blur、focus、load、和unload事件,由于早期各大web浏览器厂商抢占市场,对事件的理解又不同,所以导致了这三个阶段只有两个会同时执行,要么捕获和当前,要么当前和冒泡,后来W3C为了兼容,将两种方式都纳入了标准,当调用addEventListener方法绑定事件时,可以传入三个参数,第三个参数是false/true,false代表冒泡,true代表捕获,当通过赋值的方式绑定事件时,由于无法传入任何参数,所以默认就是冒泡,当调用attachEvent方法绑定事件时,由于只能传入两个参数,所以默认也是冒泡,使用最多的就是事件冒泡,当想要阻止事件冒泡时,就在回调函数作用域中添加一句“event.stopPropagation ();(//只支持高级浏览器)”/“event.cancelBubble = true;(//兼容任何浏览器)”即可,当所绑定的事件被触发时,系统自动创建的对象就叫做事件对象,在IE9及以上的高级浏览器中,系统会将事件对象当做实参传入回调函数的作用域中,而在IE9以下的低级浏览器中则不会,需要在回调函数的作用域中通过“window.event;”来获取(返回)事件对象,所以,获取事件对象的兼容性写法的格式为“oBtn.onclick = function (event) {event = event || window.event;}”,在JS中,由于a和input按钮等元素被系统绑定了默认事件,所以当我们手动给某个DOM元素绑定了跟系统同名的事件时,手动绑定的不会覆盖掉跟系统同名的,当想要覆盖掉时(即阻止默认行为/移除默认事件),就在回调函数作用域的结尾处添加一句“event.preventDefault ();(//只支持高级浏览器)”/“event.returnValue = false;(//只支持低级浏览器)”/“return false;(//兼容任何浏览器,推荐)”即可,事件对象访问它的target属性可以返回触发此事件的DOM元素/其内部的相应子DOM元素,可以实现用父DOM元素来监听子DOM元素,事件对象访问它的offsetX/offsetY属性可以返回触发此事件的点与此DOM元素左/上边缘的距离值,事件对象访问它的clientX/clientY属性可以返回触发此事件的点与网页可视区域(不包括滚动条滚出的区域)的左/上边缘的距离值,事件对象访问它的pageX/pageY属性可以返回触发此事件的点与整个网页(包括滚动条滚出的区域)的左/上边缘的距离值(只支持高级浏览器),事件对象访问它的screenX/screenY属性可以返回触发此事件的点与显示器的左/上边缘的距离值(不是很常用),给DOM元素所绑定的onclick事件的触发条件是此DOM元素被用鼠标左键单击,例如“oA.onclick = function () {alert (“<a>被点击了”);  return false;};”,给DOM元素所绑定的ondblclick事件的触发条件是此DOM元素被用鼠标左键双击,给<body>/<input>输入框元素所绑定的onkeyup/onkeydown事件的触发条件是此键盘上的某个按键被松开/按下,例如“document.body.onkeydown = function (event) {console.log (event.keyCode);  console.log (event.key);};(//此时形参event所接收到的事件对象是键盘上被按下的那个按键对象,按键对象访问它的key属性可以返回被按下的那个键子名称的字符串,按键对象访问它的keyCode属性可以返回被按下的那个键子名称的十进制Unicode数值即键码值)”,给DOM元素所绑定的onmouseover/onmouseout事件的触发条件是鼠标移入/出此DOM元素,但无法阻止事件冒泡,给DOM元素所绑定的onmouseenter/onmouseleave事件的触发条件是鼠标移入/出此DOM元素,可以阻止事件冒泡(推荐),给DOM元素所绑定的onmousemove事件的触发条件是鼠标在此DOM元素中移动,给DOM元素/window对象所绑定的onscroll事件的触发条件是此DOM元素/浏览器窗口的滚动条发生滚动,给DOM元素所绑定的onmousedown事件的触发条件是在此DOM元素内鼠标任意按键被按下,给DOM元素/document对象所绑定的onmouseup事件的触发条件是在此DOM元素/页面内鼠标任意按下的按键被松开,给<input>输入框元素所绑定的onfocus/onblur事件的触发条件是此单行文本输入框获取/失去了焦点,给<input>输入框元素所绑定的onchange事件的触发条件是此单行文本输入框中的内容发生了改变,并且此单行文本输入框失去了焦点,但只有手动输入的才有效,使用JS中的value属性给此单行文本输入框所添加的无效,给<input>输入框元素所绑定的oninput/onpropertychange事件的触发条件是此单行文本输入框的内容发生了改变(实时监听),但只有手动输入的才有效,使用JS中的value属性给此单行文本输入框所添加的无效,oninput事件只支持高级浏览器,onpropertychange事件只支持低级浏览器,给window对象所绑定的onresize事件的触发条件是此web浏览器窗口的宽/高度发生了变化,给window对象/<img>元素所绑定的onload/onerror事件的触发条件是整个页面的DOM元素/此img及相关资源加载成功/失败,当想要解决流式布局中此页面上所createElement的<img>元素的重叠问题时,有两种方式,第一种是等oImg.onload之后再流式布局,第二种是首先通过JSON获取到<img>的宽高,然后在createElement<img>元素的时候直接设置<img>元素的宽高,给document对象所绑定的DOMContentLoaded事件的触发条件是整个页面的DOM元素加载完毕(此事件只能通过调用addEventListener方法来绑定,只支持高级浏览器,性能优于onload事件),给document对象所绑定的onreadystatechange事件的触发条件是document对象的readyState值发生了改变(此事件只能通过调用attachEvent方法来绑定,不用去掉on,只支持低级浏览器),给<audio>/<video>元素所绑定的oncanplay事件的触发条件是此<audio>/<video>预加载完毕,可以开始播放了,给<audio>/<video>元素所绑定的ontimeupdate事件的触发条件是此<audio>/<video>的播放进度发生了更改,给<audio>/<video>元素所绑定的onended事件的触发条件是此<audio>/<video>播放完毕,给<audio>/<video>元素所绑定的ondurationchange事件的触发条件是此<audio>/<video>的总时长发生变化(由于<audio>/<video>一旦预加载完毕,其总时长就会由NaN变为其实际的总时长,所以当ondurationchange事件被触发时,此<audio>/<video>一定预加载完毕了,可以开始播放了,当想要做兼容性处理时,可以使用ondurationchange事件代替oncanplay事件,详见Player.vue),给DOM元素所绑定的ontouchstart事件的触发条件是在移动端此DOM元素内手指被按下,给DOM元素所绑定的ontouchend事件的触发条件是在移动端此DOM元素内按下的手指被抬起,给DOM元素所绑定的ontouchmove事件的触发条件是在移动端此DOM元素内手指被按下并移动,移动端这三个touch类事件有一个事件对象,此事件对象访问它的touches属性可以返回此屏幕上所有手指对象所组成的伪数组,此事件对象访问它的targetTouches属性可以返回此DOM元素上所有手指对象所组成的伪数组,此事件对象访问它的changedTouches属性可以返回此屏幕上刚刚增加/减少的手指对象所组成的伪数组,每个手指对象都有clientX/clientY属性、pageX/pageY属性、和screenX/screenY属性,在一个DOM元素覆盖了另一个DOM元素,并且给上面的DOM元素绑定了touchstart事件,给下面的DOM元素绑定了click事件时,由于click事件的触发有100~300ms的延迟(即在你click完100~300ms之后,系统才会感知到你click了),而touchstart事件却没有,所以一旦手指触摸到了上面的DOM元素,在触发上面的DOM元素所绑定的touchstart事件之后,当上面的DOM元素消失时,也会触发下面的DOM元素所绑定的click事件(当上面的DOM元素不消失时,就不会触发下面的DOM元素所绑定的click事件),这就叫做移动端的点透问题(即事件扩散),此问题的解决方案一,在给上面的DOM元素绑定touchstart事件的时候,在回调函数作用域的结尾处添加一句“event.preventDefault ();”来阻止事件扩散,解决方案二,是引入Zepto.js类库,然后使用tag事件代替click事件,但是需要注意老版本的Zepto.js类库也会出现点透问题,解决方案三,是引入fastclick.js插件,这是一个基于JS的插件,网址是“github.com/ftlabs/fastclick”,此网页的“Usage”部分就是使用方法,这是最早解决点透问题的插件,此插件里的click事件并不是原生JS的click事件,已经解决了100~300ms延迟的问题,在JS中,有重复执行和只执行一次的定时器,全局对象调用它的setInterval/setTimeout方法可以启动重复执行/只执行一次的定时器,格式为“setInterval (function () {重复执行的代码;}, 毫秒值);(//意思是每隔多少毫秒执行一次代码)”,“setTimeout (function () {只执行一次的代码;}, 毫秒值);(//意思是多少毫秒后执行代码)”,这两种方法的返回值都是此定时器唯一的标识符,全局对象调用它的clearInterval/clearTimeout方法可以关闭相应重复执行/只执行一次的定时器,格式为“clearInterval (setInterval方法的返回值)”,“clearTimeout (setTimeout方法的返回值);”,例如“startBtn.onclick = function () {id = setInterval (function () {console.log (“随便写点”);}, 1000);}  closeBtn.onclick = function () {clearInterval (id);}”,函数防抖和节流都是用来优化高频率执行的JS代码的一种手段,可以让高频触发的事件(比如oninput/onmousemove/onscroll/onresize等事件)在触发过程中,减少事件监听器的执行次数,节省网页的性能,函数防抖可以在连续的高频操作时,让事件监听器只执行一次,其核心思想是当所绑定的事件被触发时,会立即开启一个setTimeout,只要还未到setTimeout所设定的毫秒值,下一次所绑定事件的触发就会clearTimeout上一次的setTimeout,并开启一个新的setTimeout,以此类推,直到停手之后,到达setTimeout所设定的毫秒值,事件监听器才会执行,其实,函数防抖说白了就是当所绑定的事件被触发时,等待n毫秒再执行事件监听器,在等待时间内当此事件再次被触发时,就重置等待时间,使用场景是在使用<input>进行自动搜索时,当我们高频输入文字时,等到我们输入完毕之后,等待n毫秒再调用后端接口,防止调用频率过高而导致web浏览器出现延迟/假死/卡顿的现象,我们可以自己封装一个防抖函数debounce,其返回值是一个防抖回调函数,我们可让此返回值充当oninput/onmousemove/onscroll/onresize等事件的回调函数,例如“function debounce (fn, delay) {let timerId = null;  return function () {timerId && clearTimeout (timerId);  timerId = setTimeout (() => {fn.apply (this, arguments);}, delay || 1000);}; }(//修改fn的arguments的意义是,在我们手动通过“DOM元素.事件名称 ();”的形式去触发某个事件时,当传入了一些参数时,可以使函数作用域中的arguments保持一致,也可以使我们心目中真正的回调函数fn通过一个形参e就可以自动接收到事件对象,因为apply方法是通过数组/伪数组的方式进行传参的)”,函数节流可以在连续的高频操作时,减少事件监听器的执行次数,其核心思想是当所绑定的事件被触发时,会立即开启一个setTimeout,在setTimeout所设定的毫秒值之内,下一次所绑定事件的触发既不会clearTimeout上一次的setTimeout,也不会开启一个新的setTimeout,直接就return,直到到达setTimeout所设定的毫秒值,事件监听器执行完毕之后,当再次触发所绑定的事件时,才会clearTimeout上一次的setTimeout,并开启一个新的setTimeout,以此类推,我们可以自己封装一个节流函数throttle,其返回值是一个节流回调函数,我们可让此返回值充当oninput/onmousemove/onscroll/onresize等事件的回调函数,例如“function throttle (fn, delay) {let timerId = null;  let flag = true;  return function () {if (!flag) return;  flag = false;  timerId && clearTimeout (timerId);  timerId = setTimeout (() => {flag = true;  fn.apply (this, arguments);}, delay || 1000);}; }”,定义在函数/块级作用域中的函数就叫做闭包,正常来讲,当一个定义在函数/块级作用域中的变量没有被访问时,就会被系统释放掉,由于闭包能够访问到它,所以闭包可以延长它的生命周期,当后续不需要此闭包时,一定要手动将它的值设置为null,否则会出现内存泄漏,在for循环的小括号中使用let声明符所定义的变量属于父块级作用域中的局部变量,并且每次子块级作用域中的循环体执行完毕之后,都会在一个全新的父块级作用域中重新定义此变量,例如“{let i = 0;  {循环体;} }”,由于每一个块级作用域都是独立的、互不干扰的,所以使用let声明符可以实现for循环所遍历到的索引值被各个按钮所绑定事件的回调函数作用域同步访问,简称for循环索引同步,但是当我们将let换成var时,由于var声明符不识别块级作用域,所以变量i中所保存的索引值会被逐次覆盖,虽然for循环遍历索引值和各个按钮的事件绑定可以同步执行,但是所绑定事件的回调函数却是异步执行的,所以当所绑定事件的回调函数执行时,在其函数作用域中所访问到的变量i的值以最后一个为准,只有形成闭包才能解决此问题,即将各个按钮的事件绑定放入一个立即执行的匿名函数作用域中,并将变量i的值传入其中即可,例如“let oBtns = document.querySelectorAll (“button”);  for (var i = 0; i < oBtns.length; i++) {(function (i) {oBtns[i].onclick = function () {alert (i + 1);} } ) (i);}”,排它三步曲是第一步先初始化一个变量previousIndex保存上一个按钮的索引值(比如0),第二步使用for循环给各个按钮绑定事件,只要其它按钮触发所绑定的事件就先排它(即清空上一个按钮所对应的样式)并给当前这个按钮(this)设置所对应的样式,第三步把当前这个按钮的索引值i保存到变量previousIndex中就可以了,例如“let oItems = document.querySelectorAll (“li”);  let previousIndex = 0;  for (let i = 0; i < oItems.length; i++) {oItems[i].onclick = function () {oItems[previousIndex].className = “”;  this.className = “current”;  previousIndex = i;}”,正则表达式是用来表达对字符串的一种过滤逻辑的规则字符串(即逻辑公式),但是必须new RegExp/使用字面量“/正则表达式/”来将此逻辑公式(正则表达式)包装成一个正则表达式对象,格式为“let reg = new RegExp (“正则表达式”, “修饰符1”, “修饰符2”, ...);”,“let reg = new RegExp (/正则表达式/, “修饰符1”, “修饰符2”, ...);”,“let reg = /正则表达式/修饰符1修饰符2...;”,由于在字符串中反斜杠“\”具有功能性含义(比如“\t”代表制表符,“\n”代表换行,“\xa0”代表空格),所以当在new RegExp的参数1的正则表达式中出现了反斜杠“\”时,要在它前面再加一个反斜杠“\”,这样就将它转义成了普通的反斜杠了,这里边所说的转义是指使其失去功能,当想要new RegExp将正则表达式“\d{4}-\d{1, 2}-\d{1, 2}”包装成一个正则表达式对象时,格式为“let reg = new RegExp (“\\d{4}-\\d{1, 2}-\\d{1, 2}”);”,反斜杠“\”不光可以转义字符串,也可以转义正则表达式,比如中括号(范围描述符)和“.”在正则表达式中都有功能性含义,我们可以在它前面再加一个反斜杠“\”来使之变为普通的中括号和“.”,“()”在正则表达式中代表将指定内容提取出来,例如“var timeReg = /\[(\d*:\d*\.\d*)\]/;(//详见QQ音乐播放器-lyric.js)”,“+”在正则表达式中是量词,代表匹配一个/连续多个指定内容的字符串,例如“/y+/”,“^”代表匹配任何开头为指定内容的字符串,例如“/^n/”,“$”代表匹配任何结尾为指定内容的字符串,例如“/n$/”,“|”代表“或”的意思,由于默认情况下正则表达式是区分大小写的,所以使用i修饰符可以让正则表达式不区分大小写,由于默认情况下正则表达式一旦匹配到,就会停止匹配,所以使用g修饰符可以让正则表达式执行全局匹配,正则表达式对象调用它的test方法可以检测在传入的字符串中是否包含跟此正则表达式相匹配的字符串,返回值是true/false,格式为“reg.test (“abc”);”,正则表达式对象调用它的exec方法可以返回在传入的字符串中跟此正则表达式相匹配的字符串所组成的真数组,未匹配到就返回null

十三、JavaScript的小示例

关闭广告效果的核心思想是只要onclick“关闭按钮”,就removeChild广告的那个DOM元素,详见“10-JavaScript-关闭广告.html”,图片展示效果的核心思想是当onclick每一个小<img>元素时,就将此小<img>元素的src值赋值给大<img>元素的src属性,详见“11-JavaScript-图片展示.html”,手动简易轮播图的核心思想是当onclick“下一张”/“上一张”按钮时,就将数组中“下一张”/“上一张”相应的<img>元素的src值赋值给<img>元素的src属性,但是需要两个if语句规定一下当点到头了时怎么办(即安全校验),详见“12-JavaScript-简易轮播图.html”,商品展示的核心思想是使用for of循环给每一个小<img>元素都绑定上onmouseenter和onmouseleave事件,当onmouseenter时,给嵌套小<img>元素的li元素增加border,并将小<img>元素的src值赋值给大<img>元素,当onmouseleave时,就移除border,详见“15-JavaScript-商品展示.html”,Tab选项卡的核心思想是使用for of循环给每一个选项卡的按钮都绑定上onclick事件,只要触发此事件,就清空上一次所对应元素的对应样式,例如“oItems[previousIndex].className = “”;  oDivs[previousIndex].style.display = “none”;”,并设置当前所对应元素的对应样式,例如“this.className = “current”;  oDivs[i].style.display = “block”;”,并保存当前的索引值,例如“previousIndex = i;”,详见“19-JavaScript-Tab选项卡下.html”,全选和反选的核心思想是只要onclick“全选”按钮,就利用input多选框数组对象调用它的forEach方法,将此数组中每一个元素的checked值改为true,取消全选是改为false,反选是“item.checked = !item.checked;”,详见“27-JavaScript-全选反选.html”,时钟效果的核心思想是将时针`rotate(${date.getHours () * 30}deg)`,分针和秒针都将乘以30改为乘以6,详见“31-JavaScript-时钟效果.html”,缓动动画(先快后慢)的步长的公式为“(结束位置 - 开始位置) * 缓动系数”,缓动系数为一个0到1之间的小数,详见animation2.js,手动无限轮播图要在所有img的最后再增加一张跟第一张一模一样的img,一旦点击到最后一张img,当再点击时,就要瞬间跳转到第一张同时让索引值自增1,由于都是一模一样的img,所以跳转的过程肉眼根本看不出来,反向也是同样的道理,例如“oRight.onclick = function () {currentIndex++;  if (currentIndex > oItems.length - 1) {currentIndex = 0;  oUl.style.marginLeft = -currentIndex * imgWidth + “px”;  currentIndex++;}  easeAnimation (oUl, -currentIndex * imgWidth);}”,详见“36-JavaScript-无论播图.html”,自动无限轮播图可以通过手动无限轮播图来改造,增加一个setInterval,在里面调用“右箭头onclick事件”即可,只要onmouseenter就clearInterval此setInterval,onmouseleave就重新开启一个setInterval,详见“37-JavaScript-自动轮播.html”,佩奇跟我走的核心思想是使用了event.clientX和event.clientY,详见“46-JavaScript-佩奇跟我走.html”,星空背景的核心思想是使用for循环给所createElement的一定数量的小星星添加随机位置、随机scale、和随机animationDelay,小星星闪烁就做一个opacity从0到1的animation,详见“59-JavaScript-星空背景.html”,吸顶效果的核心思想是当window.onscroll时,只要竖向滚动条滚出的距离值大于等于顶部logo的高度,就将导航部分“oNav.style.position = “fixed”;”,否则就“oNav.style.position = “”;”,详见“65-JavaScript-吸顶效果.html”

十四、JavaScript的BOM特效

BOM(Browser Object Model)就是浏览器对象模型,是一套操作浏览器的API(接口/方法/属性),window对象中有navigator、location、history、screen等属性,它们都是对象类型的属性,navigator对象代表此web浏览器的信息,navigator对象访问它的userAgent属性可以返回此web浏览器的类型信息字符串,例如“/chrome/i.test (navigator.userAgent);(//判断是否是谷歌浏览器)”,“/msie/i.test (navigator.userAgent);(//判断是否是低级IE浏览器)”,““ActiveXObject”in window;(//判断是否是高级IE浏览器)”,location对象代表此web浏览器地址栏的信息,location对象访问它的href属性可以返回/设置此web浏览器地址栏当前完整的URL字符串,例如“location.href = “http://www.baidu.com”;(//必须加http://,否则无效)”,“location.href = “/exception/404”;(//这样使用也是可以的,此时相当于将web浏览器地址栏的URL的路径部分强制置成/exception/404,其余部分不变)”,location对象访问它的pathname属性可以返回/设置此web浏览器地址栏当前URL的路径部分的字符串,location对象访问它的search属性可以返回/设置此web浏览器地址栏当前URL的?及其之后部分的字符串,location对象访问它的hash属性可以返回/设置此web浏览器地址栏当前URL尾部从#开始的字符串(即哈希),返回的值包含#,设置时不用写#,系统会自动添加#,hash也叫哈希,给此web浏览器地址栏当前的URL设置一个哈希也叫种一个哈希,可以代替cookie实现当前这个html文件上页码的记录,即当前在哪一页刷新之后还在哪一页,cookie的弊端是只能在当前这个浏览器中使用,一旦换一个浏览器就失效了,这时改用哈希就可以了,详见“index(13-weibo).js”,location对象调用它的reload方法可以刷新此web浏览器的页面,当传入实参true时,代表强制刷新,会更新缓存,history对象代表浏览器的历史信息,利用history对象可以实现刷新/上一步/下一步,但是出于隐私考虑,并不能获取到此web浏览器所有的历史记录,只能获取到当前的历史记录,history对象调用它的forward方法可以在此web浏览器访问过其它页面时使页面前进,history对象调用它的go方法可以在此web浏览器访问过其它页面时使页面前进/后退,还可以刷新此web浏览器的页面,传入正整数代表前进多少个页面,传入负整数代表后退多少个页面,传入0代表刷新,history对象调用它的back方法可以在此web浏览器访问过其它页面时使页面后退,screen对象代表用户的屏幕信息

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 8,618评论 0 3
  • 一、JS前言 (1)认识JS 也许你已经了解HTML标记(也称为结构),知道了CSS样式(也称为表示),会使用HT...
    凛0_0阅读 2,721评论 0 8
  • 第1章 认识JS JavaScript能做什么?1.增强页面动态效果(如:下拉菜单、图片轮播、信息滚动等)2.实现...
    mo默22阅读 1,171评论 0 5
  • 个人博客:https://yeaseonzhang.github.io 花了半个多月的时间,终于又把“JS红宝书”...
    Yeaseon阅读 1,677评论 2 23
  • 人生必备的15项能力之一 建立自我意识 1.“自我意识”是心中有“我”的概念。在小时候,我们往往被引导着去跟其他孩...
    心灵的照见阅读 284评论 0 0