最近去面試了,對方要求我在一個星期內用原生的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);
這兩句話,就可以直接調用了。