4.2. Drag and Drop

4.2. Drag and Drop

在桌面应用中经常强调直接操纵屏幕上的对象的能力,但是对web界面来说更多的是强调操纵复杂DOM的能力。Script.aculo.us将这二者的特点结合起来提供了简单而又强大的界面拖拽支持。这意味着web开发者可以决定使用基本的拖拽功能,而不关心技术层面的东西。使用视觉效果时,要记得拖拽功能有时候并不是解决界面问题的最佳途径。但是如果需要,script.aculo.us可以做到!
4.2.1 . Draggables
Script.aculo.us 提供了一个 Draggable 的类,用来添加 DOM 元素实现拖拽的能力。在开始之前,新建一个模板文件, draggables.rhtml, 添加下面的代码:
<div id= "dragDIV" class= "green box">drag</div>

<%= javascript_tag "new Draggable('dragDIV')" %>
当页面被加载后 ([url]http://localhost:3000/chapter4/draggables[/url]),Javascript 声明了一个 Draggable 类的实例变量,跟元素的 ID 绑定在一起。现在你可以在页面内拖拽这个元素。注意它是怎么实现在移动中的轻度透明,这里使用了前面讲过的 Opacity 效果。
 
Rails 提供了 draggable_element helper 来实现拖拽,就像 Draggable.initialize, 第一个参数是元素的 ID 第二个参数是 hash 选项, 例如:
<div id= "helperDIV" class= "green box">helper</div>

<%= draggable_element :helperDIV %> 
draggable_element 输出的是一个<script>元素,在<script>标签里面有new Draggable的声明,如果你只想要这个Javascript的声明而不需要<script>标签,那么你可以使用draggable_element_js来替换draggable_element.例如:
 
<div id= "clickDIV" class= "green box">
    <%= button_to_function "Make draggable",
         draggable_element_js(:clickDIV) %>
</div>
为了让给用户一个更好的体验,当鼠标经过可拖拽元素的位置是,改变鼠标指针形状。可以用CSS的鼠标指针属性来轻松实现:
<div class= "green box" style= "cursor:move">drag</div>
当用户鼠标放到这个元素上面时,指针就变成了“可移动”的图标,指示这个元素可拖拽。当然, CSS 也可以不写在 div 里面,可以放在外部的 stylesheet 里。
 
4.2.1 .1. Draggable options
Effect.* 方法中, Draggable.initialize 用一个 JavaScript 表达式作为 hash 选项来定制它们的行为, draggable_element helper ruby 表达式作为 hash 选项,最后将其翻译成 Javascript 表达式。
revert 参数如果设置为true,那么元素在拖拽之后会回到原始位置。revert的设置也可以是一个function,指明拖拽结束后调用的功能。决定拖拽是否回到某一位置。例如:
<div id= "revertDIV" class= "green box">revert</div>

<%= draggable_element :revertDIV, :revert => true %>
---------------------------------------------------------------------
<div id= "functionRevertDIV" class= "green box"> function revert</div>

<%= draggable_element :functionRevertDIV,  

     :revert => " function(el){

         return Position.page(el)[0] > 100; }" %>
在第二段代码中,:revert后面的function用来指定元素拖拽结束后的位置。
这里,只有当拖动元素超过100像素(从窗口左侧开始算起)时,回被恢复到原来的位置,也就是说,它被拖拽后静止的范围只有长度为100像素高度不限,在窗口内的柱形区域。
 
Ghosting 参数,如果设置为true,会在拖拽时在原位置生成一个镜像,直到拖拽结束,镜像也随之消失:
<div id= "ghostingDIV" class= "green box">ghosting</div>

<%= draggable_element :ghostingDIV, :ghosting => true %>
 
handle 允许你为要拖拽的元素内部加一个子元素,这个子元素作为拖拽的把手。它的值是一个JavaScript表达式,这个Javascript表达式以元素的 ID或者元素的引用作为参数。例如:
<div id= "handleDIV" class= "green box">

  <span id= "myHandle">handle</span>

</div>

<%= draggable_element :handleDIV, :handle => "'myHandle'" %>
看到了么,这里myHandle被放在双重引号里,这是因为JavaScript表达式要将其作为字符串来执行。
 
change 选项后面跟一个function,这个function会在整个拖拽的过程中时刻被调用。Fuction的调用就像是以拖拽作为参数的:
<div id= "changeDIV" class= "green box">change</div>

<%= draggable_element :changeDIV, :change => " function(draggable) {

  draggable.element.innerHTML=draggable.currentDelta(  );

}" %>

这段代码实现的功能:当你拖动这个green box时,里面会根据拖拽的位置时时显示其坐标。
 
constraint, 如果设置为horizontal(水平)或者vertical(垂直),会约束元素拖动的角度。这个元素ID是作为JavaScript表达式中的参数的,所以要在DOM元素ID加上两对引号:
<div id= "constraintDIV" class= "green box">constraint</div>

<%= draggable_element :constraintDIV, :constraint => "'vertical'" %>
snap 是让元素像是在格子化的画面上拖拽,如果snap设置为false(默认设置),那么不会有这种效果,如果将值设为一个整数n, 元素会在拖拽时跳到最近的与其间隔为n像素的一个格子里。这个值也可以被设定为一个数组形式[x,y],这样横轴和纵轴方向都会被不同的值限制住。Snap的值也可以设置为一个functionfunction会传递元素当前位置的坐标(是以其开始位置的偏移量,而不是其绝对坐标),返回snap坐标,例如:

    
   
   
   
<div id= "snapDIV_50" class= "green box">snap to 50</div>
<%= draggable_element :snapDIV_50, :snap => 50 %>
-----------
    

    
   
   
   
<div id= "snapDIV_50_100" class= "green box">snap to 50,100</div>
<%= draggable_element :snapDIV_50_100, :snap => '[50,100]' %>
---------------

    
   
   
   
<div id= "snapDIV_function" class= "green box">snap to function</div>
<%= draggable_element :snapDIV_function, :snap => " function(x, y) {
  new_x = (x > 100) ? 100 : ((x < 0) ? 0 : x);
  new_y = (y > 100) ? 100 : ((y < 0) ? 0 : y);
   return [ new_x, new_y ];
}" %>
最后一个示例证实了为snap选项定义一个function的功效,同时使用xy两个维度的值从0-100,结果实现了拖拽只能在一个小方框内进行。
4.2.2 . Droppables
Droppable 可以让 DOM 元素接受一个拖拽过来的目标元素并执行某些动作,例如 Ajax 调用,用 javascript 建立一个 droppable ,使用 Dropables.add:

    
   
   
   
<div id= "dropDIV" class= "pink box">drop</div>
<%= javascript_tag "Droppables.add('dropDIV', {hoverclass:'hover'})" %>
第二个参数是一个hash选项,更多细节参见Droppable options这节。Rails helper使用drop_receiving_elementdrop_receiving_element_js来生成droppable.例如:
<div id= "dropHelperDIV" class= "pink box">drop here.</div>

<%= drop_receiving_element :dropHelperDIV, :hoverclass => 'hover' %>
drop_receiving_element_js helper drop_receiving_element不是一回事,它不被夹在<script>标签中,而是直接输出javascript代码。
 
droppable 没必要接受每种拖拽元素,下面的提供的一些droppable选项可以用来决定什么时候接受draggable.
 
4.2.2 .1. Droppable options
hoverclass 是一个被加在 droppable 属性中的类的类名,当可接受的拖拽元素在 droppable 上停留时, 告诉用户 droppable 是活跃的。我们在上一节中已经看了几个例子了。
 
accept 后面是一个 CSS 的类别,可以是字符串或者是一个字符串数组。如果提供了这个选项, droppable 只会接受 CSS 类别属于 accept 后面集合中的拖拽元素。例如:
<div id= "dragGreen" class= "green box">drag</div>

<%= draggable_element :dragGreen, :revert => true %>

  

<div id= "dragPink" class= "pink box">drag</div>

<%= draggable_element :dragPink, :revert => true %>

  

<div id= "dropAccept" class= "pink box">drop here (green only).</div>

<%= drop_receiving_element :dropAccept, :hoverclass => "hover",

     :accept => 'green' %>

containment 限制droppable只接受ID属于containment指定的元素ID数组的拖拽元素。这个ID会作为JavaScript表达式中的参数来执行,所以DOM元素的ID要用两对引号。例如:
<div id= "one">

    <div id= "dragGreen2" class= "green box">drag</div>

    <%= draggable_element :dragGreen2, :revert => true %>

</div>

  

<div id= "two">

    <div id= "dragPink2" class= "pink box">drag</div>

    <%= draggable_element :dragPink2, :revert => true %>

</div>

  

<div id= "dropContainment" class= "pink box">drop here.</div>

<%= drop_receiving_element :dropContainment, :hoverclass => "hover",

:containment => "'one'" %>

 
onHover 选项是当拖拽元素被拖到droppable上时引发的一段function。这个调用有3个参数: draggable, droppableoverlap(二者的重叠的百分比度)。看一个没有参数情况下的实例:
<div id= "dropOnHover" class= "pink box">drop</div>

<%= drop_receiving_element :dropOnHover, :hoverclass => "hover",

      :onHover => "function(  ){ $('dropOnHover').update('hover!'); }" %>
 
下面这个是3个参数都使用的实例:
<div id= "dropOnHover" class= "pink box">drop</div>

<%= drop_receiving_element :dropOnHover, :hoverclass => "hover",  

      :onHover => " function(draggable, droppable, overlap){  

         $('dropOnHover').update('you dragged ' + draggable.id +  

              ' over ' + droppable.id + ' by ' + overlap +  

              ' percent'); }" %>
onDrop 是当你将拖拽元素放到droppable上面放手时,droppable接受拖拽元素并发生动作。这个调用有两个参数:拖动元素和droppable元素,例如:
<div id= "dropOnDrop" class= "pink box">drop</div>

<%= drop_receiving_element :dropOnDrop, :hoverclass => "hover",

    :onDrop => " function(drag, drop){  

       alert('you dropped ' + drag.id + ' on ' + drop.id) }" %>
4.2.2 .2. Droppables with Ajax
不管你是通过 JavaScript(Droppables.add) 还是通过 Rails helper(drop_receiving_element drop_receiving_element_js) 创建的 droppable 的大部分选项我们在前面一节都了解了。然而当我们用 helper 创建 droppable 时,还有很多可用的附加选项。也就是说,所有的 link_to_remote 选项,例如 update url (第三章讲述过的)也是可用的,这些选项被用来创建一个 onDrop 调用的 function 来执行 droppable Ajax 调用:
<div id= "drag" class= "green box">drag</div>

<%= draggable_element :drag, :revert => true %>

  

<div id= "drop" class= "pink box">drop</div>

<%= drop_receiving_element :drop, :hoverclass => "hover",

     :update => "status", :url => { :action => "receive_drop" } %>

  

<div id= "status"></div>

这里:url选项,里面有个指向receive_dropaction,我们需要在chapter4_controller.rb中定义:
def receive_drop

  render :text => "you dropped element id #{params[:id]}"

end
 
除非用:with选项来指明传递的draggable,不然drop_receiving_elementAjax调用会自动接收draggableid作为请求的id参数。
 
4.2.3 . Sortables
sortable 是建立在draggabledroppable基础之上的,当你将一个元素往下拖的时候,你可以同时给出其他元素的拖拽行为,让他们在画面上重新排列。
 
使用JavascriptSortable.create创建一个sortable
<ul id= "list">

  <li>Buy milk</li>

  <li>Take out trash</li>

  <li>Make first million</li>

</ul>

  

<%= javascript_tag "Sortable.create('list')" %>
当然,Rails也提供了helper来完成这个任务:sortable_elementsortable_element_js.就像产生其他拖拽效果元素相关的helper一样,第一个参数是目标DOM元素,第二个是optionshash选项,用来控制行为。其他可用的选项有:
hoverclass
     传递给 droppable, 指定的这些 CSS 类型加到 droppable 中,以控制只有当 draggable CSS 类型在其中时才被 droppable 所接受。
 
handle
     传递给 droppable, 当元素之间会发生交互作用时(例如链接或者是表单元素)这个选项是非常有用的,例如:
<ul id= "listHandle">

  <li><span class= "handle">x</span> Buy milk</li>

  <li><span class= "handle">x</span> Take out trash</li>

  <li><span class= "handle">x</span> Make first million</li>

</ul>

  

<%= sortable_element :listHandle, :handle => 'handle' %>
拖放那个x时,发生重新排列。
ghosting
也是传递给draggables.例如:
<ul id= "listGhosting">

  <li>Buy milk</li>

  <li>Take out trash</li>

  <li>Make first million</li>

</ul>

<%= sortable_element :listGhosting, :ghosting => true %>

 
在原位置产生镜像,拖拽结束后消失
constraint and overlap
他们一起工作来决定sortable运作的方向:垂直(默认)vertical或水平horizontalconstraint是传递给draggable的,它决定元素的拖拽方向。overlap传递给droppable,droppable只有当draggable元素覆盖在其上50%时产生作用。例如:

       
       
       
       
<ul id="listHorizontal">
  <li style="display: inline; margin-right: 10px;">Buy milk</li>
  <li style="display: inline; margin-right: 10px;">Take out trash</li>
  <li style="display: inline; margin-right: 10px;">Make first million</li>
</ul>
  
<%= sortable_element :listHorizontal,
     :constraint => 'horizontal',
     :overlap    => 'horizontal' %>
 
tag
设定 sortable 元素的标签类型,默认情况下是 <li> 标签, li ul( 无序的项目 ) 或者 ol (有序的项目)中的项目。如果你要设定 sortable 元素是其他的例如图像或者是 div 标签时,要用 tag 选项标识出来:
<div id= "listTag">

  <div>Buy milk</div>

  <div>Take out trash</div>

  <span>Make first million</span>

</div>

  

<%= sortable_element :listTag, :tag => 'div' %>
这里有个<span>标签,它不属于sortable的元素。不参与拖拽
only
限制sortable元素的CSS 类型为给定的CSS类型或类型数组。例如:
<ul id= "listOnly">

  <li class= "sortable">Buy milk</li>

  <li class= "sortable">Take out trash</li>

  <li>Make first million</li>

</ul>

  

<%= sortable_element :listOnly, :only => 'sortable' %>

 
containment
用来使元素的拖拽在多个容器中进行。一个容器只会接受那些在 containment 中指定的父类元素,这个指定元素的方法可以使用元素的 ID 或者是 ID 数组。
<ul id= "list1">

  <li>Buy milk</li>

  <li>Take out trash</li>

</ul>

  

<ul id= "list2">

  <li>Make first million</li>

</ul>

  

<%= sortable_element :list1, :containment => ['list1', 'list2'] %>

<%= sortable_element :list2, :containment => ['list1', 'list2'] %>

 
dropOnEmpty
当你有2sortable容器,你希望元素可以在这两个容器之间拖动时可以使用dropOnEmpty,默认情况下,是不允许将另一个容器中的元素拖拽到一个空的容器中的。但是当dropOnEmpty设定为true时,情况就改变了。例如:

          
         
         
         
<ul id= "listFull">
    <li id= "thing_1">Buy milk</li>
    <li id= "thing_2">Take out trash</li>
    <li id= "thing_3">Make first million</li>
</ul>
  
<ul id= "listEmpty">
</ul>
  
<%= sortable_element :listFull,
     :containment => ['listFull', 'listEmpty'],
     :dropOnEmpty => true %>
<%= sortable_element :listEmpty,
     :containment => ['listFull', 'listEmpty'],
     :dropOnEmpty => true %>
scroll
可以让 sortables 被放在一个有滚动游标的区域里,拖拽元素会自动调整游标。完了完成这个有滚动游标容器的功能,必须设定容器的 style=”overflow: scroll” , 而且 scroll 选项应该设置为容器的 id 。这个 id 会作为 Javascript 表达式运行的参数,所以必须加两对引号。还必须通过 Position.includeScrollOffsets 参数设定为 true script.aculo.us 中的 scrolling 功能激活,例如:
<div id= "container" style= "overflow: scroll; height: 200px;">

  <ul id= "listScroll">

    <% 20.times do |i| %>

      <li>Buy milk</li>

      <li>Take out trash</li>

      <li>Make first million</li>

    <% end %>

  </ul>

</div>

  

<%= javascript_tag "Position.includeScrollOffsets = true" %>

<%= sortable_element :listScroll, :scroll => "'container'" %>

 
 
 
onChange
当拖拽元素使得其排列顺序发生改变时就调用该参数设定的内容。这个调用以拖拽的元素作为其参数:
<ul id= "listChange">

  <li>Buy milk</li>

  <li>Take out trash</li>

  <li>Make first million</li>

</ul>

  

<%= sortable_element :listChange,

     :onChange => "function(el) { alert(el.innerHTML); }" %>

 
onUpdate
当拖拽结束 sortable 顺序发生改变时被调用,这个调用以容器作为其参数:

            
           
           
           
<ul id= "listUpdate">
  <li>Buy milk</li>
  <li>Take out trash</li>
  <li>Make first million</li>
</ul>
  
<%= sortable_element :listUpdate,
     :onUpdate => "function(el) { alert(el.innerHTML); }" %>
<%#似乎不工作%>
4.2.3 .1. Ajax-enabled sortables
对于 droppable 来说, sortable_element helper 也可以使用常见的 Ajax 选项,例如 link_to_remote 中提供的 Ajax 选项一样。默认情况下,当建立 Ajax 调用时,对于 action 的调用会以连续的 sortable 元素作为参数。在使用时, sortable 元素的 ID 应该按这样的风格来命名: Sortable.serialize: 唯一的 ID 标识应该放到后面,前面可以加上下划线。例如 item_1,person_2 _3 都可以作为好的 ID 的来用,但是 item1 2_person 3 就不好了:
<ul id= "listAjax">

  <li id= "item_1">Buy milk</li>

  <li id= "item_2">Take out trash</li>

  <li id= "item_3">Make first million</li>

</ul>

  

<%= sortable_element :listAjax,

     :url      => { :action => 'repeat' },

     :complete => "alert(request.responseText);" %>
这段代码意思是,当重新排序列表时引发Ajax调用repeat这个action,使用listAjax数组作为参数包含了在当前顺序下(发送重新排列后)sortable元素的所有ID,为了让这段代码能够正确运行,在controller里定义repeat action,显示调用时发送的参数情况。
def repeat

  render :text => params.inspect

end

  

你可能感兴趣的:(on,drop,drag,and,(ajax)