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 相关文章推荐
JS日历 推荐
Dec 03 Javascript
用jQuery模拟页面加载进度条的实现代码
Dec 19 Javascript
javascript学习笔记(二)数组和对象部分
Sep 30 Javascript
简单解析JavaScript中的__proto__属性
May 10 Javascript
用JavaScript获取页面文档内容的实现代码
Jun 10 Javascript
AngularJS 与百度地图的结合实例
Oct 20 Javascript
JS脚本加载后执行相应回调函数的操作方法
Feb 28 Javascript
axios发送post请求springMVC接收不到参数的解决方法
Mar 05 Javascript
详解Angular6学习笔记之主从组件
Sep 05 Javascript
Vue.js + Nuxt.js 项目中使用 Vee-validate 表单校验
Apr 22 Javascript
jquery轻量级数字动画插件countUp.js使用详解
Oct 17 jQuery
JS实现长图上下滚动效果
Mar 19 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正则过滤html标签、空格、换行符的代码(附说明)
2010/10/25 PHP
php读取和保存base64编码的图片内容
2017/04/22 PHP
laravel手动创建数组分页的实现代码
2018/06/07 PHP
PHP实现的解汉诺塔问题算法示例
2018/08/06 PHP
ThinkPHP3.2框架操作Redis的方法分析
2019/05/05 PHP
打开新窗口关闭当前页面不弹出关闭提示js代码
2013/03/18 Javascript
jQuery在iframe中无法弹出对话框的解决方法
2014/01/12 Javascript
Jquery动态添加及删除页面节点元素示例代码
2014/06/16 Javascript
Windows 系统下安装和部署Egret的开发环境
2014/07/31 Javascript
JavaScript中实现sprintf、printf函数
2015/01/27 Javascript
javascript使用递归算法求两个数字组合功能示例
2017/01/03 Javascript
浅谈JavaScript异步编程
2017/01/20 Javascript
angularjs实现的前端分页控件示例
2017/02/10 Javascript
bootstrap 通过加减按钮实现输入框组功能
2017/11/15 Javascript
NodeJS简单实现WebSocket功能示例
2018/02/10 NodeJs
vue如何引入sass全局变量
2018/06/28 Javascript
vue-cli脚手架build目录下utils.js工具配置文件详解
2018/09/14 Javascript
在vue项目中使用Jquery-contextmenu插件的步骤讲解
2019/01/27 jQuery
vue+iview/elementUi实现城市多选
2019/03/28 Javascript
原生js实现的移动端可拖动进度条插件功能详解
2019/08/15 Javascript
从零搭一个自用的前端脚手架的方法步骤
2019/09/23 Javascript
基于Vue el-autocomplete 实现类似百度搜索框功能
2019/10/25 Javascript
es6函数name属性功能与用法实例分析
2020/04/18 Javascript
让python json encode datetime类型
2010/12/28 Python
python3判断url链接是否为404的方法
2018/08/10 Python
python根据url地址下载小文件的实例
2018/12/18 Python
Python进程间通信Queue消息队列用法分析
2019/05/22 Python
解决python-docx打包之后找不到default.docx的问题
2020/02/13 Python
Python爬取新型冠状病毒“谣言”新闻进行数据分析
2020/02/16 Python
Python Tornado核心及相关原理详解
2020/06/24 Python
英国护发和美妆在线商店:Klip Shop
2019/03/24 全球购物
吉列剃须刀英国官网:Gillette英国
2019/03/28 全球购物
英国No.1体育用品零售商:SportsDirect.com
2019/10/16 全球购物
骨干教师考核评语
2014/12/31 职场文书
CSS中Single Div 绘图技巧的实现
2021/06/18 HTML / CSS
【海涛dota解说】DCG联赛第一周 LGD VS DH
2022/04/01 DOTA