分类
JS,JQ菜鸟

ctrip 酒店位置浮层&tab

要实现一个这样的交互,可以归纳下功能点:

  1. 浮层的显示/隐藏
  2. 商业区/行政区/地铁线tab切换
  3. 地铁线点击显示地铁站点
  4. 选择地标返回内容到文本框内

浮层的显示/隐藏

浮层的显示和隐藏我们通过input的focus和blur事件来做控制,这里要注意的是在显示的时候需要进行定位,通过input相对于浏览器窗口左上角的偏移加上滚动条高度(宽度)就能对浮层进行绝对定位。

var input1 = document.getElementById('input1');
var inputFocus = function(){
    var rect = this.getBoundingClientRect(); //获取文本框input相对于浏览器窗口左上角的偏移
    var top = rect.top + (document.documentElement.scrollTop || document.body.scrollTop),
        left = rect.left + (document.documentElement.scrollLeft || document.body.scrollLeft);
    var style = document.getElementById('popArea').style;
    style.left = left + 'px';
    style.top = top + this.offsetHeight + 'px'; //input相对文档的高度 + 文本框自身高度
    style.display = 'block';
}
var inputBlur = function(e){
    document.getElementById('popArea').style.display = 'none';
}
addEvent(input1, 'focus', inputFocus); //绑定focus事件
addEvent(input1, 'blur', inputBlur); //绑定blur事件

商业区/行政区/地铁线tab切换

实现tab一般为两种方法:给每个tab分别绑定事件或者对所有tab共同的父元素绑定一个事件(子项会动态变化或者子项过多时使用此方法),如果tab项不多时可以循环遍历来改变样式和display,反之可以使用全局变量来存放当前选中项。这里使用了前者。

var popArea = document.getElementById('popArea');
var tabs = popArea.getElementsByTagName('li'),
    subs = popArea.getElementsByTagName('dd');
var handlerTab = function(){
    for(var i=0,l=tabs.length;i<l;i++){
        if(this == tabs[i]){    //this为当前触发事件的element
            tabs[i].className = 'hot_selected2';
            subs[i].style.display = '';
        }else{
            tabs[i].className = '';
            subs[i].style.display = 'none';
        }
    }
}
for(var i=0,l=tabs.length;i<l;i++){
    addEvent(tabs[i], 'mousedown', handlerTab); //为什么用mousedown而不是click?
}

地铁线点击显示地铁站点 & 选择地标返回内容到文本框内

这两个功能都是在点击浮层时触发,并且由于浮层内容多、经常变化的特点,我们就应该选择给浮层添加事件而不是每个内容绑事件,然后根据事件源来执行相应的代码,一劳永逸。

addEvent(popArea, 'mousedown', function(e){ //为什么用mousedown而不是click?
    e = e || window.event;
    var el = e.target || e.srcElement;
    if (el.tagName.toLowerCase() == 'a') {
        switch(el.className){   //用class判断来源
            /*
             * 地铁线点击显示地铁站点
             * 这边可以视为选项卡的一种类型,即选项卡子项很多,对应显示内容不是模块之间的显示隐藏,而是获取新的HTML替换
             * 选项卡选项发生改变时,通过之前记录的ID来获取element,还原该element的样式,然后记录当前子项ID并改变其样式
             * 生成HTML有两种方法:一是拼写html字符串通过innerHTML一次性赋值;二是createElement节点再通过appendChild添加。从效率上说innerHTML效率更高,而拼写字符串也有多种方法,可以使用数组的join方法连接和使用"+"连接,join方法效率更高。
             */
            case "metro":
                var station = document.getElementById('metroStation');
                var metroId = station.getAttribute('metroId');
                if(metroId){
                    document.getElementById(metroId).className = 'metro';   //还原前选项卡样式
                }
                el.className = 'metro current';
                station.setAttribute('metroId', el.id); //记录当前选项卡ID
                var res = ['<a href="javascript:;" data="'+ el.getAttribute('metrodata') +'" title="'+ el.innerHTML +'">全部</a>'];             var kw = '('+el.innerHTML+')';
                var reg = new RegExp('@[^\\|]*\\|(([^\\|]*)'+ kw.replace(/([\.\\\/\+\*\?\[\]\{\}\(\)\^\$\|])/g,"\\$1") +'[^\\|]*)\\|(S\\d+)\\|[^@]*', "gi");
                //通过正则来获取匹配的内容并拼写html
                POSITION = POSITION.replace(reg, function (_, name, label, id) {
                    res.push('<a href="javascript:;" data="'+ _ +'" title="'+ name +'">'+ label +'</a>');
                    return '';
                });
                if(res.length){
                    station.innerHTML = res.join('');
                    station.style.display = '';
                    }else{
                    station.innerHTML = '';
                    station.style.display = 'none';
                }
                break;
            case "metro current":
                break;
            default:    //选择地标返回内容到文本框内
                if(currentInput){
                    currentInput.value = el.getAttribute('data').split('|')[1];
                }
                return; //直接结束事件,作用是为了触发input的blur事件
        }
    }
 
    if(!-[1,]){
        el.setCapture();    //解决IE下任然触发blur事件的bug
        capture = el;
    }  
    e.preventDefault ? e.stopPropagation() : e.cancelBubble = true;
    e.preventDefault ? e.preventDefault() : e.returnValue = false;  //阻止input触发blur事件
});
if(!-[1,]){
    addEvent(popArea, 'mouseup', function(){    //释放鼠标捕获
        if(capture && capture.releaseCapture){
            capture.releaseCapture();
            capture = null;
        }
    })
}

一次鼠标点击会触发的事件有mousedown、click、mouseup,双击还有dblclick,为什么这里使用了mousedown而不是click或者其他的类型?原因是执行顺序。

(function(){
    var input = document.getElementById('input'),
    container = document.getElementById('container'),
    popArea = document.getElementById('popArea');
    var consoleType = function(e){
        e = e || window.event;
        container.innerHTML += e.type + '<br/>';
    }
    addEvent(input, 'blur', consoleType);
    addEvent(popArea, 'mousedown', consoleType);
    addEvent(popArea, 'click', consoleType);
    addEvent(popArea, 'mouseup', consoleType);
})()

这段代码的返回结果是mousedown、blur、mouseup、click,当我们点击浮层的同时不希望input失去焦点,只有在blur触发之前通过preventDefault()来阻止,符合条件的只有mousedown,这就是为什么我们使用mousedown而不使用click的原因。

这个在标准浏览器下是可以的,但是在IE下,这个似乎不行,我们会发现,在IE下,当我们点击这个浮层的时候,input还是失去了焦点,浮层还是隐藏了。为了解决IE下面的问题,我们需要用到IE的setCapture。其作用:http://baike.baidu.com.cn/view/1080215.htm

Demo:http://unicac.sinaapp.com/jquery/ctrip_tab/demo_tab.html

unicac

...long long ago,he have a dream.

发表评论

邮箱地址不会被公开。 必填项已用*标注