关于最近在做的一个js全屏轮播插件
最近去面试了,对方要求我在一个星期内用原生的js代码写一个全屏轮播的插件,第一想法就是跟照片轮播很相似,只是照片轮播是有定义一个宽高度大小已经确定了的容器用来存储所有照片,然后将照片全部左浮动,利用margin-left的值不断变化,来改变显示哪张照片,当达到最后一张的时候,将第一张照片appendChild到最后一张照片的后面,如果有左右移动的话,当到达第一张照片时,还要继续左移动的话,就将最后一张照片利用insertBefore,将最后一张照片放在第一张照片的前面,这两个方法都只是改变子元素的顺序而已,并没有增加子元素。
上面说的是照片滚动要怎么做,接下来开始说一下我制作js全屏滚动插件的过程。
因为之前的时候,对于scrollWidth和clientWidth和offsetWidth的区分还是不是很明确,所以认真测试了一下,这个网址也讲得很清楚:http://www.cnblogs.com/kongxianghai/p/4192032.html 后来选择使用了document.documentElement.clientWidth,刚开始使用document.body.clientWidth,的出来的结果一直有偏差,是因为Html文件的开头使用了dtd定义文件的W3C标准,所以才要使用document.documentElement.clientWidth,通过document.documentElement.clientWidth和document.documentElement.clientHeight来获取页面的宽高度之后,就可以动态的给页面设定宽高度了,这个过程中,需要先定义一个容器,叫做div1吧,div1的宽度设定为100%,高度先给一个确定的值,之后再通过js改变这个div的宽高度,使其宽高度为一个页面的宽高度,同时有一个要注意的是,div1要设定它的overflow为hidden,这样的话,就不会因为里面的元素而撑开div1的大小了。之后再在div1里面定义一个子容器,叫做div2吧,div2的宽度要容纳所有的子页面,这个宽度可以通过获取子页面的个数,将利用document.documentElement.clientWidth得到的页面宽度乘于页面数,之后得到的长度就是div2的宽度了,高度跟页面高度一样,这个容器是关键,以后用它来移动从而不断显示页面。之后再定义每个页面,页面的宽高度也是先设定一个值,之后再通过js动态赋值。现在html和css的代码就大概成功了。要注意,我们是制作插件,所以这个过程中,不能使用id来获取元素,只能通过class来获取元素。
实现后的代码就是这样:
这里要注意一个问题,body也要设置为overflow:hidden,要不然的话,到时出现页面大小变化的时候,右边和下方会出现一个滚动条大小的空白,这就是因为body没有设定overflow:hidden而导致的。
html和css代码搞定了,之后就开始着手处理js代码实现功能了~~
实现功能之前,首先要先了解一下闭包和原型的含义:所谓闭包,就是每个函数或者变量都有一个作用域,比如你将index定义在aa里面,那index的作用域就是在aa这个函数里面,这就是闭包的简单理解。
function aa(){
var index = 1;
}
PS:在必须不可的情况下,才定义全局变量,要不然的话,还是将变量都定义在一个局部作用域中,即定义在函数里面,这样的话,就减少全局变量的污染以及一些风险(比如外部随便更改你全局变量的值,导致一些不可预料的后果)。
原型,就是要涉及到prototype这个属性了,每个对象,都有一个prototype属性,指向它的原型对象,这样,原型对象有的属性已经方法都能被所有的对象实例共享。
同时,还要注意一下,window.onload = function(){}如果在同一个html页面中同时引用两个文件,这两个文件都包含了window.onload = function(){},那么后一个window.onload = function(){}会覆盖前一个window.onload = function(){},所以我们在做插件给别人用的时候,可以用下列代码:
if (addEventListener) {
window.addEventListener('load', 函数名);
} else {
window.attachEvent('onload', 函数名);
}
这样的话,就相当于window.onload = function(){函数名;}了。
接下来开始代码的实现~~
其实js代码的实现主要分两部分,一、初始化页面各种参数,通过document.documentElement.clientWidth来获取页面宽度,document.documentElement.clientHeight来获取页面的高度,然后设置各种参数的值,使页面全屏显示。二、点击左右箭头的移动实现,设置临界值。
接下来分步讲一下具体的实现,首先要设定一个变量,用来记录到达第几个页面,比如设置第一个页面为0,之后根据每个页面前面有几个页面,将页面数乘于页面宽度,就得到div2.style.left应该设置的距离了,注意offsetleft是无法赋值的,只能获取。比如当第一个页面即 0 的时候,其div2.style.left就为0 * document.documentElement.clientWidth,这样就可以不断控制页面的div2.style.left,从而来移动div2这个容器,来使显示的页面不断变化。
然后就是页面左移动的时候,当到达最后一个页面的时候,要确定边界。同时lock是为了防止多次点击,当点击一次之后,页面还没完全切换好,lock设置为0,这样就无法再连续点击,确保不会产生多条线程,影响setTimeout的速度。绿色字体的代码原本是放在setTimeout外面的,可是因为setTimeout会有延迟,所以导致index = index + dir;这句话先于setTime(speed, time, dir, innerIndex);执行,从而导致页面的参数都先增加或者减少1,所以我就把index = index + dir;放进setTimeout里面,这样就能确保是setTime()先执行,才执行index = index + dir;这一条语句了。(将index = index + dir;放在外面的代码看后面的截图,截图中的代码都重新设定了的,也能正常运行。)
Slide.prototype.move = function(speed, time, dir) {
var innerIndex = index;
if (lock == 0) {
lock = 1;
setTimeout(function() {
setTime(speed, time, dir, innerIndex);
index = index + dir;
}, time);
}
}
function setTime(speed, time, dir, innerIndex) { //speed:移动速度;time:每次移动时间;dir:决定左右移动,左为-1,右为1
if (innerIndex == (slidePage.length - 1) && dir == 1) {//左移动且到达最后一个页面的时候
slideContainer.appendChild(slidePage[0]);
innerIndex = innerIndex - dir;
index = innerIndex;
slideContainer.style.left = -innerIndex * cWidth + "px";
}
if (innerIndex == 0 && dir == -1) {//右移动且到达第一页页面的时候
slideContainer.insertBefore(slidePage[slidePage.length - 1], slidePage[0]);
innerIndex = innerIndex - dir;
index = innerIndex;
slideContainer.style.left = -innerIndex * cWidth + "px";
} else if (Math.abs(Math.abs(slideContainer.offsetLeft) - (innerIndex + dir) * cWidth) > 0 && Math.abs((innerIndex + dir) * cWidth - Math.abs(slideContainer.offsetLeft)) >= Math.abs(speed)) {
slideContainer.style.left = slideContainer.offsetLeft + speed + "px";
} else {
slideContainer.style.left = slideContainer.offsetLeft + (Math.abs(slideContainer.offsetLeft) - (innerIndex + dir) * cWidth) + "px";
lock = 0;
return;//注销这一条线程
}
setTimeout(function() {
setTime(speed, time, dir, innerIndex);
}, time)
}
上图为未优化之前的代码,将index = index + dir;放在setTimeout外面之前的代码。
之后我发现其他插件在页面改变大小的时候,页面的大小也会改变,移动还是能正常进行,所以我就用了下面这个事件
window.onresize = function(){
Slide.prototype.initialize(80,20);
}
重新调用了一个初始化函数,可是发现缩小页面的时候,滚动就出问题了,滚动几圈之后,页面变大,就变成了下列这样:不是一个完整的单独的页面。
后来发现,是因为我每次改变页面大小之后,虽然重新初始化了页面的宽高度,可是初始化函数里面少了一句话来初始化页面的left距离。
于是于,我加上了slideContainer.style.left = -index * cWidth + "px";
可是问题来了,现在页面是正确了,怎么缩小都可以了,可是出现了这个:右边和下方出现了滚动条大小的空白,于是乎,才发现body没有设置overflow:hidden,这样才能保证页面内容超过body大小的时候,不会出现滚动条。
至此,滚动插件已经做好了,用户调用的时候只需要引入css文件和js文件,套用html代码模板,js代码写上new slide = Slide(); initial(xx,xx);
这两句话,就可以直接调用了。