zoukankan      html  css  js  c++  java
  • 拖动插件的一些常见问题

    最近项目需要,我要实现一个拖动的功能。大概的意思是:你邮箱里面的邮件列表,你可以通过鼠标mousedown后,通过鼠标移动mousemove,把特定的邮件拖动到垃圾箱啊或者草稿箱啊,如果拖动到的地方不是像垃圾箱或草稿箱的元素,就不做处理。

    这个功能在邮箱项目上是很普遍的,我稍微看了下我们公司的标准邮箱的实现方法,其中有一个疑问是:为什么不用节流的方式来控制mousemove的操作。大家都知道像mousemove和scroll这种事件,你拖动一下,就可能触发很多次,导致回调方法也执行很多次,性能不好,如果加上函数节流的这种机制,不是可以性能优化吗?这个问题,后面会有详细的解释。

    下图就是需要实现的功能截图:

    知道了项目的需求,我就开始写代码实现了。

    第一点:你必须绑定邮件列表的mousedown事件。由于邮件数目比较多,你不可能给每一封邮件都绑定mousedown事件,这样会影响性能。因此,连初学者都知道要绑定邮件列表的父元素,这样你点击某一封邮件的时候,事件会冒泡到父元素,父元素就会收到这个事件,然后触发事件回调函数,在事件回调函数里面,通过event.target,我们就可以知道用户点击了那一封邮件,进而再处理。这就是所谓的事件委托机制。如果想对事件机制有更深的了解,请看:http://www.cnblogs.com/chaojidan/p/4167675.html。

    因此,具体的实现细节如下:

    $("div.mailList").on("mousedown",function(event){    
      .......
    }

    给邮件列表的父元素div绑定mousedown事件,大家都知道jQuery绑定事件有很多种方式,但是我推荐使用on方法,理由有两点:1.有些绑定方法在内部其实就是调用on方法进行事件绑定的。2.on方法绑定事件,兼容性好,而且后面添加的元素也不会出问题(有些绑定方法,当元素是后面添加的时候,绑定就失效了)。如果需要更详细的了解,请自行百度。

    第二点:绑定好了mousedown事件后,就要绑定mousemove事件,那这个事件绑定在哪里呢?大家可以想想看,你拖动此邮件元素的时候,是不是在整个页面都应该触发mousemove事件呢,因此,我们需要这样绑定mousemove事件:

    $(document).on("mousemove", function(event){
        ........  
    }

    然后,我们就在这个回调函数里面,通过event.target就可以得到鼠标拖动的地方的元素节点。我们通过判断这个元素节点,就可以知道邮件是否可以拖动到这个地方。

    第三点:绑定完mousedown事件后,我们就要绑定mouseup事件了,此事件理所当然也是绑定在document上。

    $(document).on("mouseup",function(event){
    .......
    }

    在此回调函数中,我们就可以通过上面mousemove的回调方法中的判断,是否进行ajax请求。如果鼠标拖动的地方可以接收邮件,那么就进行ajax请求,如果不行,就不用进行ajax请求。

    然后,还需要在此回调函数中,取消事件的绑定:

    $(document).off("mousemove");
    $(document).off("mouseup");

    至此,整个的框架就已经出来了。

    $("div.mailList").on("mousedown",function(event){    
      .......
      $(document).on("mousemove", function(event){
          ........  
      }
      $(document).on("mouseup",function(event){
        ......
        $(document).off("mousemove");
        $(document).off("mouseup");
      }
    }

    弄完这些之后,基本的功能实现了。

    深入进去,你就会发现以下几个问题:

    第一个问题:当你点击邮件元素,进行移动的时候,会让其他的文本元素变成蓝色的,这是浏览器的默认风格。但是通过,event.preventDefault()方式,只能解决chrome浏览器,但是火狐,IE下,还是不行。因此,百度得到以下方法:

    $("body,html").css({     //解决鼠标拖动时,不会让其他元素变成蓝色
                "-moz-user-select": "none",
                "-khtml-user-select": "none",
                "user-select": "none"
    });

    当鼠标mousedown时,在回调函数中执行上面的代码。

    当鼠标mouseup时,在回调函数中执行下面的代码:

    $("body,html").css({
                    "-moz-user-select": "auto",
                    "-khtml-user-select": "auto",
                    "user-select": "auto"
    });

    问题解决。

    第二个问题:当我们拖动元素的时候,需要实时的显示一个div元素,这个div元素会提示我们当前鼠标的地点是否可以接受邮件。因此,我们需要创建一个div元素,由于此div是根据窗口定位的,因此我们只要设置它的position:fixed。

    $("<div style='border: 1px solid;position:fixed;display:none'>");

    然后,把此元素添加到页面上去。(这里我之前用$(document).append()方法添加此元素,但是一直都添加不上,看jQuery源码,原来只有nodeType=1元素节点或=11文档碎片节点的时候,才能添加元素。而document的nodeType=9。)

    $("body").append(divTip);

    然后,我们在mousemove的回调函数中,把这个divTip显示出来。

    $(divTip).css({
                        "left": x+20,
                        "top": y+20,
                        "z-index" : 99999,
                        "display": "block"
    });

    其中,x = event.clientX,y = event.clientY。

    最后,在mouseup的回调函数中,把这个divTip隐藏。

    $(divTip).css({
          "display": "none"
    });
    $(divTip).remove();

    这里,我就要讲一下,如果我们在mousemove的回调函数中使用函数节流的话,那么,就会出现divTip不能实时的跟着鼠标的拖动,移动到鼠标的位置。其实这不是问题,真正的问题是,当你移动到可以接受邮件的元素时,divTip会显示可以接受,这时,你移动鼠标,不小心移到divTip上时,divTip就会显示不可以接受(divTip本身是不能接受邮件的),但过一下,divTip移动后,鼠标就会落在了可以接受邮件的元素上,这时divTip又显示了可以接受。由于你不是实时的,所以divTip就会显示一下不可接受,然后再变成可接受,闪烁的情况会出现。因此,没有用到函数节流。

    最后一个问题:如果页面存在iframe的情况,你拖动元素,在iframe下拖动,或者释放鼠标按钮,那么你在document下绑定的mousemove和mouseup就会失效,导致问题出现。当然只有chrome浏览器下没有问题,其他浏览器下都失效了。那如何解决这个问题呢?

    我的想法是,在页面上的iframe中绑定mouseup和mousemove事件,然后在mouseup的回调函数中,解绑mouseup和mousemove就行了。

    for(var i= 0,len=window.frames.length;i<len;i++){   //其实这里有最简单的方法,就是直接取那个特定的iframe,不用循环去取
                iframes[i]= window.frames[i];
                $(iframes[i].document).on("mouseup",function(event){
                    ........
                });
     }

    这样绑定后,虽然解决了页面存在iframe时,document绑定mouseup和mousemove失败的问题,但是新的问题出现了,在iframe中你取到的

    var x = event.clientX;
    var y = event.clientY;

    是有问题的,因为iframe在你的页面中存在一定的位移,而此时的event.clientX是相对于iframe来算的,因此你需要加上iframe的位移

    var iframeLoc = $("#ueditor_0").offset();

    获取iframe元素,调用jQuery的offset方法,就可以搞定了。

    然后,你判断,如果用户把邮件拖到iframe中时,你就加上这个iframe的位移:

    $(divTip).css({
             "left": x + (iframeLoc ? iframeLoc.left : 0),
             "top": y + (iframeLoc ? iframeLoc.top : 0),
             "z-index" : 99999,
             "display": "block"
    });

    问题,就解决了。

    但是,如果这时,用户拖动了滚动条,这时就会产生滚动的距离,这样上面的计算方法在iframe中就会出错了(这时的event.clientX需要减去滚动距离的scrollLeft)。因此,当在iframe中拖动邮件元素时,我们还需要绑定scroll事件,如果滚动触发,我们就需要减去滚动的位移。

    $(document).on("scroll",function(){
            scrollLeft = $(window).scrollLeft();
            scrollTop = $(window).scrollTop();
            isScroll = true;
    });

    在mousemove时,判断是否在iframe中,如果在iframe中,并且isScroll为true,就必须减去滚动距离(这里,我们通过在iframe的位移中减去scrollLeft,跟在event.clientX减去scrollLeft是一样的效果)。

    if(!iframeLoc || isScroll){
             iframeLoc = $("#ueditor_0").offset();
             iframeLoc.left = iframeLoc.left - scrollLeft;
             iframeLoc.top = iframeLoc.top - scrollTop;
             isScroll = false;
    }

    最终,问题都得到了解决。

    当然,上面的拖动插件,我还没有加入ajax请求,也许加入后,会出现更多的问题。这里,我们不讨论ajax请求的情况。

    以上只是我简单的看法,大家如果有更好的意见,请评论,我们探讨下。

    加油!

  • 相关阅读:
    sql 拼写
    五、URL Routing介绍
    六、防止JavaScript注入攻击
    七、创建自定义的HTML Helper
    二、理解Models,Views,Controllers
    一、使用ASP.NET MVC创建应用程序
    自己写的临时表应用
    四、理解视图层,视图数据和HTML辅助
    三、理解控制器、控制器动作和ActionResults
    Extensions (扩展)方法
  • 原文地址:https://www.cnblogs.com/chaojidan/p/4287630.html
Copyright © 2011-2022 走看看