prototype 源码解读的第三部分 - form.js

form.js 中主要定义了一些静态工具类,比如 Form.serialize 获取表单的所有元素的值并组合成以 & 相连的querystring,这显然用于组合XmlHttp 提交数据。

另外值得关注的是 Form.Observer,我们可以学习到 javascript 如何应用设计模式。

 form.js


/**
 * 针对 页面元素对象 的工具类,提供一些简单静态方法
 
*/

var Field = {
  
/**
   * 清除参数引用对象的值
   
*/

  clear: 
function() {
    
for (var i = 0; i < arguments.length; i++)
      $(arguments[i]).value 
= '';
  }
,

  
/**
   * 使参数引用对象获取焦点
   
*/

  focus: 
function(element) {
    $(element).focus();
  }
,
  
  
/**
   * 判断参数引用对象值是否为空,如为空,返回false, 反之true
   
*/

  present: 
function() {
    
for (var i = 0; i < arguments.length; i++)
      
if ($(arguments[i]).value == '') return false;
    
return true;
  }
,
  
  
/**
   * 使选中参数引用对象 
   
*/

  select: 
function(element) {
    $(element).select();
  }
,

  
/**
   * 使参数引用对象处于可编辑状态
   
*/

  activate: 
function(element) {
    $(element).focus();
    $(element).select();
  }

}


/*--------------------------------------------------------------------------*/

/**
 * 表单工具类
 
*/

var Form = {
  
/**
   * 将表单元素序列化后的值组合成 QueryString 的形式
   
*/

  serialize: 
function(form) {
    
var elements = Form.getElements($(form));
    
var queryComponents = new Array();
    
    
for (var i = 0; i < elements.length; i++{
      
var queryComponent = Form.Element.serialize(elements[i]);
      
if (queryComponent)
        queryComponents.push(queryComponent);
    }

    
    
return queryComponents.join('&');
  }
,
  
  
/**
   * 得到表单的所有元素对象
   
*/

  getElements: 
function(form) {
    form 
= $(form);
    
var elements = new Array();

    
for (tagName in Form.Element.Serializers) {
      
var tagElements = form.getElementsByTagName(tagName);
      
for (var j = 0; j < tagElements.length; j++)
        elements.push(tagElements[j]);
    }

    
return elements;
  }
,
  
  
/**
   * 将指定表单的元素置于不可用状态
   
*/

  disable: 
function(form) {
    
var elements = Form.getElements(form);
    
for (var i = 0; i < elements.length; i++{
      
var element = elements[i];
      element.blur();
      element.disable 
= 'true';
    }

  }
,

  
/**
   * 使表单的第一个非 hidden 类型而且处于可用状态的元素获得焦点
   
*/

  focusFirstElement: 
function(form) {
    form 
= $(form);
    
var elements = Form.getElements(form);
    
for (var i = 0; i < elements.length; i++{
      
var element = elements[i];
      
if (element.type != 'hidden' && !element.disabled) {
        Field.activate(element);
        
break;
      }

    }

  }
,

  
/*
   * 重置表单
   
*/

  reset: 
function(form) {
    $(form).reset();
  }

}


/**
 * 表单元素工具类
 
*/

Form.Element 
= {
  
/**
   * 返回表单元素的值先序列化再进行 URL 编码后的值
   
*/

  serialize: 
function(element) {
    element 
= $(element);
    
var method = element.tagName.toLowerCase();
    
var parameter = Form.Element.Serializers[method](element);
    
    
if (parameter)
      
return encodeURIComponent(parameter[0]) + '=+ 
        encodeURIComponent(parameter[
1]);                   
  }
,
  
  
/**
   *  返回表单元素序列化后的值
   
*/

  getValue: 
function(element) {
    element 
= $(element);
    
var method = element.tagName.toLowerCase();
    
var parameter = Form.Element.Serializers[method](element);
    
    
if (parameter) 
      
return parameter[1];
  }

}


/**
 * prototype 的所谓序列化其实就是将表单的名字和值组合成一个数组
 
*/

Form.Element.Serializers 
= {
  input: 
function(element) {
    
switch (element.type.toLowerCase()) {
      
case 'hidden':
      
case 'password':
      
case 'text':
        
return Form.Element.Serializers.textarea(element);
      
case 'checkbox':  
      
case 'radio':
        
return Form.Element.Serializers.inputSelector(element);
    }

    
return false;
  }
,
  
  inputSelector: 
function(element) {
    
if (element.checked)
      
return [element.name, element.value];
  }
,

  textarea: 
function(element) {
    
return [element.name, element.value];
  }
,

  
/**
   * 看样子,也不支持多选框(select-multiple)
   
*/

  select: 
function(element) {
    
var index = element.selectedIndex;
    
var value = element.options[index].value || element.options[index].text;
    
return [element.name, (index >= 0? value : ''];
  }

}


/*--------------------------------------------------------------------------*/

/**
 * Form.Element.getValue 也许会经常用到,所以做了一个快捷引用
 
*/

var $F = Form.Element.getValue;

/*--------------------------------------------------------------------------*/

/**
 * Abstract.TimedObserver 也没有用 Class.create() 来创建,和Ajax.Base 意图应该一样
 * Abstract.TimedObserver 顾名思义,是套用Observer设计模式来跟踪指定表单元素,
 * 当表单元素的值发生变化的时候,就执行回调函数
 *
 * 我想 Observer 与注册onchange事件相似,不同点在于 onchange 事件是在元素失去焦点的时候才激发。
 * 同样的与 onpropertychange 事件也相似,不过它只关注表单元素的值的变化,而且提供timeout的控制。
 * 
 * 除此之外,Observer 的好处大概就在与更面向对象,另外可以动态的更换回调函数,这就比注册事件要灵活一些。
 * Observer 应该可以胜任动态数据校验,或者多个关联下拉选项列表的连动等等
 *
 
*/

Abstract.TimedObserver 
= function() {}

/**
 * 这个设计和 PeriodicalExecuter 一样,bind 方法是实现的核心
 
*/

Abstract.TimedObserver.prototype 
= {
  initialize: 
function(element, frequency, callback) {
    
this.frequency = frequency;
    
this.element   = $(element);
    
this.callback  = callback;
    
    
this.lastValue = this.getValue();
    
this.registerCallback();
  }
,
  
  registerCallback: 
function() {
    setTimeout(
this.onTimerEvent.bind(this), this.frequency * 1000);
  }
,
  
  onTimerEvent: 
function() {
    
var value = this.getValue();
    
if (this.lastValue != value) {
      
this.callback(this.element, value);
      
this.lastValue = value;
    }

    
    
this.registerCallback();
  }

}


/**
 * Form.Element.Observer 和 Form.Observer 其实是一样的
 * 注意 Form.Observer 并不是用来跟踪整个表单的,我想大概只是为了减少书写(这是Ruby的一个设计原则)
 
*/
 
Form.Element.Observer 
= Class.create();
Form.Element.Observer.prototype 
= (new Abstract.TimedObserver()).extend({
  getValue: 
function() {
    
return Form.Element.getValue(this.element);
  }

}
);

Form.Observer 
= Class.create();
Form.Observer.prototype 
= (new Abstract.TimedObserver()).extend({
  getValue: 
function() {
    
return Form.serialize(this.element);
  }

}
);
posted on 2006-03-25 18:49  jayu  阅读(172)  评论(0)    收藏  举报