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 相关文章推荐
JavaScript 关键字屏蔽实现函数
Aug 02 Javascript
解决3.01版的jquery.form.js中文乱码问题的解决方法
Mar 08 Javascript
js隐式全局变量造成的bug示例代码
Apr 22 Javascript
基于jQuery实现自动轮播旋转木马特效
Nov 02 Javascript
基于JavaScript实现类似于百度学术高级检索功能
Mar 02 Javascript
JavaScript 对象字面量讲解
Jun 06 Javascript
使用JavaScript根据图片获取条形码的方法
Jul 04 Javascript
原生JS实现小小的音乐播放器
Oct 16 Javascript
vue 解决数组赋值无法渲染在页面的问题
Oct 28 Javascript
vue中父子组件传值,解决钩子函数mounted只运行一次的操作
Jul 27 Javascript
Vue实现一种简单的无限循环滚动动画的示例
Jan 10 Vue.js
解决vue自定义组件@click点击失效问题
Apr 30 Vue.js
对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
这部好评如潮的动漫 知名梗频出 但是画风劝退很多人
2020/03/08 日漫
第四节 构造函数和析构函数 [4]
2006/10/09 PHP
php数组函数序列之rsort() - 对数组的元素值进行降序排序
2011/11/02 PHP
php语言流程控制中的主动与被动
2012/11/05 PHP
PHP实现的日历功能示例
2018/09/01 PHP
php中字符串和整数比较的操作方法
2019/06/06 PHP
jQuery - css() 方法示例详解
2014/01/16 Javascript
解决html按钮切换绑定不同函数后点击时执行多次函数问题
2014/05/14 Javascript
js获取url中&quot;?&quot;后面的字串方法
2014/05/15 Javascript
javascript中声明函数的方法及调用函数的返回值
2014/07/22 Javascript
javascript中的遍历for in 以及with的用法
2014/12/22 Javascript
jQuery插件ajaxFileUpload实现异步上传文件效果
2015/04/14 Javascript
微信小程序仿今日头条导航栏滚动解析
2019/08/20 Javascript
vue element-ui el-date-picker限制选择时间为当天之前的代码
2019/11/07 Javascript
Python实现计算文件夹下.h和.cpp文件的总行数
2015/04/23 Python
浅谈python新手中常见的疑惑及解答
2016/06/14 Python
Python数据结构之单链表详解
2017/09/12 Python
Python实现图片添加文字
2019/11/26 Python
Python tkinter三种布局实例详解
2020/01/06 Python
python实现滑雪游戏
2020/02/22 Python
Python headers请求头如何实现快速添加
2020/11/03 Python
python多线程和多进程关系详解
2020/12/14 Python
开发人员所需要知道的HTML5性能分析面面观
2012/07/05 HTML / CSS
html5自定义video标签的海报与播放按钮功能
2019/12/04 HTML / CSS
摩飞电器俄罗斯官方网站:Morphy Richards俄罗斯
2020/07/30 全球购物
如何配置、使用和清除Smarty缓存
2015/12/23 面试题
税务专业毕业生自荐信
2013/11/10 职场文书
个人党性剖析材料
2014/02/03 职场文书
学生喝酒检讨书
2014/02/06 职场文书
《最大的麦穗》教学反思
2014/04/17 职场文书
文案策划岗位职责
2015/02/11 职场文书
后勤工作个人总结
2015/02/28 职场文书
告知书格式
2015/07/01 职场文书
如何用JavaScript实现一个数组惰性求值库
2021/05/05 Javascript
修改MySQL的数据库引擎为INNODB的方法
2021/05/26 MySQL
漫画「狩龙人拉格纳」公开TV动画预告图
2022/03/22 日漫