平常做移動端會用到tab選項卡,這和PC端有些區別,移動端是觸摸滑動切換,PC端是點擊、移入切換。
這里滑動切換就是一個移動端事件的應用,這里主要用到的觸摸事件:touchstart、touchmove、touchend。
和做其他的效果一樣,先有html結構,css樣式修飾,再寫JS代碼。
html:
<div class="mtabs" id="tabs"> <ul class="mhead"> <li>tab1</li> <li>tab2</li> <li>tab3</li> </ul> <div class="mcontent"> <ul> <li>tab1內容內容內容內容</li> <li>tab1內容內容內容內容</li> <li>tab1內容內容內容內容</li> <li>tab1內容內容內容內容</li> <li>tab1內容內容內容內容</li> </ul> <ul> <li>tab2內容內容內容內容</li> <li>tab2內容內容內容內容</li> <li>tab2內容內容內容內容</li> <li>tab2內容內容內容內容</li> <li>tab2內容內容內容內容</li> </ul> <ul> <li>tab3內容內容內容內容</li> <li>tab3內容內容內容內容</li> <li>tab3內容內容內容內容</li> <li>tab3內容內容內容內容</li> <li>tab3內容內容內容內容</li> </ul> </div> </div><!-- End .mtabs -->
css:
body,div,ul,li{ margin:0; padding:0; } ul,li { list-style:none; } body { font-size:100%; font-family:Helvetica,STHeiti,Droid Sans Fallback; } .mtabs { width:100%; overflow:hidden; } .mhead { height:38px; border-top:2px solid #9ac7ed; background:#ECF2F6; -webkit-tap-highlight-color:rgba(0,0,0,0); } .mhead li { position:relative; font-size:1.125em; text-align:center; float:left; width:64px; height:38px; line-height:38px; color:#2a70be; } .mhead li.current { border-top:2px solid #2a70be; margin-top:-2px; background:#FFF; color:#c14545; } .mcontent { width:100%; overflow:hidden; } .mcontent ul { width:100%; float:left; } .mcontent li { height:35px; line-height:35px; font-size:1.125em; padding:0 10px; }
下面的截圖是想要的一個效果預覽:

下面是實際效果,可以在Chrome的移動模式查看:
- tab1
- tab2
- tab3
- tab1內容內容內容內容
- tab1內容內容內容內容
- tab1內容內容內容內容
- tab1內容內容內容內容
- tab1內容內容內容內容
- tab2內容內容內容內容
- tab2內容內容內容內容
- tab2內容內容內容內容
- tab2內容內容內容內容
- tab2內容內容內容內容
- tab3內容內容內容內容
- tab3內容內容內容內容
- tab3內容內容內容內容
- tab3內容內容內容內容
- tab3內容內容內容內容
先貼上JS代碼,供參考
/**
* LBS mTabs
* Date: 2014-5-10
* ===================================================
* opts.mtab tabs外圍容器/滑動事件對象(一個CSS選擇器)
* opts.mhead tabs標題容器/點擊對象(一個CSS選擇器)
* opts.mcontent tabs內容容器/滑動切換對象(一個CSS選擇器)
* opts.index tabs索引(默認0) 指定顯示哪個索引的標題、內容
* opts.current tabs當前項的類名(默認current)
* ===================================================
**/
;(function(){
window.mTabs = function(opts){
if(typeof opts === undefined) return;
//取得tabs外圍容器、標題容器、內容容器
this.mtab = document.querySelector(opts.mtab);
this.mhead = document.querySelector(opts.mhead);
this.mcontent = document.querySelector(opts.mcontent);
//取得標題容器內選項集合、內容容器內容集合
this.mheads = this.mhead.children;
this.mcontents = this.mcontent.children;
this.length = this.mheads.length;
if(this.length < 1) return;
if(opts.index > this.length-1) opts.index = this.length-1;
this.index = this.oIndex = opts.index || 0;
this.current = opts.current || 'current'; //當前活動選項類名
this.touch = {};//自定義一個對象 用來保存手指觸摸相關信息
this.init();
}
mTabs.prototype = {
init: function(opts){
this.set();
this.initset();
this.bind();
},
initset: function(){
for(var i = 0; i < this.length; i++){
this.mheads[i].index = i;//設置了一個屬性 方便點擊時判斷是點了哪一項
this.mheads[i].className = this.mheads[i].className.replace(this.current,'');
this.mcontents[i].className = this.mcontents[i].className.replace(this.current,'');
}//初始化設置、先清空手動加在標題或內容HTML標簽的當前類名(this.current)、再設置哪一項為當前選項並設置類名
this.mheads[this.index].className += ' '+this.current;
this.mcontents[this.index].className += ' '+this.current;
//對應的內容要顯示在可視區域
//this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
//this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
},
set: function(){//獲取瀏覽器的視口寬度、並設置內容容器的寬度、每一項內容區域的寬度,屏幕旋轉,瀏覽器窗口變換會再次設置這些值
this.width = document.documentElement.clientWidth || document.body.clientWidth;
this.mcontent.style.width = this.length * this.width + 'px';
for(var i = 0; i < this.length; i++) this.mcontents[i].style.width = this.width + 'px';//調整在可視區域顯示的內容項
//this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
},
bind: function(){
//綁定各種事件
var _this = this;
this.mtab.addEventListener("touchstart",function(e){
_this.touchStart(e);
}, false);
this.mtab.addEventListener("touchmove",function(e){
_this.touchMove(e);
}, false);
this.mtab.addEventListener("touchend",function(e){
_this.touchEnd(e);
}, false);
this.mtab.addEventListener("touchcancel",function(e){
_this.touchEnd(e);
}, false);
this.mhead.addEventListener("click",function(e){
_this.touchClick(e);
}, false);
this.mcontent.addEventListener('webkitTransitionEnd',function(){
_this.transitionEnd();
}, false);
window.addEventListener("resize", function(){
setTimeout(function(){
_this.set();
},100);
}, false);
window.addEventListener("orientationchange",function(){
setTimeout(function(){
_this.set();
},100);
}, false);
},
touchStart: function(e){
this.touch.x = e.touches[0].pageX;
this.touch.y = e.touches[0].pageY;
this.touch.time = Date.now();
this.touch.disX = 0;
this.touch.disY = 0;
this.touch.fixed = ''; //重要 這里采用了判斷是滾動頁面行為、還是切換選項行為 如果是滾動頁面就在滑動時只滾動頁面 相應的切換選項就切換不會滾動頁面
},
touchMove: function(e){
if(this.touch.fixed === 'up') return;
e.stopPropagation();
if(e.touches.length > 1 || e.scale && e.scale !== 1) return;
this.touch.disX = e.touches[0].pageX - this.touch.x;
this.touch.disY = e.touches[0].pageY - this.touch.y;
if(this.touch.fixed === ''){//行為判斷 采用這種方式 主要解決手指按下移動(左上、右上)時滑動切換和滾動頁面同時執行的問題
if( Math.abs(this.touch.disY) > Math.abs(this.touch.disX) ){
this.touch.fixed = 'up';
}else{
this.touch.fixed = 'left';
}
}
if(this.touch.fixed === 'left'){
e.preventDefault();
if( (this.index === 0 && this.touch.disX > 0) || (this.index === this.length-1 && this.touch.disX < 0) ) this.touch.disX /= 4; //在 第一項向右滑動、最后一項向左滑動 時
//this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + ( this.touch.disX - this.index * this.width ) + "px)";
this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + ( this.touch.disX - this.index * this.width ) + "px,0,0)";
}
},
touchEnd: function(e){
if(this.touch.fixed === 'left'){
var _this = this, X = Math.abs(this.touch.disX);
this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 100ms';
if( (Date.now() - this.touch.time > 100 && X > 10) || X > this.width/2 ){
this.touch.time = Date.now();
this.touch.disX > 0 ? this.index-- : this.index++;
this.index < 0 && (this.index = 0);
this.index > this.length - 1 && (this.index = this.length - 1);
if(this.index === this.oIndex) this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 300ms';
if(this.index !== this.oIndex) this.replace();
}
//this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
}
},
transitionEnd: function(){
this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 0ms';
},
touchClick: function(e){
var target = e.target;
if(target.nodeType === 1 && target.index !== undefined){
if(target.index === this.index) return;
e.preventDefault();
e.stopPropagation();
this.index = target.index;
this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 100ms';
//this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
this.replace();
}
},
replace: function(){
this.mheads[this.index].className += ' '+this.current;
this.mheads[this.oIndex].className = this.mheads[this.oIndex].className.replace(this.current,'').trim();
this.mcontents[this.index].className += ' '+this.current;
this.mcontents[this.oIndex].className = this.mcontents[this.oIndex].className.replace(this.current,'').trim();
this.oIndex = this.index;
}
}
}());
使用方式很簡單,如下
document.addEventListener('DOMContentLoaded',function(){
//use mTabs
new mTabs({
mtab: '#tabs',
mhead: '#tabs .mhead',
mcontent: '#tabs .mcontent'
});
/*new mTabs({
mtab: '#tabs',
mhead: '#tabs .mhead',
mcontent: '#tabs .mcontent',
index: 1,
current: 'active'
});*/
},false);
mtab: <div class="mtabs" id="tabs"> //.. </div> mhead: <ul class="mhead"> //.. </ul> mcontent: <div class="mcontent"> //.. </div>
在此說下思路:
先獲得一個tabs容器對象(mtab),它包含了兩個對應的類集合容器,一個是標簽欄(mhead)、一個是內容欄(mcontent),再分別取得類集合容器里面對應的選項mheads、mcontents。
this.mtab = document.querySelector(opts.mtab); this.mhead = document.querySelector(opts.mhead); this.mcontent = document.querySelector(opts.mcontent); this.mheads = this.mhead.children; this.mcontents = this.mcontent.children;
獲取設備瀏覽器窗口的寬,並更新內容容器(mcontent)的寬,內容項的寬,一般在頁面都會有文檔聲明 <!DOCTYPE html> document.documentElement.clientWidth 就能獲取瀏覽器窗口的寬。
this.width = document.documentElement.clientWidth || document.body.clientWidth; this.mcontent.style.width = this.length * this.width + 'px'; for(var i = 0; i < this.length; i++) this.mcontents[i].style.width = this.width + 'px';
在手指觸摸按上時(在tabs容器對象上), 獲取手指按下時在頁面的位置 ( e.touches[0].pageX)。 touchs想象成有幾根手指,只需要第一根按下的手指( touches[0] )。初始化了一個行為判斷 this.touch.fixed (當在tabs上滑動時是要滾動頁面還是要切換選項卡)。
this.touch.x = e.touches[0].pageX; //.. this.touch.fixed = '';
在移動手指時,做出行為的判斷。先獲得移動的距離(左右方向、上下方向),根據兩個方向的值比較判斷是哪種行為。
this.touch.disX = e.touches[0].pageX - this.touch.x;
this.touch.disY = e.touches[0].pageY - this.touch.y;
//..
if(this.touch.fixed === ''){
if( Math.abs(this.touch.disY) > Math.abs(this.touch.disX) ){
this.touch.fixed = 'up';
}else{
this.touch.fixed = 'left';
}
}
在移動手指時,內容容器(mcontent)也會跟着移動,並且做了在處於第一項和最后一項時的移動限制。
if( (this.index === 0 && this.touch.disX > 0) || (this.index === this.length-1 && this.touch.disX < 0) ) this.touch.disX /= 4;
在手指離開屏幕的時候,做出切換判斷,是向左還是向右。在第一項時,不能向左切換,最后一項時不能向右切換。
this.touch.disX > 0 ? this.index-- : this.index++; this.index < 0 && (this.index = 0); this.index > this.length - 1 && (this.index = this.length - 1);
最后是真正的移動切換,用了css3動畫切換,translateX 或者 translate3d .
//this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
代碼中有個transitionEnd方法,配合webkitTransitionEnd事件在動畫切換執行完成時調用。這里調用這個方法是用來清除動畫定義的持續時間。
transitionEnd: function(){
this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 0ms';
}
this.mcontent.addEventListener('webkitTransitionEnd',function(){
_this.transitionEnd();
}, false);
點擊切換是判斷點擊了哪一項,在初始設置時已經為每一項保存了索引值(index),根據對應的索引值,切換選項,更新狀態。可以循環綁定點擊事件,也可以使用事件委托,這里使用的是事件委托。
this.mheads[i].index = i;
touchClick: function(e){
var target = e.target;
if(target.nodeType === 1 && target.index !== undefined){
//..
this.index = target.index;
//..
this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
}
}
this.mhead.addEventListener("click",function(e){
_this.touchClick(e);
}, false);
tab選項卡主要是獲得兩組對應的類似集合(一組標簽,一組內容),兩個類似數組都有索引值(數組下標),通過這個索引值,做出對應的切換。獲取索引值是tab選項卡的關鍵,移動web端的選項卡主要是增加了觸摸事件操作這個索引值。加上定時器,每隔多少時間增加或者減少這個索引值,自動播放也就完成了。會了tab選項卡也就會了圖片的切換,焦點圖什么的,原理都是一樣。
