手记

原生javascript实现QQ面板拖动、下拉

1、index.html(html页面)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>QQ面板拖动、下拉</title>
    <link href="css/main.css" rel="stylesheet" />
    <script src="js/domUtil.js"></script>
    <script src="js/eventUtil.js"></script>
    <script src="js/drag.js"></script>
</head>
<body>
    <div class="loginPanel" id="loginPanel">
         <div >
            <div class="ui_boxyClose" id="ui_boxyClose"></div>
        </div>
        <div class="login_logo_webqq"></div>
        <div class="inputs">
            <div class="sign-input"><span>帐 号:</span><span><input autocomplete="on" name="u" id="u" type="text"  class="input01" tabindex="1" value="QQ号码或Email帐号" onFocus="if (value =='QQ号码或Email帐号'){value =''}" onBlur="if (value ==''){value='QQ号码或Email帐号';}" /></span></div>
            <div class="sign-input"><span>密 码:</span><span><input name="p" id="p" maxlength="16" type="password" class="input01" tabindex="2" /></span></div>
        </div>

        <div class="bottomDiv">
            <div class="btn" ></div>
            <div>
                <div id="loginState" class="login-state-trigger login-state-trigger2 login-state" title="选择在线状态">
                    <div id="loginStateShow" class="login-state-show online">状态</div>
                    <div class="login-state-down">下</div>
                    <div class="login-state-txt" id="login2qq_state_txt">在线</div>
       <ul id="loginStatePanel" class="statePanel login-state" >
        <li id="online" class="statePanel_li">
            <div class="stateSelect_icon online"></div>
            <div class="stateSelect_text">我在线上</div>
        </li>
        <li id="callme" class="statePanel_li">
            <div class="stateSelect_icon callme"></div>
            <div class="stateSelect_text">Q我吧</div>
        </li>
        <li id="away" class="statePanel_li">
            <div class="stateSelect_icon away"></div>
            <div class="stateSelect_text">离开</div>
        </li>
        <li id="busy" class="statePanel_li">
            <div class="stateSelect_icon busy"></div>
            <div class="stateSelect_text">忙碌</div>
        </li>
        <li id="silent" class="statePanel_li">
            <div class="stateSelect_icon silent"></div>
            <div class="stateSelect_text">请勿打扰</div>
        </li>
        <li id="hidden" class="statePanel_li">
            <div class="stateSelect_icon hidden"></div>
            <div class="stateSelect_text">隐身</div>
        </li>
    </ul>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

2、css/main.css(页面样式)

  .loginPanel {
            width: 380px;
            height: 247px;
            left: 400px;
            top: 120px;
            position: absolute;
            border: 1px solid #ccc;
            background: #f6f6f6;
            -moz-border-radius: 10px;
            -webkit-border-radius: 10px;
            border-radius: 10px;
            -moz-box-shadow: 0 0 8px #000;
            -webkit-box-shadow: 0 0 8px #000;
            box-shadow: 0 0 8px #000;
        }

        .login_logo_webqq {
            background: url('../images/login_window_logo.png') no-repeat -210px -0px;
            margin-left: 100px;
            margin-top: 10px;
            width: 200px;
            height: 44px;
            cursor: move;
        }

        .inputs {
            font: bold 15px arial;
            margin-left: 80px;
            margin-top: 30px;
        }

            .inputs .sign-input {
                padding-bottom: 20px;
            }

                .inputs .sign-input input {
                    width: 170px;
                    border: 1px #ccc solid;
                    color: #868686;
                    font-size: 16px;
                    padding: 2px;
                    -moz-border-radius: 10px;
                    -webkit-border-radius: 10px;
                    -khtml-border-radius: 10px;
                    -border-radius: 10px;
                    outline: none;
                }

        .btn {
            background: url("../images/login_btn.png") no-repeat -111px 0;
            width: 111px;
            height: 36px;
            border: 0;
            text-align: center;
            line-height: 20px;
            color: #0C4E7C;
            cursor: pointer;
            margin-left: 14px;
        }

        .login-state-trigger {
            cursor: pointer;
            display: block;
            float: left;
            height: 16px;
            overflow: hidden;
            width: 120px;
            margin: 4px 0 0 0;
        }

        .login-state-trigger2 {
            margin: 10px 0 0 20px;
        }

        .login-state-down {
            background: url("../images/ptlogin.png") no-repeat scroll 0 -22px transparent;
            float: left;
            height: 6px;
            margin-top: 5px;
            overflow: hidden;
            text-indent: -999em;
            width: 7px;
        }

        .login-state-show {
            float: left;
            height: 14px;
            overflow: hidden;
            text-indent: -999em;
            width: 14px;
            margin: 1px 4px 0 0;
        }

        .login-state-txt {
            float: left;
            margin-left: 5px;
            font-size: 12px;
            >line-height:18px!important;
        }

        .login-state .callme {
            background: url("../images/ptlogin.png") -72px 0 no-repeat;
        }

        .login-state .online {
            background: url("../images/ptlogin.png") 0 0 no-repeat;
        }

        .login-state .away {
            background: url("../images/ptlogin.png") -18px 0 no-repeat;
        }

        .login-state .busy {
            background: url("../images/ptlogin.png") -36px 0 no-repeat;
        }

        .login-state .silent {
            background: url("../images/ptlogin.png") -108px 0 no-repeat;
        }

        .login-state .hidden {
            background: url("../images/ptlogin.png") -54px 0 no-repeat;
        }

        .statePanel {
            display: none;
            position: absolute;
            right: 68px;
            top: 193px;
            z-index: 10;
            margin: 0;
            border-width: 1px;
            border-style: solid;
            border-color: #ccc #6a6a6a #666 #cdcdcd;
            padding: 0;
            width: 100px;
            height: 133px;
            overflow: hidden;
            background: white;
            font-size: 12px;
            line-height: 1.5;
        }

            .statePanel .statePanel_li {
                display: block;
                float: left;
                margin: 0;
                padding: 3px 0;
                width: 100px;
                height: 16px;
                line-height: 16px;
                overflow: hidden;
                zoom: 1;
                cursor: pointer;
            }

        .stateSelect_icon {
            float: left;
            margin: 2px 0 0 5px;
            width: 14px;
            height: 14px;
            overflow: hidden;
        }

        .stateSelect_text {
            margin: 0 0 0 22px;
        }

        .bottomDiv {
            margin-left: 70px;
        }

        .ui_boxyClose{width:28px;height:28px;position:absolute;top:-10px;right:-10px;cursor:pointer;background:url('../images/boxy_btn.png') no-repeat;z-index:1}.ie6_0 .ui_boxyClose{background:0;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='boxy_btn.png',sizingMethod='scale')}

3、js/domUtil.js(获取节点相关操作)

//封装获取dom的相关操作,解决浏览器兼容性问题
var domUtil = {
    //由于多次使用document.getElementById,所以进行封装
    getById:function(id){
        return document.getElementById(id);
    },
    /*由于document.getElementsByClassName在IE10以下不兼容,所以进行封装
      思路:将class与parent下面的所有元素(parent.getElementsByTagName('*'))的className进行对比,匹配上压入数组
    */
    getByClass:function(clsName,parentID){
        var oParent = parentID?this.getById(parentID):document,
            eles = [],//返回的元素数组
            allChilds = oParent.getElementsByTagName('*');//oParent下面的所有子孙元素
        for (var i = 0; i < allChilds.length; i++) {
            if(clsName==allChilds[i].className){
                eles.push(allChilds[i]);
            }
        }
        return eles;
    },
    getWindowWidth:function(){
        return document.documentElement.clientWidth || document.body.clientWidth;//页面宽度
    },
    getWindowHeight:function(){
        return document.documentElement.clientHeight || document.body.clientHeight;//页面高度
    }
}

4、js/eventUtil.js(事件操作程序相关封装)

//使用变量,类似JSON类型的js对象方式进行事件处理程序的相关逻辑封装,主要解决了浏览器兼容性问题
//单独写进js中,便于html多次进行调运
var eventUtil = {
    //添加事件
    addEventHandle:function(element,eventType,fn){
        if(element.addEventListener){//非IE
            element.addEventListener(eventType,fn,false);
        }else if(element.attachEvent){//IE
            element.attachEvent('on'+eventType,fn);//这里拼接上'on',调运的时候不要加on,使用click等。
        }else{//不支持DOM2级,使用DOM0级方式
            element['on'+eventType] = fn;//这里使用[]方式实现对象的属性添加,相当于.的作用
        }
    },
    //删除事件
    removeEventHandle:function(element,eventType,fn){
        if(element.removeEventListener){//非IE,不带'on'
            element.removeEventListener(eventType,fn,false);//这里传入fn,是因为DOM2级或DOM0级都可以一次给一个元素的同一个事件绑定多个程序,所以需要传入具体的程序fn进行删除
        }else if(element.detachEvent){//IE,带'on'
            element.detachEvent('on'+eventType,fn);
        }else{//不支持DOM2级,使用DOM0级方式
            element['on'+eventType] = fn;
        }

    },
    //获取事件对象
    getEvent:function(event){
       return event?event:window.event;
    },
    //获取事件类型
    getType:function(event){
        return event.type;
    },
    //获取执行事件的目标元素
    getTarget:function(event){
        return event.target||event.srcElement;
    },
    //禁用默认行为
    preventDefault:function(event){
        if(event.preventDefault){
            event.preventDefault();//非IE
        }else{
            event.returnValue = false;//针对IE
        }

    },
    //阻止传播冒泡
    stopPropagation:function(event){
        if(event.stopPrapagation){
            event.stopPrapagation();//非IE
        }else{
            event.cancelBubble = true;//针对IE
        }
    }
}

5、js/drag.js(页面逻辑)

window.onload = drag;//页面加载完成以后,此时是能够拿到所有的dom节点的
function drag(){
    var oDown = domUtil.getByClass('login_logo_webqq','loginPanel')[0];
    //拖曳
    oDown.onmousedown=fnDown;
    //关闭窗口
    var oClose = domUtil.getById('ui_boxyClose');
    oClose.onclick = function(){
        domUtil.getById('loginPanel').style.display = 'none';
    };
    //切换状态
    var oState = domUtil.getById('loginState'),//状态最终显示的目标区域,这个区域用于点击展开状态列表,同时接收显示最终选择的状态
        oStateImg = domUtil.getById('loginStateShow'),//状态显示样式,例如图标
        oStateTxt = domUtil.getById('login2qq_state_txt'),//状态文本信息
        oStatePanel = domUtil.getById('loginStatePanel'),//状态面板ul,状态元素的外层
        oStateLis = oStatePanel.getElementsByTagName('li');//所有的状态元素数组

    //显示状态面板,事件需要阻止冒泡,因为后面document的onclick需要隐藏状态面板,这里不阻止的话,会冒泡,导致先显示,后隐藏的逻辑
    oState.onclick = function(){
        eventUtil.stopPropagation(event);
        oStatePanel.style.display = 'block';
    };
    //页面所有地方点击,都要关闭状态面板
    document.onclick = function(){
        oStatePanel.style.display = 'none';
    };
    //每一个状态的元素添加移入、移出、点击事件
    for (var i = 0; i < oStateLis.length; i++) {
        var oStateLi = oStateLis[i];
        oStateLi.onmouseover = function(event){
            this.style.background='#567';
        };
        oStateLi.onmouseout = function(event){
            this.style.background='#fff';
        };
        //点击状态元素:
        //1、隐藏状态面板;注意下面也要阻止冒泡,因为上层的oState的onclick需要显示状态面板
        //2、替换当前状态id值(id值在css里面有同名的class)到loginStateShow元素的class上去,保证图标的显示
        //3、替换当前文本到login2qq_state_txt的文本
        oStateLi.onclick = function(event){
            eventUtil.stopPropagation(event);
            oStatePanel.style.display = 'none';//1
            oStateImg.className = 'login-state-show '+this.id;//2
            oStateTxt.innerHTML = domUtil.getByClass('stateSelect_text',this.id)[0].innerHTML;//这里不要用innerText,因为firefox不存在innerText属性
        };
    }
}
/*鼠标在区域内按下动作 执行逻辑
1、鼠标变成+形状,这个由css设置
2、面板在整个页面内跟随移动,需要为document注册onmousemove事件
3、在鼠标松开情况下,面板停止移动,为document注册onmouseup事件
因为不考虑同一事件注册多个程序的情况,所以我们使用DOM0级方式即可。
*/
function fnDown(event){
    event = eventUtil.getEvent(event);//获取事件对象,此时为鼠标按下事件
    var oDrag = domUtil.getById('loginPanel'),//需要拖曳的面板对象
        dx = event.clientX-oDrag.offsetLeft,//光标与面板之间的x距离
        dy = event.clientY-oDrag.offsetTop;//光标与面板之间的y距离
    document.onmousemove = function(event){
        event = eventUtil.getEvent(event);
        fnMove(oDrag,event,dx,dy);
    };    
    document.onmouseup = function(){
        document.onmousemove = null;
    }
}
//需要根据光标位置和光标相对面板的距离,为面板设置left和top,而且为了保证面板能够完全在页面显示,需要对每个临界点进行特殊处理
//临界点有四个:
//1、面板向左不能小于0 
//2、面板向右:不能大于屏幕宽度-10-面板宽度(还是考虑关闭图标右边额外占用的10px)
//3、面板向上不能小于10(因为关闭图标在上面、右边分别额外占用了10px,得考虑进来)
//4、面板向下:不能大于屏幕高度-面板高度
function fnMove(element,event,disX,disY){
    var l = event.clientX-disX,//移动后的面板left
        t = event.clientY-disY,//移动后的面板top
        winW = domUtil.getWindowWidth(),//页面宽度
        winH = domUtil.getWindowHeight(),//页面高度
        maxL = winW -10-element.offsetWidth,//面板最大left
        maxT = winH-element.offsetHeight;//面板最大top
    if(l<0){
        l = 0;
    }else if(l>maxL){
        l = maxL;
    }
    if(t<10){
        t = 10;
    }else if(t>maxT){
        t = maxT;
    }    
    element.style.left = l+'px';
    element.style.top = t+'px';
}
5人推荐
随时随地看视频
慕课网APP

热门评论

粉丝一个经过此地  特来报道

查看全部评论