JS获取form下的所有有效的数据

/**
 * 按PHP语言的标准形式,获取form表单关联的合法数据:不包括文件控件
 * [说明]已测试并对比过所有具有 字符实体、空白字符、[] 特殊情况的name属性和数据,以及提交按钮、多选下拉列表,结果完全一致
 * [说明]此方法代码量虽大,但远比new FormData()方法更加细致入微
 * @param {Element} submitButton [默认:表单中首个提交按钮]提交按钮:请使用提交事件的对象的submitter属性
 * @param {String} prefixToKeys [默认:'']为每个索引拼接的前缀,防止与原型重复
 * @returns {Object} 一或二维的全为字符串的JSON数据
 */
HTMLFormElement.prototype.getValues = function (submitButton, prefixToKeys) {
  const forms = Object.values(this)
  // 如果参数2不在form中,或者不是submit按钮,改为form中首个submit按钮
  if (forms.indexOf(submitButton) < 0 || !isSubmit(submitButton)) {
    let i = 0, len = form.length
    for (; i < len; i++) if (isSubmit(form[i])) break
    submitButton = form[i]
  }
  // 重置索引前缀,用于防止索引与原型冲突
  prefixToKeys = prefixToKeys || ''
  // 多选数据允许的最大整数索引
  const MAX_SAFE_INTGER = 0x7FFFFFFF
  // 用于存储合法数据
  const values = {}
  // 用于存储合法数据的表单元素,且与values所有索引一一对应
  const elements = {}
  // 遍历控件,获取并转化所有合法数据
  form.forEach(function (e) {
    // 获取name属性,并解析转化字符实体、换行符,再并去除左边空格
    let name = parse(e.name).replace(/^ +/, '')
      , k1 = name.indexOf('[') // 获取首个多选起始字符位置
      , k2
    // 如果控件被禁用、name为空、首字符是多选起始字符、文件控件则忽略,会中断并继续下一项
    if (!name || e.disabled || k1 === 0 || e.tagName === 'INPUT' && e.type === 'file') return
    // 单选:name中所有空格替换为'_'
    if (k1 < 0) setValue(e, name.split(' ').join('_'), values, elements)
    // 其它
    else {
      // 把多选起始字符之前的所有空格替换为'_'
      const prefix = name.substring(0, k1).split(' ').join('_')
      // 获取'['之后的首个']'位置
      k2 = name.indexOf(']', ++k1)
      // 单选:name中首个'['转为下划线,并保留后面文本
      if (k2 < 0) setValue(e, prefix + '_' + name.substring(k1), values, elements)
      // 多选
      else {
        // 用于设置多选时的二维索引:获取'['和']'之间的文本,后面的忽略
        k1 = name.substring(k1, k2)
        // 重置name
        name = prefixToKeys + prefix
        // 多选时调整为对象(不能是数组)
        if (typeof values[name] !== 'object') values[name] = {}, elements[name] = {}
        // 用于判断索引采用的形式
        let isSet = true
        // 满足条件时采用自增的整数索引,否则索引不变
        if (k2 = k1 === '' || k1 === ' ') {
          // 获取当前name数据的最大的索引
          k1 = Math.min(MAX_SAFE_INTGER, getMaxIntKey(values[name]) + 1)
          // 仅当 预设索引<安全整数 时才自增和设置数据
          isSet = k1 < MAX_SAFE_INTGER
        }
        if (isSet) setValue(e
          , k1
          , values[name]
          , elements[name]
          // 如果select标签多选时设置了二维索引,那么只获取最后一项,即反转k2;为真时执行
          , !k2
        )
      }
    }
  })
  // 清除空的多选项
  Object.keys(values).forEach(function (k) {
    if (typeof values[k] === 'object' && Object.keys(values[k]).length === 0) {
      delete values[k]
      delete elements[k]
    }
  })
  /**
   * 获取select标签多选时的数据
   * @param {HTMLSelectElement} select select标签
   * @param {String|Number} name 设置的索引
   * @param {Boolean} isLastSelected [false]是否只选择select标签的最后一项
   * @returns {Object}
   */
  function getSelectMultipleValues(select, name, isLastSelected) {
    let i = 0, j = select.length, r = {}
    // 只取最后一项选中的
    if (isLastSelected) while (j--) if (select[j].selected) {
      r[prefixToKeys + name] = select[j].value
      break
    }
    // 获取所有选中值:此时name为自然数,且不大于最大整数
    else for (; i < j; i++) if (select[i].selected) r[prefixToKeys + Math.min(name++, MAX_SAFE_INTGER)] = select[i].value
    return r
  }
  /**
   * 获取对象中最大的整数索引,且不大于最大整数
   * @param {Object} obj 获取的对象
   * @returns {Number}
   */
  function getMaxIntKey(obj) {
    let k = Object.keys(obj), i = k.length, max = -1, pl = prefixToKeys.length, t
    while (i--) {
      // 因为有前缀所以要先去除
      t = k[i].substring(pl)
      // 获取最大整数索引
      if (t <= MAX_SAFE_INTGER && t.match(/^[1-9]\d*$/) || t === '0') max = Math.max(max, +t)
    }
    return max
  }
  /**
   * 判断参数是否是提交按钮
   * @param {Element} e 判断的标签
   * @returns {Boolean}
   */
  function isSubmit(e) {
    return e && e.type === 'submit' && (e.tagName === 'BUTTON' || e.tagName === 'INPUT')
  }
  /**
   * 正确转化特殊文本:字符实体、换行符
   * @param {String} str 转化的文本
   * @returns {String}
   */
  function parse(str) {
    return str.replace(/&(#\d|\w)+;/g, function (v) {
      const e = document.createElement('span')
      e.innerHTML = v
      return e.textContent
    }).replace(/\n|\r/g, '\r\n')// 注意:win系统下为'\r\n',其它系统可能不一致
  }
  /**
   * 指定索引传入控件数据
   * @param {Element} e 表单控件
   * @param {String} name 设置的索引
   * @param {Object} values 存储数据的1维对象
   * @param {Object} elements 存储控件的1维对象
   * @param {Boolean} isLastSelected [false]是否只选择select标签的最后一项
   */
  function setValue(e, name, values, elements, isLastSelected) {
    const n = e.tagName
    const t = e.type // 只有小写;未设置或不支持时,均会转为默认类型
    if (n === 'INPUT' && t !== 'reset' && t !== 'button') {
      name = prefixToKeys + name
      // 如果是提交按钮,且与设定的一致,则录入
      if (t === 'submit') {
        if (e === submitButton) elements[name] = e, values[name] = parse(e.value)
      }
      // 如果不是提交按钮:如果不是单/复选框,记录;否则记录选中的
      else if (t !== 'radio' && t !== 'checkbox' || e.checked) elements[name] = e, values[name] = parse(e.value)
    } else if (n === 'SELECT') {
      // 若无选项则不记录
      if (e.length === 0) return
      // 如果是多选,补充到数据对象中(已对索引补前缀);否则直接设置
      e.multiple ? Object.assign(values, getSelectMultipleValues(e, name, isLastSelected)) : (name = prefixToKeys + name, elements[name] = e, values[name] = parse(e.value))
    } else if (n === 'TEXTAREA' || e === submitButton) {
      name = prefixToKeys + name
      elements[name] = e
      values[name] = parse(e.value)
    }
  }
  return values
}
/**
 * PHP语言对html表单的name属性的转化方式(左边数字表示优先级,0最大)
 * 0)所有表单控件均有type属性,如果未设置或不支持均会转为默认值,并且会自动转为全小写
 * 0)严格区分大小写
 * 0)'[]'两个字符分别为多选起始符、多选终止符
 * -1)解析并转化字符实体、换行符
 * -2)去除左边的空格(非字符实体)
 * -3)控件被禁用、name为空(去除'[xxx]'部分后)、首字符是'['、文件控件则忽略
 * -4)多选时,自增的最大整数索引(设为N)为2^31-1,即0x7FFFFFFF
 * -4)出现相同的name名称(去除'[xxx]'部分后),如果之前不是多选,或者与之前选择类型不同,那么之前的数据一定会被覆盖掉
 * -5)把首个'['之前的所有空格替换为'_'
 * -6)如果存在'[',但其后不存在']',则会把首个'['替换为'_',并保留所有文本
 * -7)如果存在'[',且其后存在']',则会以首个'['到其后首个']'之间的文本(设为S)作为二维索引,忽略其后的文本,但是:
 *   如果S为''或' ',则以当前数据中最大的自然数索引+1(称:预设索引),但是:
 *     预设索引<=N时,预设索引不变
 *     预设索引>N时,会忽略当前数据,即保持索引为N的初始数据不变,也不再添加任何数据
 *   否则直接以S作为二维索引(无转化)存储数据
 *   基于以上规则,如果是select标签多选,那么当前数据会插入被选中的数据,而不是新增单独一组
 * -8)按钮中仅具有name合法属性的[type="submit"]的提交按钮可以上传自身value,但是:
 *   如果是通过当前按钮提交的,则上传当前按钮的value值
 *   否则找到关联form的首个具有name合法属性的[type="submit"]的提交按钮的数据,如果未被禁用则上传,否则不上传
 */
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值