更新于

JS 修缮拖拽

发布于 / 分类: JavaScript / 暂无评论 / 阅读量: 50

之前有一篇js的登录框拖拽文章,后面又对事件绑定做了完善,但是当时拖拽 用的还是传统事件绑定,这次就做一下修缮。

首先就是将对应的事件绑定改为现代事件绑定

旧版拖拽功能:

Base.prototype.drag = function() {
        for (var i = 0; i < this.arr.length; i++) {
            this.arr[i].onmousedown = function(event) {
                preDef(event);
                var e = getEvent(event);
                var _this = this;
                var ctop = e.clientY - _this.offsetTop;
                var cleft = e.clientX - _this.offsetLeft;
                if (typeof _this.setCapture != 'undefined') {
                    _this.setCapture();
                }
                document.onmousemove = function(event) {
                    var e = getEvent(event);
                    var logintop = e.clientY - ctop;
                    var loginleft = e.clientX - cleft;
                    if (logintop < 0) {
                        logintop = 0;
                    } else if (logintop > getInner().height - login.offsetHeight) {
                        logintop = getInner().height - login.offsetHeight;
                    }
                    if (loginleft < 0) {
                        loginleft = 0;
                    } else if (loginleft > getInner().width - login.offsetWidth) {
                        loginleft = getInner().width - login.offsetWidth;
                    }
                    login.style.top = logintop + 'px';
                    login.style.left = loginleft + 'px';
                }
                document.onmouseup = function() {
                    this.onmousemove = null;
                    this.onmouseup = null;
                    if (typeof this.releaseCapture != 'undefined') {
                        _this.releaseCapture();
                    }
                }

            }
            return this;
        }

        //跨浏览返回event

        function getEvent(e) {
            return e || window.event;
        }
//跨浏览器阻止默认行为
function preDef(event) {
    var e = event || window.event;
    if (typeof e.preventDefault != 'undefined') {
        e.preventDefault();
    } else {
        e.returnValue = false;
    }
}

在事件绑定中,我们搞定了事件对象event的传输,所以这里不需要跨浏览返回event函数了,然后我们又对ie做了模拟w3c的属性调用,其中就模拟出了preventDefault 阻止默认行为,这里也不需要跨浏览器阻止默认行为的兼容函数了,改动之后:

新的拖拽功能:

Base.prototype.drag = function() {
        for (var i = 0; i < this.arr.length; i++) {
            addEvent(this.arr[i], 'mousedown', function(e) {
                e.preventDefault();
                var _this = this;
                var ctop = e.clientY - _this.offsetTop;
                var cleft = e.clientX - _this.offsetLeft;
                if (typeof _this.setCapture != 'undefined') {
                    _this.setCapture();
                }
                addEvent(document, 'mousemove', function(e) {
                    var logintop = e.clientY - ctop;
                    var loginleft = e.clientX - cleft;
                    if (logintop < 0) {
                        logintop = 0;
                    } else if (logintop > getInner().height - login.offsetHeight) {
                        logintop = getInner().height - login.offsetHeight;
                    }
                    if (loginleft < 0) {
                        loginleft = 0;
                    } else if (loginleft > getInner().width - login.offsetWidth) {
                        loginleft = getInner().width - login.offsetWidth;
                    }
                    login.style.top = logintop + 'px';
                    login.style.left = loginleft + 'px';
                });
                addEvent(document, 'mouseup', function() {
                    this.onmousemove = null;
                    this.onmouseup = null;
                    if (typeof this.releaseCapture != 'undefined') {
                        _this.releaseCapture();
                    }
                });
            });
            return this;
        }

将原来的传统事件改为现代事件,然后preDef(event);改为e.preventDefault();获取event的函数 var e = getEvent(event);删除,将事件函数传入的参数event改为e。

以上改动完毕,但是会产生bug,用户无法在输入框输入,原因是因为在mousedown用户点击后,我们e.preventDefault();禁止了默认行为。用户无法点击,然后ie因为要修复鼠标移动到窗口外依旧可以产生滚动条从而使用了_this.setCapture();禁止用户选择,这里我们要修复一下。

修复无法输入:

e.preventDefault();我们要做个判断,这个函数的使用初衷是因为火狐浏览器在元素login中没有东西的时候无法移动而使用的,所以这里我们要做个判断,当login没有内容的时候才禁止默认行为。

但是我们要考虑到即便我们将login元素中的代码删除,但是它依旧会产生空白节点,所以我们在判断的时候要将这个空白节点忽略,最直白的方法就是在判断的时候删除这个节点,然后再判断里面是否有内容,这里就要用正则了。

if (removeSpen(this.innerHTML).length == 0) e.preventDefault();

//删除空白节点
function removeSpen(html) {
  return  html.replace(/(^\s*)|(\s*$)/g,'')
}

将login中的html传入函数中,在函数值将html内容中的空白节点替换,然后替换无内容,相当于删除了,然后再判断,如果说这个login中没有内容了,删除空白节点后他的length只能是0,那么等于0的话,就可以执行阻止默认行为。

修复ie的_this.setCapture();

使用这个的初衷是因为当用户将login一直拖拽,直到鼠标已经超出ie的窗口是产生滚动条,使用这个可以让用户选择不到这个元素,那么既然他是在拖拽的过程中使用的,那么就应该丢到mousemove事件中去,于是:

 addEvent(document, 'mousemove', function(e) {
     var logintop = e.clientY - ctop;
     var loginleft = e.clientX - cleft;
     if (logintop < 0) {
         logintop = 0;
     } else if (logintop > getInner().height - login.offsetHeight) {
         logintop = getInner().height - login.offsetHeight;
     }
     if (loginleft < 0) {
         loginleft = 0;
     } else if (loginleft > getInner().width - login.offsetWidth) {
         loginleft = getInner().width - login.offsetWidth;
     }
     login.style.top = logintop + 'px';
     login.style.left = loginleft + 'px';
     //丢到这里
     if (typeof _this.setCapture != 'undefined') {
         _this.setCapture();
     }
 });

在移动的过程中使用 _this.setCapture();当鼠标移开login时mousemove又会被删除。

但是实际上使用了现代函数后,无法通过传统函数this.onmousemove = null; this.onmouseup = null;来删除对应的事件,所以这里又会产生一个新的问题,用户拖拽后一直在拖拽的状态,无法关闭,这里就要使用到删除事件函数了。

修复无法删除事件绑定:

Base.prototype.drag = function() {
    for (var i = 0; i < this.arr.length; i++) {
        addEvent(this.arr[i], 'mousedown', function(e) {
            if (removeSpen(this.innerHTML).length == 0) e.preventDefault();
            var _this = this;
            var cleft = e.clientX - _this.offsetLeft;
            var ctop = e.clientY - _this.offsetTop;
            var login_titile = document.getElementById('login_titile');
            
                addEvent(document, 'mousemove', move);
                addEvent(document, 'mouseup', up)

            function move(e) {
                var left = e.clientX - cleft;
                var top = e.clientY - ctop;
                if (left < 0) {
                    left = 0;
                } else if (left > getInner().width - _this.offsetWidth) {
                    left = getInner().width - _this.offsetWidth;
                }
                if (top < 0) {
                    top = 0;
                } else if (top > getInner().height - _this.offsetHeight) {
                    top = getInner().height - _this.offsetHeight;
                }
                _this.style.top = top + 'px';
                _this.style.left = left + 'px';
                if (typeof _this.setCapture != 'undefined') {
                    _this.setCapture();
                }
            }

            function up() {
                removeEvent(document, 'mousemove', move);
                removeEvent(document, 'mouseup', up);
                if (typeof _this.releaseCapture != 'undefined') {
                    _this.releaseCapture();
                }
            }

            function removeSpen(html) {
                return html.replace(/(^\s*)|(\s*$)/g, '');
            }
        });
    }
    return this;
}

上一章我们讲过,删除事件无法删除匿名函数,我们mousemove和mouseup使用的都是匿名函数,所以这里分别各创建一个函数,然后再去调用,删除的时候就可以使用对应的函数名删除了。

到现在,还有一些小问题,比如用户无法选择login中的文本内容,用户一点击就会产生拖拽。

修复无法选中内容:

其实产生这个问题的原因是我们将login整个元素作为了一个触发器,用户选择时自然要点击然后移动,这样和拖拽的事件一样了,从而导致无法选中,我们只需要将拖拽的触发器改为小一点地方,比如顶部标题:

我们的html结构是这样的:

<div id="login">
    <i class="login_off"><span class="off">+</span></i>
    <h2 id="login_title">网站登录</h2>
    <form action="" id="login_form">
        <div class="user">账 号:<input type="text" name="user" class="text" /></div>
        <div class="pass">密 码:<input type="password" name="pass" class="text" /></div>
        <div class="buttom"><input type="button" name="submit" value="登录" class="submit" /></div>
        <div class="other">注册新用户 | 忘记密码?</div>
    </form>
</div>

我们将触发区域改为login中的h2元素上,h2的class改为id,然后判断:

Base.prototype.drag = function() {
    for (var i = 0; i < this.arr.length; i++) {
        addEvent(this.arr[i], 'mousedown', function(e) {
            if (removeSpen(this.innerHTML).length == 0) e.preventDefault();
            var _this = this;
            var cleft = e.clientX - _this.offsetLeft;
            var ctop = e.clientY - _this.offsetTop;
            //获取h2元素
            var login_titile = document.getElementById('login_title');
           //判断当用户选择的元素是h2的时候才执行事件绑定
            if(e.target === login_title) {
                addEvent(document, 'mousemove', move);
                addEvent(document, 'mouseup', up)
            }

            function move(e) {
                var left = e.clientX - cleft;
                var top = e.clientY - ctop;
                if (left < 0) {
                    left = 0;
                } else if (left > getInner().width - _this.offsetWidth) {
                    left = getInner().width - _this.offsetWidth;
                }
                if (top < 0) {
                    top = 0;
                } else if (top > getInner().height - _this.offsetHeight) {
                    top = getInner().height - _this.offsetHeight;
                }
                _this.style.top = top + 'px';
                _this.style.left = left + 'px';
                if (typeof _this.setCapture != 'undefined') {
                    _this.setCapture();
                }
            }

            function up() {
                removeEvent(document, 'mousemove', move);
                removeEvent(document, 'mouseup', up);
                if (typeof _this.releaseCapture != 'undefined') {
                    _this.releaseCapture();
                }
            }

            function removeSpen(html) {
                return html.replace(/(^\s*)|(\s*$)/g, '');
            }
        });
    }
    return this;
}

以上对拖拽功能的修缮就差不多了,下面对遮罩层修缮

遮罩层修缮

我之前是创建了一个元素,然后让这个元素显示,但是旧版的谷歌和ie11都会有一个很头疼的问题,当用户在遮罩层区域左键选中然后下拉的时候会选中到遮罩层下面的内容,这个不是很大麻烦,大麻烦是是当用户下拉后,会产生滚动条,然后遮罩层完全被无视了。

这里我们可以对用户点击事件做一个默认行为阻止,你可以在遮罩层的函数里加上

addEvent(document, 'mousemove', function(e) {
    e.preventDefault()
});

但是这样也会有一些问题,而且太极端了,可能会产生其他问题,这里我们使用另一种方法,用户往下拉不是会产生滚动条吗,那么就对这个滚动条做文章。

//设置遮罩层
Base.prototype.Masked = function() {
    for (var i = 0; i < this.arr.length; i++) {
        var preSlg = this.arr[i].previousSibling;
        if (typeof preSlg == 'undefined' || preSlg.className != 'login_Masked') {
            var div = document.createElement('div');
            this.arr[i].parentNode.insertBefore(div, this.arr[i]);
            div.className = 'login_Masked';
            div.style.width = getInner().width + 'px';
            div.style.height = getInner().height + 'px';
            //对滚动条清零
            addEvent(window, 'scroll', scrollTop);
        } else if (preSlg.className == 'login_Masked') {
            preSlg.style.width = getInner().width + 'px';
            preSlg.style.height = getInner().height + 'px';
        }
    }
    return this;
}

//滚动条清零
function scrollTop() {
    document.documentElement.scrollTop = 0;
    document.body.scrollTop = 0;
}

展开遮罩层后对window添加scroll事件,然后用户每下拉一下,scroll的top总是为0。

既然你添加的事件自然要删除了,不然用户关闭login后无法滚动了,那么也是一样,在关闭遮罩层的时候删除这个事件就行了。

//删除遮罩层
Base.prototype.removeMasked = function() {
    for (var i = 0; i < this.arr.length; i++) {
        if (this.arr[i].previousSibling.className == 'login_Masked') {
            this.arr[i].parentNode.removeChild(this.arr[i].previousSibling);
            removeEvent(window, 'scroll', scrollTop);
        }
    }
    return this;
}

遮罩层处理好了,我们现在对居住函数做一个现代事件绑定修缮。

自动居住修缮

居住的时候我们使用了resize事件,这里改动如下:

//自动居中
Base.prototype.resize = function(fn) {
    for (var i = 0; i < this.arr.length; i++) {
        var element = this.arr[i];
        addEvent(window, 'resize', function() {
            fn();
            if (element.offsetLeft > getInner().width - element.offsetWidth) {
                element.style.left = getInner().width - element.offsetWidth + 'px';
            }
            if (element.offsetTop > getInner().height - element.offsetHeight) {
                element.style.top = getInner().height - element.offsetHeight + 'px';
            }
        });
    }
    return this;
}

这里没啥好讲的,就是将传统事件改为现代事件。

以上就是全部的修缮了。

暂无评论

设置
配色方案

布局