JavaScript 实现鼠标拖动元素实例代码


Posted in Javascript onFebruary 24, 2014

一、前言

最开始实现鼠标拖动元素的目的就是在一个页面上拖动很多小圆点,用于固定定位,然后在复制HTML,粘贴在页面的开发代码中,就是这么一个功能,实现了很多遍,都没有做好,不得已采用了jQuery.fn.draggable插件,在接触一些资料和别人的思路,今天终于把这个拖动功能给完善了,下面就来看看它的实现

 
二、设计思路

在拖动元素上绑定鼠标按下事件,在文档对象中绑定鼠标移动,鼠标弹起事件;
为什么不把三个事件都绑定在拖动元素上,这是因为鼠标移动太快时,鼠标移动和弹起事件处理程序将不会执行

$target.bind('mousedown', fn);
$(document)
.bind('mousemove', fn)
.bind('mouseup', fn);

三、源码实现细节

在实现源码中有很多需要值得注意的地方:

1、首先在鼠标按下事件中,当单击拖动元素中,可能会选择区域文字,这并不是我们所需要的,解决方法如下:

// 阻止区域文字被选中 for chrome firefox ie9
e.preventDefault();
// for firefox ie9 || less than ie9
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

2、如果拖动元素是图片(img标签),鼠标在拖动图片一小段距离,会出现一个禁止的小提示,即:图片不能再拖动,
这是浏览器的默认行为,因此只要阻止浏览器默认行为就可以了

e.preventDefault();

3、关于边界(处理拖动范围)的问题

一开始实现的代码如下:

// x,y代表拖动元素将要设置的left,top值,limitObj为拖动区域范围对象,测试时就发现问题,
// 在拖动过程中,拖动对象有时不能直接靠近边界
if ( x >= limitObj._left && x <= limitObj._right ) {
    $target.css({ left: x + 'px' });
}
if ( y >= limitObj._top && y <= limitObj._bottom ) {
    $target.css({ top: y + 'px' });
}

进一步思考:为什么会出现上面问题,原因在于变量x可能会小于limitObj._left或大于limitObj._right,变量y同理,
因此代码需要像下面这样处理:

if (x < limitObj._left) {
    x = limitObj._left;
}
if (x > limitObj._right) {
    x = limitObj._right;
}
if (y < limitObj._top) {
    y = limitObj._top;
}
if (y > limitObj._bottom) {
    y = limitObj._bottom;
}
$target.css({ left: x + 'px', top: y + 'px' });

终于解决了这个问题,但是cloudgamer给出了更好的写法:

$target.css({
    left: Math.max( Math.min(x, limitObj._right),  limitObj._left) + 'px',
    top: Math.max( Math.min(y, limitObj._bottom),  limitObj._top) + 'px'
});

完整程序源码:

$.fn.extend({
    /**
     *   Autor: 博客园华子yjh 2014/02/21
     */
    drag: function(options) {
        var dragStart, dragMove, dragEnd,
            $boundaryElem, limitObj;
        function _initOptions() {
            var noop = function(){}, defaultOptions;
            defaultOptions = { // 默认配置项
                boundaryElem: 'body' // 边界容器
            };
            options = $.extend( defaultOptions, options || {} );
            $boundaryElem = $(options.boundaryElem);
            dragStart = options.dragStart || noop,
            dragMove = options.dragMove || noop,
            dragEnd = options.dragEnd || noop;
        }
        function _drag(e) {
            var clientX, clientY, offsetLeft, offsetTop,
                $target = $(this), self = this;
            limitObj = {
                _left: 0,
                _top: 0,
                _right: ($boundaryElem.innerWidth() || $(window).width()) - $target.outerWidth(),
                _bottom: ($boundaryElem.innerHeight() || $(window).height()) - $target.outerHeight()
            };
            // 记录鼠标按下时的位置及拖动元素的相对位置
            clientX = e.clientX;
            clientY = e.clientY;
            offsetLeft = this.offsetLeft;
            offsetTop = this.offsetTop;
            dragStart.apply(this, arguments);
            $(document).bind('mousemove', moveHandle)
                        .bind('mouseup', upHandle);
            // 鼠标移动事件处理
            function moveHandle(e) {
                var x = e.clientX - clientX + offsetLeft;
                var y = e.clientY - clientY + offsetTop;
                $target.css({
                    left: Math.max( Math.min(x, limitObj._right),  limitObj._left) + 'px',
                    top: Math.max( Math.min(y, limitObj._bottom),  limitObj._top) + 'px'
                });
                dragMove.apply(self, arguments);
                // 阻止浏览器默认行为(鼠标在拖动图片一小段距离,会出现一个禁止的小提示,即:图片不能再拖动)
                e.preventDefault();
            }
            // 鼠标弹起事件处理
            function upHandle(e) {
                $(document).unbind('mousemove', moveHandle);
                dragEnd.apply(self, arguments);
            }
        }
        _initOptions(); // 初始化配置对象
        $(this)
        .css({ position: 'absolute' })
        .each(function(){
            $(this).bind('mousedown', function(e){
                _drag.apply(this, [e]);
                // 阻止区域文字被选中 for chrome firefox ie9
                e.preventDefault();
                // for firefox ie9 || less than ie9
                window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
            });
        });
        return this;
    }
});

实例调用:

// 调用实例
(function(){
    $('.drag-elem').drag({
        boundaryElem: '#boundary',
        dragStart: function(){
            $(this).html('<span>准备拖动</span>').css({ zIndex: 2 }).siblings().css({ zIndex: 1 });
        },
        dragMove: function(){
            var pos = $(this).position();
            $(this).html('<span>拖动中(' +  pos.left + ',' + pos.top + ')</span>' );
        },
        dragEnd : function(){
            $(this).html('<span>拖动结束</span>');            
        }
    });
}());
Javascript 相关文章推荐
自适应图片大小的弹出窗口
Jul 27 Javascript
一个刚完成的layout(拖动流畅,不受iframe影响)
Aug 17 Javascript
JavaScript编程中容易出BUG的几点小知识
Jan 31 Javascript
详解javascript传统方法实现异步校验
Jan 22 Javascript
在localStorage中存储对象数组并读取的方法
Sep 24 Javascript
细数JavaScript 一个等号,两个等号,三个等号的区别
Oct 09 Javascript
js浏览器滚动条卷去的高度scrolltop(实例讲解)
Jul 07 Javascript
推荐VSCode 上特别好用的 Vue 插件之vetur
Sep 14 Javascript
关闭Vue计算属性自带的缓存功能方法
Mar 02 Javascript
jQuery实现手风琴效果(蒙版)
Jan 11 jQuery
微信小程序实现录制、试听、上传音频功能(带波形图)
Feb 27 Javascript
微信小程序用户盒子、宫格列表的实现
Jul 01 Javascript
对table和ul实现js分页示例分享
Feb 24 #Javascript
jquery.post用法之type设置问题
Feb 24 #Javascript
jQuery获得IE版本不准确webbrowser的解决方法
Feb 23 #Javascript
js获得页面的高度和宽度的方法
Feb 23 #Javascript
使用js判断当前时区TimeZone是否是夏令时
Feb 23 #Javascript
js获得当前时区夏令时发生和终止的时间代码
Feb 23 #Javascript
js 金额格式化来回转换示例
Feb 23 #Javascript
You might like
基于PHP代码实现中奖概率算法可用于刮刮卡、大转盘等抽奖算法
2015/12/20 PHP
利用php实现一周之内自动登录存储机制(cookie、session、localStorage)
2016/10/31 PHP
php有效防止图片盗用、盗链的两种方法
2016/11/01 PHP
PHP实现根据密码长度显示安全条
2017/07/04 PHP
Javascript之文件操作
2007/03/07 Javascript
老鱼 浅谈javascript面向对象编程
2010/03/04 Javascript
Jquery数独游戏解析(一)-页面布局
2010/11/05 Javascript
Js放到HTML文件中的哪个位置有什么区别
2013/08/21 Javascript
jquery预加载图片的方法
2015/05/27 Javascript
jQuery simplePage+AJAX plus分页插件用法实例
2016/02/17 Javascript
select隐藏选中值对应的id,显示其它id的简单实现方法
2016/08/25 Javascript
Vue制作Todo List网页
2017/04/26 Javascript
详解Angular2响应式表单
2017/06/14 Javascript
在Vue.js中使用Mixins的方法
2017/09/12 Javascript
Vue.js自定义事件的表单输入组件方法
2018/03/08 Javascript
Vue.js 实现微信公众号菜单编辑器功能(二)
2018/05/08 Javascript
JS实现贪吃蛇游戏
2019/11/15 Javascript
JavaScript实现简单验证码
2020/08/24 Javascript
[44:04]OG vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
Python更新数据库脚本两种方法及对比介绍
2017/07/27 Python
Python 循环语句之 while,for语句详解
2018/04/23 Python
深入理解Python中的 __new__ 和 __init__及区别介绍
2018/09/17 Python
在python中,使用scatter绘制散点图的实例
2019/07/03 Python
python flask几分钟实现web服务的例子
2019/07/26 Python
python多进程并行代码实例
2019/09/30 Python
在python中使用nohup命令说明
2020/04/16 Python
Python常用库Numpy进行矩阵运算详解
2020/07/21 Python
Python3如何使用多线程升程序运行速度
2020/08/11 Python
python中numpy数组与list相互转换实例方法
2021/01/29 Python
Django中template for如何使用方法
2021/01/31 Python
Laravel中Kafka的使用详解
2021/03/24 PHP
大学生简历求职信
2014/06/24 职场文书
2014年销售经理工作总结
2014/12/01 职场文书
python flask开发的简单基金查询工具
2021/06/02 Python
解决mysql模糊查询索引失效问题的几种方法
2021/06/18 MySQL
Vue OpenLayer测距功能的实现
2022/04/20 Vue.js