JavaScript WebApi
Senior JavaScript
Published: 2020-02-20

The article contains the knowledge of JavaScript WebApi…

DOM

获取元素

getElementById()

GetElementsByTagName()

getElementsByClassName()

querySelector()

querySelectorAll()

获取body元素和html元素

事件基础

步骤

常见鼠标事件

操作元素

innerTEXT与innerHTML

区别就是innerHtml识别html标签,另一个不识别,且innerHtml是W3C标准,而另一个不是,因此innerHtml用的更多。

另外,innerText会去除空格和换行,而innerHtml不会,如下:

outerTEXT与outerHTML

具体参考博客:https://blog.csdn.net/html5_/article/details/23619103

属性操作

按钮的disabled设置为true的话这个按钮就不能再按下去了

显然这种方法只能在修改元素样式比较少的时候使用,如果要修改的样式比较多,应该通过给标签添加class的方式去修改

element.getAttribute()(获取元素属性的另一种方式)

getAttribute()可以获得自定义的属性,这是与element.属性 的区别

element.setAttribute()(设置属性值)

element.removeAttribute()(移除属性)

H5自定义属性

设置和获取

这里的dataset是自定义属性的集合

节点操作

显然childNodes太麻烦,一般我们不用

我们一般使用children

这里的firstChild还是会返回文本节点,而我们大多数情况想要的还是元素节点

firstElementChild返回的就是第一个元素节点

实际开发中一般用element.children[]的方式去获取

下拉菜单案例

兄弟节点

这两个方法同firstChlid()一样,还是会返回文本节点

和firstElementChild()一样,只会返回元素节点

对于nextSibling,他的兼容性没有什么太好的办法,只能写算法解决

创建与添加节点

留言板案例

删除节点

复制节点

三种动态创建元素区别

document.write()会导致页面重绘(之前页面的所有元素都没了,就只剩下了这个加进去的元素,相当于重新创建了一张页面),一般不用,有点类似window.onload

由于拼接字符串会导致内存栈里面生成大量的字符串,所以直接导致性能下降,所以如果用innerHTML 并用拼接字符串的方式生成标签效率是很低的,但是如果配合数组就不一样了,我们将需要生成的标签直接加到数组里面,最后用 .join(‘’) 的方式拼成字符串再用innerHTML生成,这个时候innerHTML的效率为最高。

如果用createElement()与appendChild()的方式,他的效率会比直接拼接字符串的innerHTML高很多,而且结构更加清晰,但是较配合数组的innerHTML来讲他的效率还是要低那么一点

事件高级

注册事件(绑定事件)

这个唯一性就是如果写了两个onclick,那么后面的会把前面的覆盖掉,而监听注册方式就不会出现这个情况,因此更加推荐

attachEvent是IE9以前用的,是非标准,不推荐使用

注册事件兼容性解决方案

删除事件的方式

1、

2、

3、

删除事件兼容性解决方案

DOM事件流

在实际开发过程中更关心事件冒泡过程

事件对象

现在基本不需要考虑兼容性

事件对象的常见属性和方法

注意,this和e.target是不一样的,this指向的是绑定事件的对象(元素),而e.target指向的是触发的那个事件对象(元素),例如:我们给ul添加监听事件,然后点击ul下的li,那么this还是ul,而e.target是点击的那个li

有一个跟this几乎一样的参数叫做e.currentTarget,这两个可以换着用,该用法也有兼容性问题

由于兼容性问题,ie678只能使用e.srcElement而不能使用e.target,解决兼容性问题可以这么写:

还是一样,实际开发中一般不用再考虑兼容性了

e.type 返回事件类型:

阻止默认行为(事件),比如让链接不跳转 或者让提交按钮不提交

可以用e.preventDefault(); 考虑ie678的兼容性问题也可以用e.returnValue(); 由于上面两个都有兼容性问题,我们其实可以直接return false;

但是return false; 有个问题,他下面的代码将不会再执行:

而且return false; 只限于传统的注册方式,在 .addEventListener() 上他就不管用了。

因此 e.preventDefault() 是我们开发中最常用的

e.stopPropagation() 阻止事件冒泡:

这里浏览器显示 “son” 之后就不会再显示 “father” 了

考虑ie678兼容性我们可以使用e.cancelBubble=true :

解决兼容性方案:

事件委托(代理、委派)

利用冒泡的特点,我们不再需要给每个li添加监听了:

这里直接给ul(父亲)添加节点,当点击li的时候由ul去接收事件

如果我们想要给当前li添加背景颜色,可以使用e.target:

常用鼠标事件补充

contextmenu

该事件可以防止用户在页面点击鼠标右键之后显示上下文菜单(防复制)

selectstart

上面的方法我们只禁止了鼠标事件但是没有禁键盘事件,所以如果直接ctrl+c 还是可以复制文本内容的,那我们干脆直接禁止鼠标选中就好了

鼠标事件对象

注意:上面红色区域是用户可视区域,控制台不属于用户可视区

clientX 是鼠标距离可视区左侧的距离,clientY 是鼠标距离可视区右侧的距离

然后我们设置body的height为3000px

发现用户可视区的x、y轴还是没变

用pageX、pageY之后,我们可以得到鼠标距离文档上沿和左侧的距离(使用较多):

用screenX、screenY可以得到鼠标距离电脑屏幕上沿和左侧的距离

mousemove

跟随鼠标移动的图片

做到这里我们发现图片并没有随着鼠标移动,原因很简单,top和left属性后面都要加 “px” 才行,我们这里没加

Vscode也可以查看图片尺寸

调整图片位置让他更好看

键盘事件

keypress和keydown的区别就是keypress不能识别ctrl、shift、左右箭头等功能键,但是keydown都可以识别,而且:

keyup和keydown不区分a和A(两者都是返回ascii码65),

但是keypress是区分的

注意

上面这三个事件是有执行顺序的,keyup这个不用说肯定是最后执行的,keypress和keydown这两个先执行keydown后keypress

键盘事件对象

跟鼠标事件对象一样,键盘也有键盘事件对象

键盘对象里面的key属性可以显示用户具体按了那颗键,但是他有很大的兼容性问题,有些浏览器甚至还不支持,因此一般不用这个,我们一般用keyCode(返回按键ascii码)来代替

注意

keyup和keydown不区分a和A(两者都是返回ascii码65),

但是keypress是区分的

这时候a返回的是97,A返回的是65

案例

1、搜索框按下s键获得焦点

由于keydown 用户按下键盘的时候会一直触发,所以我们最好设置为keyup

2、

注意这里不能用keydown,因为这个事件触发的时候文本还没有进入到文本框中,因此他会先执行上面的if代码块之后再将文本输入到文本框:

另外,这里我们更不能用keypress,因为keypress不能识别Backspace键:

所以这个案例用keyup

之后再添加功能:

总结

1、

2、

对于父亲节点、子节点、兄弟节点,我们重点记住parentNode、children就可以了

3、

DOM小结

补充

1、如果不需要a标签跳转,可以在href里面写 javascript:void(0);或者直接写 javascript:;

BOM

BOM比DOM大,原先的document.xxx其实应该这么写:window.document.xxx

全局变量会变成window对象的属性,函数会变成window的方法

之前说的声明变量最好不要取名name,因为window对象里面就有一个name属性

window对象

parent属性

// 获取当前窗口的父窗口
window.parent

top属性

// 获取当前窗口的最顶层浏览器窗口
window.top

window对象的常见事件

由于是传统的注册方法,因此如果注册多个会以最后一个为准

当然我们也可以不用传统的,我们用推荐的注册方法:

这样就可以注册多个了

用了这个注册之后我们的js代码就能写到任何一个地方而不受限制了

窗口加载事件

document的DOMContentLoaded和window的load差不多,区别在于document的DOMContentLoaded不会等待样式表、图片、flash等的加载而会在仅当DOM加载完成之后就直接去执行js事件;对比来讲window的load会等待所有的元素都加载完毕(包括DOM、样式表、图片、flash等)之后再去执行js事件,显然当图片多的时候使用window的load的话js事件会需要等很长的时间才能被执行,用户体验会非常不好,而document的DOMContentLoaded就不需要等待这么久了,虽然可能图片什么的还没有加载出来,但是下拉框、js交互效果什么的还是能用的,用户体验会比较好

另外当document的DOMContentLoaded和window的load写在一起的时候document的DOMContentLoaded会更快一点

这里的执行顺序是:先跳出33 再跳出22 最后跳出“点击我”

调整窗口大小事件

定时器

setTimeout()

直接这么写就好了,不需要再去调用

停止setTimeout()定时器

setInterval() 和setTimeout() 基本一样,区别就在于执行方式不同,setInterval() 倒计时完了之后会再调用

setInterval()

停止setInterval()定时器

案例

1、五秒之后自动关闭广告

2、京东倒计时器

这里之所以要提前调用一次countDown()函数是为了防止定时器刚开始的那一秒间隔让元素显示空白

3、开启和停止定时器

4、手机验证码发送之后要等待60s才能再次点击的那个按钮的实现

this指向问题

JS执行队列

如果是单线程那么上面的这个程序用户体验就很差了

所以后来html5提出了js的同步和异步,现在不需要任何更改执行上面的程序打印顺序为:1 2 3

我们再看下一个程序

出乎意料的是,该程序输出结果也为1 2 3

这是因为js把同步任务和异步任务分开了,同步任务会被放到主线程执行,而异步任务会被放在任务队列中执行

注意,这里定时器还是会在主线程执行栈中,而定时器中的回调函数会被放到任务队列中

JS执行机制:先执行主线程中的同步任务,碰到定时器这样的异步任务后先将回调函数放入任务队列中不执行,再继续往下执行主线程中的同步任务,当主线程任务执行完毕后再将任务队列中的异步任务放到执行栈中执行

有多个异步任务时

像鼠标事件,他是先到异步进程处理器这边的,等用户按下鼠标之后,异步进程处理器才会把该任务加到任务队列里面,如果用户不点鼠标它是不会加到任务队列里面的;

再比如计时器事件,也是先到异步进程处理这边,等到时间到了之后才会被放入任务队列中

然后放入任务队列之后还是不执行的,要等到主线程执行栈中任务全部完成之后他才会去任务队列找是否有任务,如果有则拿到执行栈中执行

事件循环(event loop)

我们发现主线程栈会时不时来任务队列查看,有任务的话就拿过来,这个过程叫做事件循环(event loop)

Location对象

属性

这里直接给href赋值就可以完成点击按钮页面跳转功能

案例

京东5秒倒计时跳转页面

数据在不同页面之间传递的效果

location对象的方法

location.assign()

记录浏览器历史,所以可以实现后退功能

location.replace()

不记录浏览器历史,所以不可以实现后退功能

location.reload()

相当于浏览器中的刷新按钮,刷新页面

网页在打开过一次之后系统会将图片等存入缓存,那么在下一次再次打开页面的时候就会非常快速了,但是有时候我们不想要使用这些缓存,这个时候在reload()中写true,表示强制刷新,相当于浏览器的ctrl+F5 强制刷新页面

有时候我们在写页面的时候他的样貌总是不对可能就是缓存的问题,可能强制刷新一下就可以出来了

user-agent就可以从这里获取

这里用了正则表达式去判断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 = “”; //电脑

}

history对象

这个back()和forward()其实就是浏览器中的后退和前进

go的话写起来更加方便,直接写go(1)或者go(-1)

history一般很少使用,但是如果开发的是OA(office application)系统就会用得到了

PC端网页特效

元素偏移量offset系列

offsetTop和offsetLeft

JS可以动态获取元素的css中的一些定位的值

需要注意的是,如果父元素没有定位的,子元素的定位信息会以body为准:

这里紫色的盒子距离左边框的距离是150 + 45 = 195px

如果非要得到子元素左边框距离父元素左边框的距离,应该要去父元素的css中添加定位为relative

这样上面紫色盒子距离左边框的距离就会是45px了

offsetWidth和offsetHeight

需要注意的是,这里获得的宽度和高度包含了padding和border值,但是不包含margin值,也就是说如果给元素添加了padding和border值,那么获取的width和height就会变大,但是margin值是不会影响他们的

那我们思考,其实这些值我们直接从style中也可以获取,那么为什么还需要这两个函数呢?其实有一种情况,就是我们把元素的css中的width或height去掉,那从style中就很难获取这些信息了,这个时候就需要借助这两个函数:

offsetParent

offsetParent在父亲没有定位的时候返回的是body,在父亲有定位的时候返回的是父亲

这里就要和parentNode进行区别了

parentNode返回的是亲爸爸,不管他有没有定位;而offsetParent返回的是有定位的爸爸,如果亲爸爸没有定位,就往上找爷爷,以此类推直到最后找到有定位的上级或者body

offset和style的区别

1、style不能获得内嵌样式表,只能获得行内样式表。比方说我们写在head中的或者从外部引入的css样式,那么style是获取不到的;而我们的offset不仅可以获取行内样式表,还可以获取内嵌样式表;

2、offset获取的数值是不带单位的,而style获取的数值是带单位的;

3、offsetWidth包含padding+border+width,而style.width不包含padding和border;

4、offsetWidth等属性是只读的,不能修改,而style.width等是可读写属性,是可以获取也可以赋值的;

所以,以后如果我们想要获取元素的属性,用offset比较合适;如果我们想要给元素更改属性,则需要用style去改变

案例

1、

当然也可以使用mousemove事件

2、

下面这个js是老师写的,上面的html是自己的,可能会有不对应的地方

重点:接下来做拖拽效果

这个px千万别忘了

最后再编写mouseup事件

3、京东鼠标放大镜案例

原理:

添加两个div,一个是上图黄色的mask,一个是上图被放大的图片,这里涉及到绝对定位,子绝父相

之后每当鼠标放到图片上,指针就变可移动图标,并且黄色mask跟随鼠标移动,被放大的图片也随之移动

由于本案例js较多,因此我们将js移出去

接下来制作黄色盒子跟随鼠标移动。

注意! 由于黄色盒子和他的父盒子用了子绝父相,因此黄色盒子的left和top是以他的父亲为基准定的,因此这个移动事件跟前面那个是有区别的,当然原理还是一样的,所以当遇到这种问题的时候应该先分析,到底这个盒子的left和top是怎么来的,如何通过计算正确给他们赋值,之后再下手写算法。

但是,下一个问题来了,我们要拿到下图黄色盒子与紫框框的相对距离应该是用鼠标坐标去减去紫框框的left和top值,但是我们不能直接减紫框框的css中的属性,还是要分析紫框框是否自己也是子绝父相,如下面的两张图,如果他自己也是的话那就不能这么减了,所以具体问题还是要具体分析!那么一般遇到这种情况可以通过chrome一级一级查看他们的父亲,如果他们的父亲全部没有定位,那么好消息,直接减就行了,如果有,那只能一步一步减。

本案例黄盒子的父亲们没有子绝父相,所以直接减就行

这里我们发现鼠标跟黄盒子左上角对齐了,但是我们希望鼠标在黄盒子中心,这个时候可以减去黄盒子宽高的一半,即.offsetWidth / 2和.offsetHeight / 2

接下来我们让黄盒子出不了父盒子

接下来制作大图片跟随黄盒子覆盖的区域显示,

由于大盒子的图片 : 小盒子的图片 = 2 : 1

所以可以设大盒子移动的距离为x,那么就有如下公式:

根据公式:

首先求遮挡层最大移动距离

然后求大图片最大移动距离(大图片的宽度-装大图片的大框的宽度)

最后计算大图片的移动距离,算完之后赋值即可

启动之后发现图片没动,那是因为我们修改的是他的left和top值,而图片没有添加定位属性,添加后即可

但是问题又出现了,我们发现大图和小图的移动应该是反方向的,所以进行修改:

元素可视区client系列

clientTop

注意是上边框的大小

clientLeft

注意是左边框的大小

clientWidth

注意不包括border大小但是包括padding+width大小

clientHeight

注意不包括border大小但是包括padding+height大小

淘宝flexible.js源码分析

它可以创建一个独立作用域,避免了命名冲突

立即执行函数

如果直接写function(){}或者funtion(){}()是会报错的,所以要么写(function(){})()要么写(function(){}())

注意,这样的写法在最后一定要加 ; 否则可能会出错(养成好习惯,都加就行了)

还可以给函数加名称

里面的所有变量都是局部变量,所以不会有命名冲突的情况

window.devicePixelRatio

dpr 物理像素比,一般在pc端是1(就是我真实的是1,他显示的也是1),在移动端是2(就是我真实的是1,他显示的是2)

pageshow与load事件

有一种情况就是类似火狐的页面缓存,比方说我们写了load事件,到最后的时候设置页面会打印“go”,但是像有页面缓存的火狐当我们再次到这个页面(可能是前进后退键的方式回到这个页面的)的时候他是不会在页面打印“go”的,那么为了解决这种情况,我们可以使用pageshow事件,pageshow事件在load事件触发后触发,无论页面是否来自缓存他都会在页面显示时触发。注意这个事件是给window添加的。

另外,我们还可以利用事件对象中的persisted(function(e){e.persisted})来判断是否是缓存中的页面触发的pageshow事件

这里的e.persisted应该是如果是有缓存页面而且触发了缓存页面的话才会有值,像chrome这样的本身就没有缓存,就算触发了pageshow事件,他的e.persisted也是未定义状态是不会触发下面的setRemUnit()函数的。

总之,这里的resize和pageshow事件目的都是触发setRemUnit(),也就是重新设置页面大小。

解决有些浏览器不支持0.5像素的写法

元素滚动scroll系列

scrollHeight

我们发现scroll系列属性和client系列属性很像。

上图就是scrollHeight和clientHeight的区别,scrollHeight是内容实实在在的实际高度,clientHeight只是包含内容的盒子的高度

注意scrollHeight是内容顶部到边框下沿的长度:

案例1

日志滚动窗口。实现新日志来的时候屏幕信息自动向下滚动,当鼠标放上去的时候屏幕信息停止滚动,鼠标离开的时候屏幕信息再次开始滚动。

<style>
    .parent {
        width: 300px;
        height: 200px;
        margin: 0 auto;
        background: #242424;
        overflow-y: scroll;
    }
    .child {
        height: auto;
    }
    .child li {
        height: 50px;
        margin: 2px 0;
        background: #009678;
    }
</style>
<body>
    <div id="parent" class="parent">
        <div id="child1" class="child">
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
        </div>
    </div>
    <script type="text/javascript">
        (function () {
            let parent = document.getElementById('parent');
            let child1 = document.getElementById('child1');
            var x = setInterval(function () {
                if(parent.scrollTop <= child1.scrollHeight) {
                    parent.scrollTop++;
                } 
            }, 10);
            parent.addEventListener("mouseenter", () => {
                clearInterval(x)
            })
            parent.addEventListener("mouseleave", () => {
                x = setInterval(function () {
                    if(parent.scrollTop <= child1.scrollHeight) {
                        parent.scrollTop++;
                    } 
                }, 10);
            })
        })()
    </script>
</body>
scroll事件

案例2

pageYOffset

可以获取页面被卷去的上部的值

pageXOffset

可以获取页面被卷去的左侧的值

注意,元素被卷去的头部是element.scrollTop,如果是页面被卷去的头部则是window.pageYOffset

上面这个172写死有点不好,其实他就是某一个模块的offsetTop值(这里是banner的offsetTop)

注意这里banner.offsetTop一定要写到滚动的外面,因为滚动的时候这个值可能会变

还有一个问题,我们的sliderbar在改为fixed之后突然跳到下面了,那是因为sliderbar原先是absolute绝对定位且本身有个top:300px属性,突然改为fixed之后top值没改,因此一下子跳到距离body上沿300px的地方了,解决方法很简单,修改top的值即可

这里的top值应该改为sliderbar的top值减去banner的top值

切记要加 ‘px’

接下来我们制作goback模块

解决pageXOffset和pageYOffset的兼容性问题

可以让window.pageYOffset配合document.documentElement.scrollTopdocument.body.scrollTop来做兼容;同理window.pageXOffset可以配合document.documentElement.scrollLeftdocument.body.scrollLeft来做兼容:

其实现在也不在乎兼容性问题了,直接用最新的就好

####### DTD

html页面加了这个就是声明了DTD,没加就是没声明

三大系列总结

鼠标事件补充

mouseover和mouseenter的区别

动画函数封装

封装

不同的元素记录不同定时器

这里每当我们调用函数我们的程序都会到内存中给timer开辟一块空间,如果调用次数过多会造成空间浪费,其次每个对象调用该函数的时候他的定时器都叫timer容易引起歧义,对此我们可以利用对象的特性解决这个问题

这样写的好处第一因为这个timer只是obj的属性,我们不会再为每一个调用者到内存中开辟一块timer空间;第二这个timer是每一个调用者自己的属性,不会引起歧义

接下来我们给动画设置按钮,要求用按钮启动动画,但是发现一个bug,当我们多次点击按钮之后,这个div移动的速度越来越快,那是因为每点击一次就给他设置了一个定时器,多个定时器叠加后就会出现这个问题

代码的解决方案就是每一次启动我先给你清除原先的定时器

缓动效果原理

我们发现这个left不是整数,这会很难看,给它向上取整就行了

这个距离移动公式非常厉害,我们可以写两个按钮一个让盒子移动到500,一个让盒子移动到800,当我们再点击500的按钮的时候他还能自己回来,而且效果跟过去是一样的,但是有一个bug,回来的时候不是500那个位置了,而是509,这是因为我们向上取整了,负数向上取整的时候出的错

调整一下取整的方式就好了

在封装的动画函数中添加回调函数

如果不写到定时器结束的位置,那这个函数只是一个普通的函数,只有写到回调函数后面他才是一个回调函数

将动画函数写到单独的js文件中,下次调用直接引入即可

案例

1、

计算紫盒子移动的距离:

200 – 40 = 160px ,由于是向左移动,所以是负值

最后我们再将上面的紫盒子弹出来之后将右侧的指向左侧的箭头改为指向右侧

2、轮播图

上图的ul中只有一个li,我们给他多加几个:

结果这几张图片是竖下来的,原因是没加float,

但是加了float他还是竖下来的,原因是父盒子太小了,a标签的大小默认是随父盒子ul的,因此还要将父盒子的width单独设置大一些:

注意这里要先引入animate.js再引入index.js,因为index.js是依赖animate.js的

首先搞清楚是ul移动而不是li移动,然后给ul添加position:absolute

那么小圆圈是怎么跟图片联系起来的呢?显然只能是index,我们在添加小圆圈的时候给小圆圈设置index属性,然后在点击事件里面获取即可,之后用index去乘以一张图片的宽度再取负值即可算出图片移动的距离,然后调用我们的animate函数

后来显示图片的时候我们会发现两侧的左滑右滑按钮不见了,那是因为给ul加了定位,解决方案就是给左滑右滑按钮添加z-index属性

然后我们发现我们的a标签按下之后url后面会带一个 # 而且页面会跳回到最顶部,解决方法就是把a标签的href内容由 ## 改为javascript:;

这里focusWidth之前是局部变量,这里我们干脆直接把他拿出来变成全局变量了,不然用不了

这里又来了一个bug,当图片已经到最后一张的时候,我们再次点击右滑键,它会显示上图这个画面(其实就是图片下面被盖住的那层被显示出来了,简单来讲就是图片移过头了)

无缝滚动

事实上淘宝他们的是无缝滚动,实现原理其实很简单就是复制一个li到最后,然后当用户点击右滑键的时候先跳到最后一张复制的li,当再次点击的时候直接将ul的top改为0px,即可实现该效果

当然了num别忘了再赋值为0

这里又来了个新问题,就是我们的小圆圈是根据图片的数目自动生成的,但是我们给ul最后加了一个li,导致真实图片有4张但是我们的li却有5个,其实我们可以让js来复制这最后的li

最后我们将ul的overflow改为hidden

现在我们来写点击右滑键之后小圆圈的动态变化

先定义全局变量circle

然后bug又出现了,当我们先点击小圆圈来跳转页面之后再点击右滑键会发现图片竟然没有跳到下一张而是跳到了不是我们希望跳转到的那一张(反正就是没有跳到下一张)

解决方法其实很简单,就是在点击li事件获取它的index之后将我们的全局变量赋值为li的index值(其实思路就是li的点击事件和右滑键的点击事件两者的控制li的index给他同步就行了)

但是这时候li的颜色变化还是不对,我们选中了第三个小li,但是实验发现点击右滑键的时候第二个li亮了,其实只需要将控制li变亮的index也给他赋值为点击li事件得到的该li的index即可

这里也可以连写

然后我们会发现这一块有一个小空隙

这是因为框框的宽度和一张照片的宽度不一致,导致翻页的时候越翻这个空隙越大,我们给他俩改一致就不会有这个空隙了

下一步制作左滑按钮

还可以用三元表达式

如果有重复的代码就给他封装出来

接下来制作自动播放功能

手动调用点击事件

xxx.click()

由于自动播放与点击右滑键十分相像,所以我们直接手动调用点击事件触发右滑键就可以了

要养成良好的编程习惯,没用了的变量及时置为null

节流阀

这里用到了animate的回调函数,这里的回调函数只会在动画执行完毕之后才会执行,因此我们把flag=true放在这里

3、按钮返回顶部(优雅地)

window.scroll(x, y)

注意,这里的scroll里面的x, y是不带单位“px”的

由于这里我们的animate.js已经被引入了,因此我们直接把他复制粘贴到主项目下进行修改

这里就看着改吧,搞清楚是哪一块在动(这里的话明显是整个document在动),注意别漏了就行

然后需要注意,这里是整个document在scroll,而不是某个元素的top或left值在变,所以别忘了在这里要改一下。然后就是scroll不需要加’px’ 这个也需注意

最后再调用

4、筋斗云案例

js动态修改css动画(animation)

第一步:通过 document.styleSheets 定义动画变化的样式

document.styleSheets[0].insertRule(
	`@keyframes example {
		0% {background-color: red;}
		100% {background-color: yellow;} }`,
	0
);

第二步:使用 js 修改元素 animation 等属性

document.getElementById("ele").style.animation = "example 1s linear"

js动态修改css渐变(transition)

使用过渡实现动画效果:

document.getElementById("ele").style.transition = "width 5s linear";

操作 transform 属性:

document.getElementById("ele").style.transition = "-webkit-transform 1000ms linear";
document.getElementById("ele").style.webkitTransform = "scale(1) scale(0.65)";

移动端特效

移动端的head上面要加这几行(目前还不知道什么意思,可能是移动端的全局基础属性配置),只知道中间那一行的user-scalable=no表示用户无法将页面放大缩小(一般是为了解决移动端click会有延时300ms的问题,这个问题在下面有说到)

触屏事件

touchstart

touchmove

touchend

TouchEvent

touches

触摸屏幕手指个数,是一个列表,几个手指触摸列表中就会有几个元素,上图的length会显示当前有几个手指在触摸

targetTouches

触摸当前DOM元素的所有手指的信息

当我们想要获取某个DOM元素上的第一个手指的信息:

changedTouches

手指状态发生了改变的列表 从无到有 或者 从有到无

touches、changedTouches、targetTouches的区别

我们比较常用的是targetTouches

移动端拖动元素

注意,手指移动会触发滚动屏幕的默认事件,我们需要用e.preventDefault()把这个默认事件给阻止掉

这里我们没有用touchend事件,因为当touchmove事件结束的时候他就不会移动了,没必要写touchend

移动端轮播图

由于移动端可以左滑右滑,因此第一张的前面也需要放一张最后一张的图片

之后还是老样子给li添加float,并给ul的宽度设置为500%,但是发现居然不行,li中的图片突然变得特别大,那是因为在pc端中我们的li的宽度是写死的,而在移动端中的li宽度没有写死,那么他就会去找父亲的宽度,而父亲宽度为500%(假设为x px),这时候li宽度会就会变x px,相当于默认写了li的宽度为100%,那么我们有5个li,我们将li的宽度写成20%(相当于父亲的宽度 * 20%),这个时候li的显示就正常了

这样改之后虽然li的显示正常了,但是整个ul下面的元素却不正常了,那是因为ul没有高度,而li设置为float:left,这是典型的没有清除浮动,那么此时最简单的方法就是给ul加overflow:hidden,反正他本来也是不能显示溢出部分的。

之后我们发现这块搜索框不见了,那是因为之前的操作撑大了我们的标有class=“focus”的盒子,而我们的搜索框是居中显示的,现在他跑到右边去了,让他回来的原理就是不要撑大focus盒子,那么我们只需要在focus盒子里添加overflow:hidden即可

然后我们发现那几个小圆点有点高,检查元素之后发现他有margin,那么只需要给他加margin=0即可

之后我们又发现一个问题显示的轮播图由于是靠左对齐的,现在显示的是最左边的图片,这是不对的,应该显示第二张图

给ul加一个margin-left=-100%即可(意为向左移动距离为父亲宽度的1倍)

下面开始编写轮播图特效

检测过渡完成事件transitionend

用于检测过渡事件是否完成

这里ul的每次过渡完成之后就会打印1

Index为3说明走到最后一张图了,由于index控制着轮播图的位置我们需要修改index,但是index切为0之后在移的过程中轮播图会有过渡地移到第一张图上,因此我们还要清除过渡。同理index为-1。

返回元素类名className

例子:

document.write(document.getElementById('myId').className);

返回元素类名列表classList

第一个类名:

element.classList[0]

添加类element.classList.add(‘类名’)

移除类element.classList.remove(‘类名’)

切换类element.classList.toggle(‘类名’)

toggle的作用是当元素没有该类名的时候就给它加上,当元素有该类名的时候就给他去掉,之前的开关灯案例都需要写判断逻辑才能完成,现在有了toggle就不需要了

下一步添加小圆点事件

选出class为current的li

还可以给小圆圈的变化添加过渡

下一步制作手指滑动轮播图

注意手指放上去的时候就要清除定时器,手指拖动的时候不需要过渡

在touchend事件中再次开启定时器

优化:

当用户触摸的时候如果没有拖动其实就不需要执行某些代码

因此设置全局变量

当手指移动的时候,flag变为true

然后在touchend事件中包一层flag即可

还有最重要的一点:清除移动的默认行为

至此轮播图已经完成了,接下来制作返回顶部按钮

移动端click延时解决方案

手机可以放大缩小页面,因此会在300ms之内判断用户是否点了两次,所以这个300ms是不可避免的

解决方案:

1、直接禁止用户放大缩小页面

缺点:有些用户就是要放大缩小,这时候就有点麻烦

2、利用touchstart、touchmove和touchend自己编写逻辑判断封装一个函数代替click

缺点:要写这么多代码,关键是每次还只能传入一个元素,如果元素多了有多少元素就要调用多少次函数。

上面两个方案都不是特别完美,那么为了解决这个问题,有一个贡献者专门写了一段js代码(fastclick插件)

核心代码中是一个立即执行函数,所以作者在最前面加了一个 ”;” ,当然,在末尾是必须加的

既然是js文件,怎么用就不用多说了

我们来看怎么调用他的函数:

‘addEventListener’ in document表示addEventListener 属于document(其实就是判断document是否有addEventListener这个事件)

就只需要添加这段代码,页面中所有的延时问题就都解决了,之后直接想怎么写click事件就怎么写就行了

Swiper插件的使用

下载完的文件里面的demos就是案例示例,dist里面有需要引入的css和js文件,带.min后缀的说明是压缩过的,直接引这个就行

在demos中找案例的时候先去官网找你喜欢的type的编号:

之后再去demos里面找

由于里面的class名、id名等都是写死的,所以我们最好把需要的部分全量复制,用它的html

复制完结构复制css样式

之后再复制js

就能用了!

之后有一些层级的bug的话只需要改一下就行了

Swiper使用进阶

之前讲了怎么用起来,现在讲怎么把他用的更好

先去看引入的js文件里面用到的方法,如果想改默认参数,可以来API查这个方法具体怎么改,比方说上面的轮播图间歇啊什么的都可以在这里查到怎么改

Js可以通过以上方式更改,css更简单直接自己改就行了

除了Swiper之外还有superslide和iscroll

移动端选touchslide

之后就一样了,直接找到相应demo,右键查看网页源代码复制粘贴就行

移动端视频插件zy./js_webapi.js

不同浏览器控件外观都不一样,而想要让他们一样也可以通过写js,但是那样的话就需要写大量的js代码,这里我们可以使用zy./js_webapi.js控件

前端开发框架

本地存储

sessionStorage

浏览器中查看方式

setItem

存储数据或者更改数据

getItem

获取数据

removeItem

删除数据

clear

删除所有数据(谨慎使用)

localStorage

与sessionStorage不同的是,localStorage直接将数据存储在本地,关闭浏览器他都不会消失,除非手动删除

浏览器中查看方式

setItem

getItem

removeItem

clear

案例

change事件

Cookie的设置以及Cookie中可能存在的敏感信息的加密解密

// hours为空字符串时,cookie的生存期至浏览器会话结束。
// hours为数字0时,建立的是一个失效的cookie,这个cookie会覆盖已经建立过的同名、同path的cookie(如果这个cookie存在)。
function setCookie(name, value, hours, path){
    // 注意:escape()也可以换用window.btoa()
    name = escape(name);// escape可以加密数据
    value = escape(value);// escape可以加密数据
    let expires = new Date();// 过期时间
    expires.setTime(expires.getTime() + hours*3600000);// 设置过期时间
    path = path == "" ? "" : ";path=" + path;// path不为空则设path=path
    const _expires = (typeof hours) == "string" ? "" : ";expires=" + expires.toUTCString();// hours参数类型字符串则为空,否则转为UTCString
    document.cookie = name + "=" + value + _expires + path;// cookie:name=value;expires=time;path=path,过期时间,路径
}
// 获取cookie值
function getCookieValue(name){
    // 注意:escape()也可以换用window.btoa()
    name = escape(name);// escape可以加密数据
// 读cookie属性,这将返回文档的所有cookie
    let allcookies = document.cookie;
// 查找名为name的cookie的开始位置
    name += "=";
    let pos = allcookies.indexOf(name);
// 如果找到了具有该名字的cookie,那么提取并使用它的值
    if (pos != -1){// 如果pos值为-1则说明搜索"version="失败
        let start = pos + name.length;// cookie值开始的位置
        let end = allcookies.indexOf(";",start);// 从cookie值开始的位置起搜索第一个";"的位置,即cookie值结尾的位置
        if (end == -1) end = allcookies.length;    // 如果end值为-1说明cookie列表里只有一个cookie
        let value = allcookies.substring(start,end); // 提取cookie的值
        // 注意:unescape()也可以换用window.atob()
        return unescape(value);// 对它解码
    }
    else return "";// 搜索失败,返回空字符串
}
 
// 删除cookie
function deleteCookie(name,path){
    // 注意:escape()也可以换用window.btoa()
    name = escape(name);
    let expires = new Date(0);
    path = path == "" ? "" : ";path=" + path;
    document.cookie = name + "="+ ";expires=" + expires.toUTCString() + path;
}

前端获取并解析后端返回的图片文件流

需求:后端直接返回了图片文件流,前端需要显示这个图片,开始解析URL一直出错,最后发现,还需要设置个responseType参数才可解析出正确的url:

const formdata = new FormData();
formdata.append("content", this.state.text);
formdata.append("file", this.state.imageArr[0]);
axios({
    method: "post",
    url: "/api/qr-code",
    data: formdata,
    responseType: "blob",
}).then((res) => {
    let blob = new Blob([res.data], { type: "image/png" });
    let url = window.URL.createObjectURL(blob);
    console.log(url);
    this.setState({ url });
    Toast.hide()
});

技巧

1、多使用短路运算符

&& 和 || 都有情况可以使用的,但是需要思考一下

2、css中的属性100%永远是跟父亲来比较的

比方说上图的意思就是宽度为父亲的5倍,元素向左移动距离为父亲宽度的1倍

3、移动端能用css3就用css3,css3与js搭配效果更好

4、为了保证某个定时器只有一个在运行,当我们要开启一个定时器的时候一定要思考一下是否需要先清除该定时器

5、移动端的touchmove事件一定要留一个心眼,一般情况下都要先清除滚动屏幕的默认行为

6、写多个立即执行函数的时候中间一定要加 “;”

这个是fastclick插件,在核心代码中是一个立即执行函数,作者在最前面加了一个 ”;” ,当然,在末尾是必须加的

Web API接口参考

https://developer.mozilla.org/zh-CN/docs/Web/API

里面有诸如:HTMLDivElement(https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLDivElement)HTMLElement(https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement)等内容