prototype学习资料(三)

js 代码
  1. /**   
  2. * Form.Element.getValue 会经常用到,所以做了一个快捷引用   
  3. * 取得某个表单控件的值,可以简化调用为 $F("username"),真是方便啊   
  4. */    
  5. var $F = Form.Element.getValue;    
  6. /**   
  7. * Abstract.TimedObserver 也没有用 Class.create() 来创建,和Ajax.Base 意图应该一样   
  8. * Abstract.TimedObserver 顾名思义,是套用Observer设计模式来跟踪指定表单元素,   
  9. * 当表单元素的值发生变化的时候,就执行回调函数   
  10.  
  11. * 我想 Observer 与注册onchange事件相似,不同点在于 onchange 事件是在元素失去焦点的时候才激发。   
  12. * 同样的与 onpropertychange 事件也相似,不过它只关注表单元素的值的变化,而且提供timeout的控制。   
  13.  
  14. * 除此之外,Observer 的好处大概就在与更面向对象,另外可以动态的更换回调函数,这就比注册事件要灵活一些。   
  15. * Observer 应该可以胜任动态数据校验,或者多个关联下拉选项列表的连动等等   
  16.  
  17. */    
  18. Abstract.TimedObserver = function() {}    
  19. /**   
  20. * 这个设计和 PeriodicalExecuter 一样,bind 方法是实现的核心   
  21. */    
  22. Abstract.TimedObserver.prototype = {    
  23. initialize: function(element, frequency, callback) {    
  24. this.frequency = frequency;    
  25. this.element = $(element);    
  26. this.callback = callback;    
  27.   
  28. this.lastValue = this.getValue();    
  29. this.registerCallback();    
  30. },    
  31. registerCallback: function() {    
  32. setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);    
  33. },    
  34. onTimerEvent: function() {    
  35. var value = this.getValue();    
  36. if (this.lastValue != value) {    
  37. this.callback(this.element, value);    
  38. this.lastValue = value;    
  39. }    
  40. }    
  41. }    
  42. /**   
  43. * Form.Element.Observer 监视指定表单域的值是否变化   
  44. */    
  45. Form.Element.Observer = Class.create();    
  46. Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({    
  47. getValue: function() {    
  48. return Form.Element.getValue(this.element);    
  49. }    
  50. });    
  51. /**   
  52. * Form.Element.Observer 监视指定表单所有控件的值是否有变化   
  53. */    
  54. Form.Observer = Class.create();    
  55. Form.Observer.prototype = (new Abstract.TimedObserver()).extend({    
  56. getValue: function() {    
  57. return Form.serialize(this.element);    
  58. }    
  59. });    
  60. /*--------------------------------------------------------------------------*/    
  61. /**   
  62. * EventObserver 相比上面的 TimedObserver,是更具主动性的一种监测   
  63. * 它直接为表单控件(根据 type 的不同) 注册相应的事件处理, 只要发现某个控件值发生改变,就执行回调函数   
  64. */    
  65. Abstract.EventObserver = function() {}    
  66. Abstract.EventObserver.prototype = {    
  67. initialize: function(element, callback) {    
  68. this.element = $(element);    
  69. this.callback = callback;    
  70.   
  71. this.lastValue = this.getValue();    
  72. if (this.element.tagName.toLowerCase() == 'form')    
  73. this.registerFormCallbacks();    
  74. else    
  75. this.registerCallback(this.element);    
  76. },    
  77. onElementEvent: function() {    
  78. var value = this.getValue();    
  79. if (this.lastValue != value) {    
  80. this.callback(this.element, value);    
  81. this.lastValue = value;    
  82. }    
  83. },    
  84. registerFormCallbacks: function() {    
  85. var elements = Form.getElements(this.element);    
  86. for (var i = 0; i < elements.length; i++)    
  87. this.registerCallback(elements);    
  88. },    
  89. registerCallback: function(element) {    
  90. if (element.type) {    
  91. switch (element.type.toLowerCase()) {    
  92. /**   
  93. * checkbox 和 radio 类型的控件注册 onclick 事件处理   
  94. */    
  95. case 'checkbox':    
  96. case 'radio':    
  97. element.target = this;    
  98. element.prev_onclick = element.onclick || Prototype.emptyFunction;    
  99. /**   
  100. * 相信这里有改进的空间,应该使用其后的 Event对象提供的注册管理功能来统一注册   
  101. */    
  102. element.onclick = function() {    
  103. this.prev_onclick();    
  104. this.target.onElementEvent();    
  105. }    
  106. break;    
  107. /**   
  108. * 其他类型的控件注册 onchange 事件处理   
  109. */    
  110. case 'password':    
  111. case 'text':    
  112. case 'textarea':    
  113. case 'select-one':    
  114. case 'select-multiple':    
  115. element.target = this;    
  116. element.prev_onchange = element.onchange || Prototype.emptyFunction;    
  117. element.onchange = function() {    
  118. this.prev_onchange();    
  119. this.target.onElementEvent();    
  120. }    
  121. break;    
  122. }    
  123. }    
  124. }    
  125. }    
  126. /**   
  127. * 监视指定表单控件   
  128. */    
  129. Form.Element.EventObserver = Class.create();    
  130. Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({    
  131. getValue: function() {    
  132. return Form.Element.getValue(this.element);    
  133. }    
  134. });    
  135. /**   
  136. * 监视指定表单所有控件   
  137. */    
  138. Form.EventObserver = Class.create();    
  139. Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({    
  140. getValue: function() {    
  141. return Form.serialize(this.element);    
  142. }    
  143. });    
  144. /**   
  145. * 封装事件处理的静态工具对象   
  146. */    
  147. if (!window.Event) {    
  148. var Event = new Object();    
  149. }    
  150. Object.extend(Event, {    
  151. KEY_BACKSPACE: 8,    
  152. KEY_TAB: 9,    
  153. KEY_RETURN: 13,    
  154. KEY_ESC: 27,    
  155. KEY_LEFT: 37,    
  156. KEY_UP: 38,    
  157. KEY_RIGHT: 39,    
  158. KEY_DOWN: 40,    
  159. KEY_DELETE: 46,    
  160. element: function(event) {    
  161. return event.target || event.srcElement;    
  162. },    
  163. isLeftClick: function(event) {    
  164. return (((event.which) && (event.which == 1)) ||    
  165. ((event.button) && (event.button == 1)));    
  166. },    
  167. /**   
  168. * click事件时鼠标以页面为基准的x坐标值, 考虑到了滚动条导致的位移差   
  169. */    
  170. pointerX: function(event) {    
  171. return event.pageX || (event.clientX +    
  172. (document.documentElement.scrollLeft || document.body.scrollLeft));    
  173. },    
  174. /**   
  175. * click事件时鼠标以页面为基准的y坐标值, 考虑到了滚动条导致的位移差   
  176. */    
  177. pointerY: function(event) {    
  178. return event.pageY || (event.clientY +    
  179. (document.documentElement.scrollTop || document.body.scrollTop));    
  180. },    
  181. /**   
  182. * 停止冒泡(参见 http://www.quirksmode.org/js/events_order.html) 和阻止浏览器执行与事件相关的默认动作   
  183. * 比如   
  184. * <a href="http://www.google.com" οnclick="Event.stop(event);">google</a>   
  185. * 那么点击该连接,页面并不会执行转向   
  186. */    
  187. stop: function(event) {    
  188. if (event.preventDefault) {    
  189. event.preventDefault();    
  190. event.stopPropagation();    
  191. else {    
  192. event.returnValue = false;    
  193. }    
  194. },    
  195. // find the first node with the given tagName, starting from the    
  196. // node the event was triggered on; traverses the DOM upwards    
  197. /**   
  198. * 找到事件元素的父级元素中,最接近事件元素且等同于指定标签名的父元素。   
  199. * 如果到达顶级元素(HTML),那么就返回顶级元素   
  200. */    
  201. findElement: function(event, tagName) {    
  202. var element = Event.element(event);    
  203. while (element.parentNode && (!element.tagName ||    
  204. (element.tagName.toUpperCase() != tagName.toUpperCase())))    
  205. element = element.parentNode;    
  206. return element;    
  207. },    
  208. /**   
  209. * 其后的代码封装了事件的注册和反注册,避免ie的内存泄露的bug   
  210. * 参见 http://javascript.weblogsinc.com/en...34000267034921/   
  211. */    
  212. observers: false,    
  213. /**   
  214. * this.observers 的数据格式是一个二维数组,二维的数组分别四个元素分别是   
  215. * [注册事件对象,事件名,事件处理函数,事件处理模式布尔值]   
  216. */    
  217. _observeAndCache: function(element, name, observer, useCapture) {    
  218. if (!this.observers) this.observers = ;    
  219. if (element.addEventListener) {    
  220. this.observers.push([element, name, observer, useCapture]);    
  221. element.addEventListener(name, observer, useCapture);    
  222. else if (element.attachEvent) {    
  223. this.observers.push([element, name, observer, useCapture]);    
  224. element.attachEvent('on' + name, observer);    
  225. }    
  226. },    
  227. unloadCache: function() {    
  228. if (!Event.observers) return;    
  229. for (var i = 0; i < Event.observers.length; i++) {    
  230. /**   
  231. * 这里与 Ajax.Request 对象设置 request header 的代码异曲同工   
  232. */    
  233. Event.stopObserving.apply(this, Event.observers);    
  234. Event.observers[0] = null;    
  235. }    
  236. Event.observers = false;    
  237. },    
  238. /**   
  239. * 注册对象的事件处理,并记录到cache中   
  240. */    
  241. observe: function(element, name, observer, useCapture) {    
  242. var element = $(element);    
  243. useCapture = useCapture || false;    
  244.   
  245. if (name == 'keypress' &&    
  246. ((navigator.appVersion.indexOf('AppleWebKit') > 0)    
  247. || element.attachEvent))    
  248. name = 'keydown';    
  249.   
  250. this._observeAndCache(element, name, observer, useCapture);    
  251. },    
  252. /**   
  253. * 取消对象已注册的事件处理   
  254. */    
  255. stopObserving: function(element, name, observer, useCapture) {    
  256. var element = $(element);    
  257. useCapture = useCapture || false;    
  258.   
  259. if (name == 'keypress' &&    
  260. ((navigator.appVersion.indexOf('AppleWebKit') > 0)    
  261. || element.detachEvent))    
  262. name = 'keydown';    
  263.   
  264. if (element.removeEventListener) {    
  265. element.removeEventListener(name, observer, useCapture);    
  266. else if (element.detachEvent) {    
  267. element.detachEvent('on' + name, observer);    
  268. }    
  269. }    
  270. });    
  271. /* prevent memory leaks in IE */    
  272. /**   
  273. * 页面onload 的时候取消所有事件注册,避免ie内存泄漏的bug   
  274. */    
  275. Event.observe(window, 'unload', Event.unloadCache, false);    
  276. /**   
  277. * Position 对象也是常用的工具类,提供了获取元素在页面上位置的函数,Drag&Drop的效果一定常会用到   
  278. * 具体的应用参考 script.aculo.us 基于prototype 的实现,尤其是dragdrop.js。   
  279. */    
  280. var Position = {    
  281. // set to true if needed, warning: firefox performance problems    
  282. // NOT neeeded for page scrolling, only if draggable contained in    
  283. // scrollable elements    
  284. includeScrollOffsets: false,    
  285. // must be called before calling withinIncludingScrolloffset, every time the    
  286. // page is scrolled    
  287. prepare: function() {    
  288. this.deltaX = window.pageXOffset    
  289. || document.documentElement.scrollLeft    
  290. || document.body.scrollLeft    
  291. || 0;    
  292. this.deltaY = window.pageYOffset    
  293. || document.documentElement.scrollTop    
  294. || document.body.scrollTop    
  295. || 0;    
  296. },    
  297. /**   
  298. * 当对象所处的页面有滚动条是,计算位移   
  299. */    
  300. realOffset: function(element) {    
  301. var valueT = 0, valueL = 0;    
  302. do {    
  303. valueT += element.scrollTop || 0;    
  304. valueL += element.scrollLeft || 0;    
  305. element = element.parentNode;    
  306. while (element);    
  307. return [valueL, valueT];    
  308. },    
  309. /**   
  310. * 计算出对象在页面上的位置   
  311. */    
  312. cumulativeOffset: function(element) {    
  313. var valueT = 0, valueL = 0;    
  314. do {    
  315. valueT += element.offsetTop || 0;    
  316. valueL += element.offsetLeft || 0;    
  317. element = element.offsetParent;    
  318. while (element);    
  319. return [valueL, valueT];    
  320. },    
  321. // caches x/y coordinate pair to use with overlap    
  322. /**   
  323. * 判断一个坐标是否在指定元素的空间范围中   
  324. * 比如你想判断鼠标点击点的坐标是否在某个层或窗口   
  325. */    
  326. within: function(element, x, y) {    
  327. if (this.includeScrollOffsets)    
  328. return this.withinIncludingScrolloffsets(element, x, y);    
  329. this.xcomp = x;    
  330. this.ycomp = y;    
  331. this.offset = this.cumulativeOffset(element);    
  332. return (y >= this.offset[1] &&    
  333. y < this.offset[1] + element.offsetHeight &&    
  334. x >= this.offset[0] &&    
  335. x < this.offset[0] + element.offsetWidth);    
  336. },    
  337. withinIncludingScrolloffsets: function(element, x, y) {    
  338. var offsetcache = this.realOffset(element);    
  339. this.xcomp = x + offsetcache[0] - this.deltaX;    
  340. this.ycomp = y + offsetcache[1] - this.deltaY;    
  341. this.offset = this.cumulativeOffset(element);    
  342. return (this.ycomp >= this.offset[1] &&    
  343. this.ycomp < this.offset[1] + element.offsetHeight &&    
  344. this.xcomp >= this.offset[0] &&    
  345. this.xcomp < this.offset[0] + element.offsetWidth);    
  346. },    
  347. // within must be called directly before    
  348. /**   
  349. * 调用该方法时,确保首先调用了within方法   
  350. * 如果x,y坐标位于element的空间范围中,那么返回一个小于1的标示位置的值,比如0.5标示该坐标位于element空间的中线上   
  351. */    
  352. overlap: function(mode, element) {    
  353. if (!mode) return 0;    
  354. if (mode == 'vertical')    
  355. return ((this.offset[1] + element.offsetHeight) - this.ycomp) /    
  356. element.offsetHeight;    
  357. if (mode == 'horizontal')    
  358. return ((this.offset[0] + element.offsetWidth) - this.xcomp) /    
  359. element.offsetWidth;    
  360. },    
  361. /**   
  362. * 复制源对象的空间数据到目的对象。   
  363. * 常用的地方:拖缀一个层到新地方时,常常动态构造和该层同样大小的虚层。   
  364. */    
  365. clone: function(source, target) {    
  366. source = $(source);    
  367. target = $(target);    
  368. target.style.position = 'absolute';    
  369. var offsets = this.cumulativeOffset(source);    
  370. target.style.top = offsets[1] + 'px';    
  371. target.style.left = offsets[0] + 'px';    
  372. target.style.width = source.offsetWidth + 'px';    
  373. target.style.height = source.offsetHeight + 'px';    
  374. }    
  375. }   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值