====DOM===========================================================================
=DAY-01=====================================
正课:
1. 什么是DOM
2. *DOM Tree
3. *查询: 4种
1. 什么是DOM: Document Object Model
什么是:
专门操作网页内容的统一的API标准——W3C
为什么:
早期的DOM时木有标准,存在严重的兼容性问题
DOM就是为了统一操作网页内容的API标准
用DOM操作网页内容,几乎100%兼容所有浏览器
何时:
只要操作网页内容,都用DOM
原生JS:只需要浏览器,不需要下载第三方软件,就可以操作
ECMAScript(核心语法)
+DOM(操作网页内容)
+BOM(操作浏览器窗口)
如何:
2. DOM Tree:
什么是:
在内存中,存储网页中所有内容的树型结构
为什么:
树型结构最好的展现层次关系结构,且可无限向下延伸。
何时:
当浏览器读到网页内容时,就会自动在内存中创建树形结构
只要存储不确定层级深度的上下级关系,都用树型结构
如何:
自动创建,自动维护
1. 当浏览器读取到HTML文档时,开始创建
2. 首先创建根节点document
document对象是整棵DOM树的树根
所有网页内容,都是document节点的后代节点
3. 依次读取网页中每个元素,属性,文本
网页中每项内容(元素,属性,文本)都是DOM树上的一个节点对象。
节点对象: 所有节点都是node类型的节点对象
三大属性:
1、nodeType: 节点类型
何时: 只要鉴别节点的类型时
值是一个整数,包括:
document 9
element 1
attribute 2
text 3
问题: 只判断是否为元素,无法细致区分元素的标签名
2、nodeName: 节点名称
何时: 只要进一步区分元素的名称时
包括:
document #document
element 全大写的标签名***12个记这一个就够了
attribute 属性名
text #text
3、nodeValue: 表示节点的值——了解
何时使用:几乎不用
document null
elem null
attr 属性值
text 文本内容
DOM: *查询,修改,添加,删除,事件绑定
所有DOM操作,都遵循4步
1、查找触发事件的元素
2、绑定事件处理函数
3、查找要修改的元素
4、修改其内容
3. 查找: 4种: ***
1.VIP 不需要查找就可直接获得元素:
document.documentElement -->html
document.head -->head
document.body -->body
document.forms[i(下标)/id/name] -->form
2. 按节点间关系查找:
什么是:
DOM树中任何节点都不是孤立的。
一个节点和父级,子级,兄弟之间都建立了联系
何时:
如果已经获得一个节点,找周围附近的有关系的节点时
如何: 2大类关系:
节点树: 包含所有网页内容(元素,文字)的完整树结构
1. 父子关系:
elem.parentNode 获得elem的父节点
返回值: 唯一的一个父节点对象
elem.childNodes 获得elem的所有*直接*子节点
返回值: 所有直接子节点组成的类数组对象
elem.firstChild 获得elem下的第一个子节点
elem.lastChild 获得elem下的最后一个子节点
2. 兄弟关系:
elem.previousSibling 获得elem的前一个兄弟节点
elem.nextSibling 获得elem的后一个兄弟节点
问题:
受看不见的空字符的干扰
一切文本都是节点对象,包括看不见的空字符,
也是节点对象(tab,空格,换行)
解决: 元素树
元素树: 仅包含元素节点的树结构
优: 不受看不见的空字符的干扰
1. 父子关系:
elem.parentElement 获得elem的父元素
.parentNode 父元素只会是元素
elem.children 获得elem的所有*直接*子元素
返回值: 所有直接子元素组成的类数组对象
elem.firstElementChild 获得elem下的第一个子元素
elem.lastElementChild 获得elem下的最后一个子元素
2. 兄弟关系:
elem.previousElementSibling 获得elem的前一个兄弟元素
elem.nextElementSibling 获得elem的后一个兄弟元素
元素树不是一颗新树,只是节点树的一个子级
问题: 1. IE9+
2. 遍历指定父节点下的所有后代节点: ——鄙视
问题: children和childNodes只能查找直接子节点,无法查找更深层次!
解决: 递归遍历:
如何: 2步:
1. 先定义函数,仅遍历直接子节点
2. 对每个直接子节点,调用和父元素完全相同的方法
算法: 深度优先遍历:
什么是:
每次同时碰到子元素和兄弟元素时,总是优先遍历子元素。
所有子元素遍历完,才返回遍历兄弟。
问题: children和childNodes返回动态集合
什么是: 不实际存储数据,每次访问集合,都重新查找DOM树.
优: 首次查找,不需要返回完整数据,效率高!
缺: 反复访问集合,导致反复查找DOM树,效率低!
错误: for(var i=0;i 解决: 遍历时,提前缓存length 正确: for(var i=0,len=children.length;i 问题: 递归效率低,避免使用 解决: 用循环代替: 节点迭代器: NodeIterator: 什么是: 可按深度优先遍历的顺序,依次遍历下一个节点的对象 如何: 2步: 1. 用父元素创建节点迭代器对象: var iterator=document.createNodeIterator( parent,NodeFilter.SHOW_ELEMENT(遍历元素节点),null,false 开始位置 .SHOW_ALL(遍历所有节点) ); 2. 循环调用Iterator迭代器的nextNode()方法:获取下一个节点对象 nextNode()2件事: 1. 返回当前节点, 2. 跳到下一个节点 如果没有后续节点,返回null do{ var node=iterator.nextNode(); if(node)//if(node!=null) console.log(node.nodeName); else break; }while(true) 3. 按HTML查找: 4种: 1. 按id查找: var elem=document.getElementById("id"); 强调: 只能用document调用! 返回值: 返回一个元素(id重复时,返回第一个) 找不到时,返回null 2. 按标签名(tag name)查找: var elems=parent.getElementsByTagName("标签名"); 强调: 1. 可在任意父元素上调用! 2. 不仅找直接子元素,且查找所有后代中的元素 返回值: 返回包含多个元素的动态集合(类数组对象) 找不到,返回length为0的空集合 3. 按name属性查找:(表单常用这个) var elems=document.getElementsByName("name"); 强调: 只能用document调用 返回值: 返回包含多个元素的动态集合(类数组对象) 找不到,返回length为0的空集合 4. 按class属性查找: var elems=parent.getElementsByClassName("class"); 返回值: 返回包含多个元素的动态集合(类数组对象) 找不到,返回length为0的空集合 强调: 1. 可在任意父元素上调用! 2. 不仅找直接子元素,且在查找所有后代 3. 如果一个元素同时被多个class修饰时,只要其中一个class匹配,就可找到该元素 问题: 每次只能按一种条件查找,当查找条件复杂时,步骤就非常繁琐。 解决: 用选择器查找 4. 按照选择器查找: 为什么: 按HTML查找,每次只能按一个条件查找 如果查找条件复杂时,步骤会很繁琐 何时: 只要查找条件复杂时,都用选择器查找 1. 仅查找一个元素: var elem=parent.querySelector("选择器"); 返回值:一个元素对象,没找到,返回null 2. 查找多个元素: var elems=parent.querySelectorAll("选择器"); 返回值: 包含所有符合条件元素的非动态集合 如果找不到,返回length为0的空集合 非动态集合: 实际存储完整数据,即使反复访问集合,也不会反复查找DOM树 强调: 1. 可在任何父元素上调用 2. 不仅查找直接,切查找所有后续元素 3. 受制于浏览器的兼容性限制 鄙视:getElementsByTagName VS querySelectorAll 返回值: html-->动态集合for(var len=xxx.length) 选择器查找-->非动态集合 首次查找: 前者块,后者慢 易用性: 总结:如果只要一个条件就可获得想要的 =DAY-02====================================================================== 4. 修改: 3种: 1. 内容: 3种: 1. 获取或修改元素的HTML代码片段内容:(代码) elem.innerHTML 2. 获取或修改元素的纯文本内容(网页显示的) elem.textContent vs innerHTML: 1. 去掉内嵌标签 2. 将转移字符翻译为原文 说明:兼容性 3. 获取或修改表单元素的值 elem.value 2. 属性: 1. HTML标准属性: HTML中规定的值,值为字符串类型的 2种: 1. 核心DOM: 支持操作一切结构化文档(html和xml)的统一API 优点: 万能!几乎可以做任何事情 缺点:繁琐! (属性节点都保存在elem的attributes集合中 var node=elem.attributes["属性名"]; var value=node.nodeValue;)-这句他没讲 获取: var value=elem.getAttribute("属性名") 修改: elem.setAttribute("属性名","值") 判断有没有: var bool=elem.hasAttribute("属性名") 有-true 移除: elem.removeAttribute("属性名") 2. HTML DOM: 专门操作HTML文档的简化版DOM API 特点: 简洁, 不是万能! 原理: HTML DOM将所有HTML标准属性,已经提前预定义在了元素对象上,默认值为"" 如何: 获取: var value=elem.属性名 修改: elem.属性名=值 判断有没有: elem.属性名!=="" 有true 移除: elem.属性名="" 特例: class属性:obj.class类型名=> .className样式类名 核心DOM: 可直接使用class HTML DOM: 必须换为.className => 就是HTML中的class 因为js的对象中已经提前有一个内部属性class,用来记录对象创建时的类型名 2. 状态属性: disabled selected checked 特点: 值是bool类型 不能用核心DOM操作,因为核心DOM只能操作字符串类型 只能用HTML DOM 打. 操作 都有对应的选择器: :check : 3. 自定义扩展属性: 什么是: 自定义的,不在HTML标准范围内的属性 为什么: id的问题: 唯一! class的问题: 不稳定,可能随时人为或被程序修改 何时: 1、只要标识多个元素,且不希望受个数和样式修改影响 2、代替id,elem,class选择器,用于选中触发事件的元素 3、保存自定义的业务数据 总结: 今后,只要给元素添加行为时,查找元素都用自定义扩展属性 如何: 1.html中定义: 说明:data- 只是扩展属性的前缀标志,并不是属性名的一部分 选择器:[data-属性名=值] 2. 获取或设置: 2种: HTML DOM无法操作自定义扩展属性, 因为自定义扩展属性,无法提前预定义在DOM元素上 1. 核心DOM 2. HTML5: elem.dataset.属性名 说明:dataset会收集所有data-XX前缀的扩展属性 访问时,仅凭elem.dataset.XX就可以 3. 样式: 2种: 1. 内联样式: 用style设置的css属性,默认出现在内联样式中 特点: 优先级最高, 仅当前元素独有 修改: 1. 仅修改一个内联样式 elem.style.css属性名=值 强调: 所有css属性名要去横线变驼峰 比如: z-index => zIndex font-size=> fontSize background-position=>backgroundPosition 2. 批量替换内联样式: elem.style.cssText="..."; 获取: 错误: elem.style.css属性 style仅表示内联样式,elem.style只能获得内联延时,无法获得外部样式,丢样式 解决: 获得计算后的样式: 什么是: 最终应用到当前元素上的所有样式的合集 为什么: 一个元素的完整样式,可能来源自多个地方 何时: 只要获取样式,都要获得计算后的完整样式! 如何: 2步: 1. 获得计算后的完整style对象 var style=getComputedStyle(elem); 2. 获得style中的css属性值; var value=style.css属性; 强调: 计算后的样式style是只读的,不能修改! 2. 修改样式表中的样式: ——了解 1. 获得样式表对象:sheet var sheet=document.styleSheets[i]; 2. 获得要修改的属性所在的cssRule(样式表对象中的一套规则:(一对儿{}中的内容) var rule=sheet.cssRules[i]; 说明: 如果是keyframes 继续获得 继续: var sub_rule=rule.cssRules[i] 3. 修改样式: rule.style.css属性=值 问题: 一句话只能修改一个css属性值 解决: 今后都是用class来批量修改元素的样式 所有DOM操作,都遵循4步 1、查找触发事件的元素 2、绑定事件处理函数 3、查找要修改的元素 4、修改其内容 =DAY03======================================================== 5. 添加和删除: 3步: 1. 创建空元素: var elem=document.createElement("标签名"); ex: var a=document.createElement("a"); 2. 设置关键属性: a.href="http://tmooc.cn"; a.innerHTML="go to tmooc"; 3. 将新元素添加到DOM树 3种: 指定父元素末尾追加: parent.appendChild(a) 在当前子元素前插入: parent.insertBefore(a, child) 将a插入到child之前 替换现有子元素: parent.replaceChild(a, child) 用a替换child DOM优化: 尽量减少操作DOM树的次数 为什么: 每次操作DOM树都会导致重新layout和paint 什么是layout? 网页的加载原理: html -> DOM Tree ↓ Render Tree-> ***layout*** -> paint ↑ 计算每个元素的 css -> COM 绝对布局位置 只要修改DOM树, 包括: 修改样式,修改位置,添加删除元素 都会导致重新layout ——>效率低 解决: 1. 如果同时添加父元素和子元素时, 都要先在内存中将所有子元素,添加到父元素中, 最后,再将父元素,一次性添加到DOM树。 2. 如果父元素已经在树上,要同时添加多个平级子元素时: 先将多个子元素添加到文档片段中,再将文档片段一次性添加到DOM树中 文档片段: 什么是: 内存中,临时存储一棵dom子树片段的存储空间(虚拟父元素对象) 何时: 只要同时添加多个平级子元素时 如何: 3步: 1. 创建文档片段对象 var frag=document.createDocumentFragment(); 2. 先将子元素添加到文档片段中 frag.appendChild(child) 3. 将文档片段一次性添加到DOM树指定父元素下 parent.appendChild(frag) 强调: frag将子元素送到dom树后,自动释放 删除: parent.removeChild(child) child.parentNode.removeChild(child); 2. HTML DOM常用对象: 什么是: 对常用的元素,提供了简化版的API 优: 简化 缺: 不是万能 Image: 创建: var img=new Image(); Select: 属性: .value 当前选中项有value属性时,会返回option的value 如果选中项没有value属性,则用内容代替 .selectedIndex 快速获得当前选中项的下标位置 .options: 获得select下所有option元素对象的集合 .options.length 获得select下option个数 .length => .options.length 固定套路: 清空所有option sel.innerHTML="";(包打天下) sel.length=0; =>sel.options.length=0;清除所有option 方法: add(option) 代替 appendChild(option) 问题: .add不支持frag(文档片段) .remove(i) 移除i位置的option %、Option:*** 创建: var opt=new Option(text,value); 属性: .index .text .value %、table:管着行分组: 创建: var thead=table.createTHead(); var tbody=table.createTBody(); var tfoot=table.createTFoot(); 删除: table.deleteTHead(); table.deleteTFoot(); 获取: tabel.tHead table.tFoot table.tBodies[i] %、行分组:管着行: 创建: var tr=行分组.insertRow(i) 在i位置插入一个新行,原i为位置的行 向后挤压 固定套路: 1.在开头插入一行: 行分组.insertRow(0) 2. 在结尾追加一行: 行分组.insertRow() 删除: 行分组.deleteRow(i)删除i位置的行 强调: 主语是行分组时,i要求是在行分组内的相对下标位置 问题:行分组内的相对下标无法自动获取 解决:table.deleteRow(tr.rowIndex) tr.rowIndex可自动获得当前tr相对于整个表格的下标位置 table.deleteRow(i) i刚好需要相对于整个表格的下标位置 获取: 行分组.rows 获得行分组内所有行的集合 %、行:管着格: 创建: var td=tr.insertCell(i) 固定套路: 末尾追加: tr.insertCell() 强调:只能创建td不能创建th 删除: tr.deleteCell(i) 获取: tr.cells form: 获取: var form=document.forms[i/id/name]; 属性: form.elements 获得表单中所有表单元素的集合 强调: 表单元素包括: input select textarea button form.elements.length 获得表单中表单元素的个数 form.length==> form.elements.length 固定套路: 获得结尾的按钮: var btn=form.elements[form.length-1] 方法: form.submit() //手动提交表单 表单元素: 获取: var elem= form.elements[i/id/name] 如果表单元素有name属性,则: form.name属性值 方法: elem.focus() 让当前表单获得焦点 elem.blur()让当前元素失去焦点 ===================================================== BOM: Browser Object Model 什么是: 专门操作浏览器窗口或软件的API 没有标准! window: 2个角色: 1. 代替ES中的GLOBAL充当全局作用域对象 2. 封装保存所有内置,全局的API和对象(像alert 啊等) 包括:history,location,document,navigator,screen,event (属性: 文档显示区大小: 浏览器窗口中专门显示网页的区域 /*window.*/innerWidth, /*window.*/innerHeight 没讲) 功能: 打开和关闭窗口: 打开窗口: window.open("url","target") 4种: 1. 在当前窗口打开,可后退 js: open("url","_self ") 2. 在当前窗口打开,不可后退 js: location.replace("新url") 用新的url代替history中当前地址 3. 在新窗口打开,可打开多个 js: open("url","_blank ") 4. 在新窗口打开,只能打开一个 js:open("url","自定义的窗口名") 原理: 其实,每个窗口都有一个唯一的name属性 浏览器规定,同一时刻,同名窗口只能打开一个 后打开的会覆盖先打开的 target属性就是在为新窗口指定name名称 预定义name: _self 自动使用当前窗口自己的name打开新窗口 _blank 不指定窗口名, 每打开一个窗口,浏览器会自动随机生成内部窗口名 关闭窗口: /*window.*/close(); history: 保存当前窗口打开后,成功访问过的url的历史记录栈 不允许修改history内容! 只能三个操作: history.go(n); 前进 history.go(1) 后退 history.go(-1), history.go(-2), 刷新 history.go(0) location: 什么是: 保存当前窗口正在打开的url地址的对象 属性: .href: 获取或设置完整的url地址 .protocol: 协议 .host: 主机名+端口号 .hostname: 主机名 .port: 端口号 .pathname: 相对路径 .search: ?xxx=xxx&xx=xx查询字符串参数 .hash: 锚点地址 鄙视: 将search转化为对象形式: 方法: 1. 在当前窗口打开新连接,可后退: location.assign("url") => location.href="url"=>location="url" 2. 在当前窗口打开,不可后退: location.replace("新url") 3. 刷新: 1、普通刷新: 优先从缓存中获取资源,缓存没有或过期,才去服务器找新的。 f5 history.go(0) location.reload() 2、强制刷新: 无论有没有缓存,都强制从服务器获取新资源! location.reload(true) 4. navigator: 什么是: 封装浏览器配置信息的对象 何时: 只要读取浏览器配置信息时 如何: 1. 判断浏览器是否启用了cookie 什么是: 客户端本地持久存储一个数据小文件 为什么: 程序内存中的所有数据(变量、数组、对象)都是临时存储的 何时: 只要希望在客户端持久保存用户私密数据时 如何判断是否启用cookie: var bool=navigator.cookieEnabled 设置: 设置->高级->隐私->内容设置->查看和禁用cookie 2. 判断是否安装插件: 什么是: 保存当前浏览器安装插件的集合 什么是插件:为浏览器添加新功能的小软件 如何判断插件是否安装: navigator.plugins["插件名"]!==undefined 3. 判断当前浏览器名称和版本号——鄙视 navigator.userAgent: 保存浏览器名称,版本和内核信息的字符串 何时: 只要判断浏览器名称和版本时就用 5. ***定时器: 2种: 1. 周期性定时器: 什么是: 让程序每隔一端时间间隔,自动反复执行一项任务 何时: 只要让程序按照指定的时间间隔反复执行一项任务 ——动画! 如何: 3步: 1. 定义任务函数: 变化一次的函数 function task(){...} 2. 将任务函数放入定时器中反复执行: var timer=setInterval(task,ms) 启动定时器,让定时器每隔ms毫秒,自动反复执行task函数 其中: timer 指当前定时器唯一的序号 专门用于停止定时器之值 3. 停止定时器: clearInterval(timer) 2种: 1. 定时器自动停止: 在任务函数中,设定临界值 一旦达到临界值,就自动调用clearInterval 2. 手动停止定时器 2. 一次性定时器: 一次性: 让程序先等待一段时间,再延迟执行一项任务 执行后,自动停止 何时: 只让程序延迟执行一项任务,且不需要反复执行时 如何: 3步: 1. 任务函数: 2. 启动定时器: timer=setTimeout(任务函数,ms) 其中: ms指延迟的毫秒数 3. 停止定时器: clearTimeout(timer) 在执行前,停止等待,不再执行任务 3.定时器原理: setTimeout 和 setInterval只是将任务函数,保存到定时器中, 必须等到主程序所有语句执行完,才能执行! 鄙视: 定时器中的任务函数,必须等到主程序所有语句执行完,才能执行! 1.var a=10; function fun(){ a++; } setTimeout(fun,0); setInterval //fun无论等待多长时间都必须到最后才执行 console.log(a);//10 2.for(var i=0;i<3;i++){ setTimeout(()=>console.log(i),0); }//3 3 3 6. ***event: ——DOM 什么是: 用户手动触发的或浏览器自动触发的 页面内容状态的改变。 事件处理函数: 当事件发生时,自动调用执行的函数 所有事件处理函数: this->elem 当前触发事件的.前的元素 何时: 今后只要希望触发事件时,自动执行一项任务,就要提前绑定事件处理函数 绑定事件处理函数: 3种: 1. 在HTML中绑定事件处理函数: (组件开发中常用) html: js:function 函数(){...} 问题: 1. 不便于集中管理事件 2. 不便于灵活重用 总之: 不符合内容与行为分离的原则 2. 在js中,用赋值方式绑定: elem.on事件名=function(){ this->elem 当前触发事件的.前的元素 } 问题: 是用=赋值的方式给事件属性赋值的,后赋值的处理函数会覆盖先赋值的 (赋值是替换原函数。每个事件只能绑定一个处理函数) 3. 在js中,为元素添加事件监听对象: elem.addEventListener("事件名",handler) 优: 一个事件,可同时添加多个处理函数 可随时添加和移除 如何移除:移除处理函数时,必须找到原处理函数对象 elem.removeEventListener("事件名",原handler); 强调: 如果一个处理函数,可能被移除, 则绑定时,就必须用有名的函数。不能用匿名函数。 事件模型: 当事件触发后,发生的一系列行为过程——鄙视 DOM标准认为: 点在内层元素上,也等效于点在外层元素上了 3个阶段: 1. 捕获: 由外向内记录各级父元素上绑定的处理函数 捕获阶段只记录处理函数,不执行! 2. 目标触发: 目标元素: 最初实际触发事件的元素 优先触发内层目标元素上的事件处理函数 3. 冒泡: 由内向外,按捕获阶段顺序的反向,依次触发各级父元素上的事件处理函数 事件对象:e 什么是: 事件发生时,自动创建的记录事件信息的并提供修改事件默认行为的API对象 何时: 1、只要获得事件的信息 2、修改事件的默认行为 如何: 创建: 自动创建 获取: 事件对象e默认总是作为处理函数的第一个参数,自动传入。 API: 1、取消冒泡: e.stopPropagation(); 2、利用冒泡: 优化: 尽量减少事件监听的个数 为什么: 浏览器触发事件处理函数,是用遍历方式查找处理函数并执行。 遍历的效率取决于遍历次数。 何时: 只要多个平级子元素,要绑定相同事件时 如何: 只要在父元素上绑定依次处理函数,所有子元素自动共用! 2大难题: 1. 获取目标元素: 错误: this->指向父元素 正确: e.target->始终保存着最初触发事件的目标元素 且不随冒泡而改变! 2.判断e.target是否是想要的: 手段:nodeName, 元素名, class属性, 内容... 阻止事件(默认行为): 什么是: 当事件处理函数过程中,可取消事件的触发 何时: 1、默认行为不是想要的,就要阻止 2、出错时,不想继续执行下去了 如何: e.preventDefault(); 三个典型: css: JS: $(div).dialog(); //仅将div及其内容,变为对话框的样式 总结: 问题: jquery ui采用侵入的方式,自动加载样式和行为 优: 极大的减少了开发人员的代码量 缺: 侵入的class和行为都是写死的,不便于维护 解决: bootstrap vs jquery ui bootstrap不采用侵入的方式,隐式添加任何代码 而是仅提供样式类库 由开发人员,自主的选择应用何种class 自定义插件: 何时: 只要在项目中发现频繁重用的功能,都要封装为自定义插件 如何提取: 前提: 必须使用HTML,css,js实现了插件的完整样式和功能 Step1: 将当前功能的css,提取到一个专门的css文件中 强调: css中尽量少的使用id,元素选择器 尽量一切都用class去实现! Step2: 将当前功能的js行为,提取到一个专门的js文件中 在插件的js文件中查找自定义扩展属性的元素 强调: 将来插件都是通过查找自定义扩展属性来为元素添加行为的 今后,只要出发事件的元素,都要标记自定义扩展属性 如何使用: 1. 按插件要求,编写HTML内容结构 2. 引入插件的css, 在HTML中对应元素上,手动添加class 第三方插件: 1. jquery validate 2. jQuery的Ajax封装: $.ajax({ url:"xxx.php", type:"get|post", data:"参数数据"|{变量:值,...}| $(form).serialize(), //jquery-1.11.3.js //success:data=>{...} })//jquery-3.2.1.js .then(data=>{...}) 补充: jquery表单操作: $(form).serialize()