Prototype 提供了方便的扩展机制让用户可以把自定义的方法加入到框架中。用户可以在独立的文件中编写代码,而不用修改prototype.js本身,大大的增加了代码的可维护性,和可扩展性。这个方法就是Element.addMethods() 。
在官方的文档中对这个方法的介绍是:
Element.addMethods makes it possible to mix in your own methods to the Element object, which you can later use as methods of extended elements - those returned by the $() utility, for example - or as methods of Element.
方法原型是Element .addMethods([methods])<o:p></o:p>
一个最简单的使用:<o:p></o:p>
- Element.addMethods({
- sayHello: function() {
- alert(‘hello!’);
- }
- });
$(anything).sayHello();
//弹出对话框 hello
<o:p> </o:p>
复杂点的用法,加上参数:
- Element.addMethods({
- ajaxUpdate: function(element, url, options){
- element = $(element);
- element.update('
- new Ajax.Updater(element, url, options);
- return element;
- }
- });
$(element).ajaxUpdate('/new/content');
// ->返回 HTMLElement
<o:p> </o:p>
仔细看一下Prototype.js最后一行调用了这一个方法,但是不带任何参数。文档里面说明,这样的用法是遍历Element.Methods, Element.Methods.Simulated, Form.Methods 和 Form.Element.Methods对象,把所有的方法加入到相应的元素中。例如:<o:p></o:p>
- Form.Element.Methods.processing = function(element, text) {
- element = $(element);
- if (element.tagName.toLowerCase() == 'input' && ['button', 'submit'].include(element.type))
- element.value = typeof text == 'undefined' ? 'Please wait...' : text;
- return element.disable();
- };
- Element.addMethods();
然后就可以通过$(someInputElement).processing()来调用了。<o:p></o:p>
借用这个机制,可以方便的把自己写的方法加入进来。比如我上一篇博客提到的无刷上传。
言归正传,看看prototype是怎么实现这个机制的。以下是实现的源代码:(1.6rc)
- Element.addMethods = function(methods) {
- var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
- //
- if (!methods) { //如果没有提供参数,那么默认初始化全部方法
- Object.extend(Form, Form.Methods);
- Object.extend(Form.Element, Form.Element.Methods);
- Object.extend(Element.Methods.ByTag, {
- "FORM": Object.clone(Form.Methods),
- "INPUT": Object.clone(Form.Element.Methods),
- "SELECT": Object.clone(Form.Element.Methods),
- "TEXTAREA": Object.clone(Form.Element.Methods)
- });
- }//继承Methods里面所有的方法
- if (arguments.length == 2) {//如果提供的参数是2个
- var tagName = methods;//把第一个参数视为tagName
- methods = arguments[1];
- }
- //如果没有定义tagName,那么默认把methods添加到Element.Methods中
- if (!tagName) Object.extend(Element.Methods, methods || { });
- else {
- if (Object.isArray(tagName)) tagName.each(extend);
- else extend(tagName);
- }//调用extent方法,如果tagName是数组,那么依次调用extent方法。
- function extend(tagName) {
- tagName = tagName.toUpperCase();
- if (!Element.Methods.ByTag[tagName])
- Element.Methods.ByTag[tagName] = { };
- Object.extend(Element.Methods.ByTag[tagName], methods);
- } //在Element.Methods.ByTag中存放以tagName为标识的继承了methods的对象
- function copy(methods, destination, onlyIfAbsent) {
- onlyIfAbsent = onlyIfAbsent || false;
- for (var property in methods) {
- var value = methods[property];
- if (!Object.isFunction(value)) continue;
- if (!onlyIfAbsent || !(property in destination))
- destination[property] = value.methodize();
- }
- }//此方法用来把methods里面的所有方法复制到destination中
- function findDOMClass(tagName) {
- var klass;
- var trans = {
- "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
- "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
- "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
- "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
- "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
- "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
- "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
- "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
- "FrameSet", "IFRAME": "IFrame"
- };
- if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
- if (window[klass]) return window[klass];
- klass = 'HTML' + tagName + 'Element';
- if (window[klass]) return window[klass];
- klass = 'HTML' + tagName.capitalize() + 'Element';
- if (window[klass]) return window[klass];
- window[klass] = { };
- window[klass].prototype = document.createElement(tagName).__proto__;
- return window[klass];
- }此方法用来在各种不同的浏览器中找到正确的html dom 类名,然后返回之
- //接着处理,下面就是该把对应每个标签的自定义方法绑定到标签的prototype上了。
- if (F.ElementExtensions) {
- copy(Element.Methods, HTMLElement.prototype);
- copy(Element.Methods.Simulated, HTMLElement.prototype, true);
- }
- if (F.SpecificElementExtensions) {
- for (var tag in Element.Methods.ByTag) {
- var klass = findDOMClass(tag);
- if (Object.isUndefined(klass)) continue;
- copy(T[tag], klass.prototype);
- }
- }//把Element.Methods.ByTag里面的方法绑定到相应的tagName上
- Object.extend(Element, Element.Methods);//把Element.Methods里的方法绑到Element里
- delete Element.ByTag; //删掉Element.ByTag;
- if (Element.extend.refresh) Element.extend.refresh();
- Element.cache = { };//刷新并清空缓存,扫尾。
- };