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 相关文章推荐
prototype 1.5相关知识及他人笔记
Dec 16 Javascript
JavaScript String.replace函数参数实例说明
Jun 06 Javascript
基于jquery插件制作左右按钮与标题文字图片切换效果
Nov 07 Javascript
js的匿名函数使用介绍
Dec 11 Javascript
node.js中的buffer.Buffer.byteLength方法使用说明
Dec 10 Javascript
jQuery实现Flash效果上下翻动的中英文导航菜单代码
Sep 22 Javascript
jQuery实现的购物车物品数量加减功能代码
Nov 16 Javascript
xmlplus组件设计系列之网格(DataGrid)(10)
May 05 Javascript
深入浅出理解JavaScript闭包的功能与用法
Aug 01 Javascript
vue component 中引入less文件报错 Module build failed
Apr 17 Javascript
extjs图形绘制之饼图实现方法分析
Mar 06 Javascript
Vue router配置与使用分析讲解
Dec 24 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
mysql 中InnoDB和MyISAM的区别分析小结
2008/04/15 PHP
windows下升级PHP到5.3.3的过程及注意事项
2010/10/12 PHP
php函数mkdir实现递归创建层级目录
2016/10/27 PHP
ThinkPHP Where 条件中常用表达式示例(详解)
2017/03/31 PHP
Ucren Virtual Desktop V2.0
2006/11/07 Javascript
学习YUI.Ext 第六天--关于树TreePanel(Part 2异步获取节点)
2007/03/10 Javascript
读jQuery之五(取DOM元素)
2011/06/20 Javascript
JavaScript中的连字符详解
2013/11/28 Javascript
jQuery选择器全面总结
2014/01/06 Javascript
js实现使用鼠标拖拽切换图片的方法
2015/05/04 Javascript
3kb jQuery代码搞定各种树形选择的实现方法
2016/06/10 Javascript
使用伪命名空间封装保护独自创建的对象方法
2016/08/04 Javascript
轻松实现js选项卡切换效果
2016/09/24 Javascript
详解Angular4 路由设置相关
2017/08/26 Javascript
通过nodejs 服务器读取HTML文件渲染到页面的方法
2018/05/17 NodeJs
默认浏览器设置及vue自动打开页面的方法
2018/09/21 Javascript
angularJs使用ng-repeat遍历后选中某一个的方法
2018/09/30 Javascript
js form表单input框限制20个字符,10个汉字代码实例
2019/04/12 Javascript
VueJS实现用户管理系统
2020/05/29 Javascript
[02:02]2018DOTA2亚洲邀请赛Mineski赛前采访
2018/04/04 DOTA
python assert的用处示例详解
2019/04/01 Python
Python对象的属性访问过程详解
2020/03/05 Python
浅析Python requests 模块
2020/10/09 Python
Python OpenCV中的numpy与图像类型转换操作
2020/12/11 Python
python爬取代理ip的示例
2020/12/18 Python
丝芙兰美国官网:SEPHORA美国
2016/08/03 全球购物
Contém1g官网:巴西彩妆品牌
2020/01/17 全球购物
电气工程及其自动化自我评价四篇
2013/09/24 职场文书
运动会解说词50字
2014/01/18 职场文书
安全生产管理责任书
2014/04/16 职场文书
硕士研究生就业推荐信
2014/05/18 职场文书
三年级学生评语大全
2014/12/26 职场文书
班级元旦晚会开幕词
2015/01/29 职场文书
信用卡工作证明范本
2015/06/19 职场文书
Python干货实战之八音符酱小游戏全过程详解
2021/10/24 Python
《月歌。》宣布制作10周年纪念剧场版《RABBITS KINGDOM THE MOVIE》
2022/04/02 日漫