javascript的拖放(第2部分)
实现手柄拖动的功能,就是把mousedown的事件侦听器放到handle中。由于我们原先程序的骨架搭建得比较好,添加新功能非常容易。1.# 2. (handle || el).onmousedown = dragstart;但这不够人性化,最好是我们输入一个类名作为handle的参数,拖动类会自动根据此类名在其子孙元素寻找此元素。
01.if(handle){ 02. var cls = new RegExp("(^|\\s)" + handle + "(\\s|$)"); 03. for(var i=0,l=el.childNodes.length;i <l;i++){ 04. var child = el.childNodes[i]; 05. if(child.nodeType == 1 && cls.test(child.className)){ 06. _handle = child; 07. break; 08. } 09. } 10.}相应地方修改为:
1.# 2. (_handle || el).onmousedown = dragstart;有时如果拖动元素是个非常复杂,包含相当多东西,这样拖起来很吃内存,这时是不是拖动一个空元素是不是好一些呢?我们可以克隆原来的拖动元素,并把它加入原来元素的后面(相同z-Index,后面的会放到前面的前面)。如果要求是用手柄拖动,我们把原来的手柄也克隆过来就是。
01.if(ghosting){ // ghosting为可选参数,表示使用克隆体拖动 02. _ghost = el.cloneNode(false); 03. el.parentNode.insertBefore(_ghost,el.nextSibling); 04. if(_handle){ 05. _handle = _handle.cloneNode(false); 06. _ghost.appendChild(_handle); 07. } 08. !+"\v1"? _ghost.style.filter = "alpha(opacity=50)" : _ghost.style.opacity = 0.5; 09.}我们还可以增添一些回调函数(onStart,onDrag,onEnd),如果用户没有实现回调函数,我们给个空函数(function(){})它就好了。
像拖动时显示元素的坐标,我们也可以做成可选参数coords。默认为true,并在手柄上显示,但这样会覆盖住原来的一些东西,我们在拖动前把它保存到一个变量中,拖动后再还原。
另,如果是一个范围中拖动,如一个指定的容器或浏览器的可视区,当拖动元素存在margin时,其右边与下边可能会超出容器。因此我们必须取出其相应的margin,当右边与下边的坐标大于容器右边或下边的坐标时,强逼元素往左边与上边移动相应的值,那个值不用说就是margin值。我们用一个简单的getStyle()函数来取margin值,因此设置元素的margin时请以px为单位,否则在IE中会计算错误。
01.var getStyle = function(el, style){ 02. if(!+"\v1"){ 03. style = style.replace(/\-(\w)/g, function(all, letter){ 04. return letter.toUpperCase(); 05. }); 06. var value = el.currentStyle[style]; 07. (value == "auto")&&(value = "0px" ); 08. return value; 09. }else{ 10. return document.defaultView.getComputedStyle(el, null).getPropertyValue(style) 11. } 12.}最后一个较为重要的功能,当元素往右边或下边移动,让元素永远留在可视区。这怎样做到呢?我们计算元素右下角的坐标(right与bottom),然后再计算出浏览器可视区的宽与高,当right大于宽时,就让它们的差值作为浏览器的scrollLeft,bottom的情形相仿。
01.if(scroll){ //表示让滚动条与元素一起移动 02. var doc = isQuirk ? document.body : document.documentElement; 03. doc = options.container || doc; 04. var a = getCoords(el).left + el.offsetWidth; 05. var b = doc.clientWidth; 06. if (a > b){ 07. doc.scrollLeft += a - b; 08. } 09. var c = getCoords(el).top + el.offsetHeight; 10. var d = doc.clientHeight; 11. if (c > d){ 12. doc.scrollTop += c - d; 13. } 14.}最后函数扩展成以下样子,功能比较多,下附文档说明:
001.//辅助函数1 002.var getCoords = function(el){ 003. var box = el.getBoundingClientRect(), 004. doc = el.ownerDocument, 005. body = doc.body, 006. html = doc.documentElement, 007. clientTop = html.clientTop || body.clientTop || 0, 008. clientLeft = html.clientLeft || body.clientLeft || 0, 009. top = box.top + (self.pageYOffset || html.scrollTop || body.scrollTop ) - clientTop, 010. left = box.left + (self.pageXOffset || html.scrollLeft || body.scrollLeft) - clientLeft 011. return { 'top': top, 'left': left }; 012.}; 013.//辅助函数2 014.var getStyle = function(el, style){ 015. if(!+"\v1"){ 016. style = style.replace(/\-(\w)/g, function(all, letter){ 017. return letter.toUpperCase(); 018. }); 019. var value = el.currentStyle[style]; 020. (value == "auto")&&(value = "0px" ); 021. return value; 022. }else{ 023. return document.defaultView.getComputedStyle(el, null).getPropertyValue(style) 024. } 025.} 026.//============================ 027.var Drag = function(id){ 028. var el = document.getElementById(id), 029. isQuirk = document.documentMode ? document.documentMode == 5 : document.compatMode && document.compatMode != "CSS1Compat", 030. options = arguments[1] || {}, 031. container = options.container || document.documentElement, 032. limit = options.limit, 033. lockX = options.lockX, 034. lockY = options.lockY, 035. ghosting = options.ghosting, 036. handle = options.handle, 037. revert = options.revert, 038. scroll = options.scroll, 039. coords = true || options.coords, 040. onStart = options.onStart || function(){}, 041. onDrag = options.onDrag || function(){}, 042. onEnd = options.onEnd || function(){} , 043. marginLeft = parseFloat(getStyle(el,"margin-left")), 044. marginRight = parseFloat(getStyle(el,"margin-right")), 045. marginTop = parseFloat(getStyle(el,"margin-top")), 046. marginBottom = parseFloat(getStyle(el,"margin-bottom")), 047. cls, 048. _handle, 049. _ghost, 050. _top, 051. _left, 052. _html; 053. el.lockX = getCoords(el).left; 054. el.lockY = getCoords(el).top; 055. el.style.position = "absolute"; 056. if(handle){ 057. cls = new RegExp("(^|\\s)" + handle + "(\\s|$)"); 058. for(var i=0,l=el.childNodes.length;i<l;i++){ 059. var child = el.childNodes[i]; 060. if(child.nodeType == 1 && cls.test(child.className)){ 061. _handle = child; 062. break; 063. } 064. } 065. } 066. _html = (_handle || el).innerHTML; 067. var dragstart = function(e){ 068. e = e || window.event; 069. el.offset_x = e.clientX - el.offsetLeft; 070. el.offset_y = e.clientY - el.offsetTop; 071. document.onmouseup = dragend; 072. document.onmousemove = drag; 073. if(ghosting){ 074. _ghost = el.cloneNode(false); 075. el.parentNode.insertBefore(_ghost,el.nextSibling); 076. if(_handle){ 077. _handle = _handle.cloneNode(false); 078. _ghost.appendChild(_handle); 079. } 080. !+"\v1"? _ghost.style.filter = "alpha(opacity=50)" : _ghost.style.opacity = 0.5; 081. } 082. (_ghost || el).style.zIndex = ++Drag.z; 083. onStart(); 084. return false; 085. } 086. var drag = function(e) { 087. e = e || window.event; 088. el.style.cursor = "pointer"; 089. !+"\v1"? document.selection.empty() : window.getSelection().removeAllRanges(); 090. _left = e.clientX - el.offset_x ; 091. _top = e.clientY - el.offset_y; 092. if(scroll){ 093. var doc = isQuirk ? document.body : document.documentElement; 094. doc = options.container || doc; 095. options.container && (options.container.style.overflow = "auto"); 096. var a = getCoords(el).left + el.offsetWidth; 097. var b = doc.clientWidth; 098. if (a > b){ 099. doc.scrollLeft = a - b; 100. } 101. var c = getCoords(el).top + el.offsetHeight; 102. var d = doc.clientHeight; 103. if (c > d){ 104. doc.scrollTop = c - d; 105. } 106. } 107. if(limit){ 108. var _right = _left + el.offsetWidth , 109. _bottom = _top + el.offsetHeight, 110. _cCoords = getCoords(container), 111. _cLeft = _cCoords.left, 112. _cTop = _cCoords.top, 113. _cRight = _cLeft + container.clientWidth, 114. _cBottom = _cTop + container.clientHeight; 115. _left = Math.max(_left, _cLeft); 116. _top = Math.max(_top, _cTop); 117. if(_right > _cRight){ 118. _left = _cRight - el.offsetWidth - marginLeft - marginRight; 119. } 120. if(_bottom > _cBottom){ 121. _top = _cBottom - el.offsetHeight - marginTop - marginBottom; 122. } 123. } 124. lockX && ( _left = el.lockX); 125. lockY && ( _top = el.lockY); 126. (_ghost || el).style.left = _left + "px"; 127. (_ghost || el).style.top = _top + "px"; 128. coords && _ghost && ((_handle || _ghost).innerHTML = _left + " x " + _top); 129. onDrag(); 130. } 131. var dragend = function(){ 132. document.onmouseup = null; 133. document.onmousemove = null; 134. _ghost && el.parentNode.removeChild(_ghost); 135. el.style.left = _left + "px"; 136. el.style.top = _top +"px"; 137. (_handle || el).innerHTML = _html; 138. if(revert){ 139. el.style.left = el.lockX + "px"; 140. el.style.top = el.lockY + "px"; 141. } 142. onEnd(); 143. } 144. Drag.z = 999; 145. (_handle || el).onmousedown = dragstart; 146.}| 参数 | 类型 | 说明 |
|---|---|---|
| id | string | 必选,拖动元素的ID |
| container | object | 可拖的范围,必须为拖动元素的父级元素,是否为定位元素无所谓。 |
| limit | boolean | 默认false,与container配合使用。当值为true时,它会以container或浏览器的可视区作拖动范围。 |
| lockX | boolean | 默认false。当值为true时,锁定X轴,只允许上下移动。 |
| lockY | boolean | 默认false。当值为true时,锁定Y轴,只允许左右移动。 |
| handle | string | 手柄的类名,当设置了此参数后,只允许用手柄拖动元素。 |
| ghosting | boolean | 默认false。当值为true时,会生成一个与拖动元素相仿的影子元素,拖动时只拖动影子元素,以减轻内存消耗。 |
| revert | boolean | 默认false。当值为true时,让拖动元素在拖动后回到原来的位置。 |
| coords | boolean | 默认true。拖动时在手柄或影子元素上显示元素的坐标。 |
| scroll | boolean | 默认false。当值为true时,允许滚动条随拖动元素移动。 |
| onStart | function | 在拖动前鼠标按下的那一瞬执行。 |
| onDrag | function | 在拖动时执行。 |
| onEnd | function | 在拖动后鼠标弹起的那一瞬执行。 |
浙公网安备 33010602011771号