分享

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

 douli 2005-07-23

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);
  }
});


此日记TrackBack地址: http://www./trackback.do?log_id=866

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多