DOM JQuery

====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<children.length;i++)

解决: 遍历时,提前缓存length

正确: for(var i=0,len=children.length;i<len;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中定义:<ANY data-属性名="值"...

  说明: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");

<a></a>

2. 设置关键属性:

a.href="http://tmooc.cn";

a.innerHTML="go to tmooc";

<a href=" http://tmooc.cn "> go to tmooc </a>

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. 在当前窗口打开,可后退

HTML: <a href="url" target="_self">

js: open("url","_self ")

2. 在当前窗口打开,不可后退

js: location.replace("新url")

用新的url代替history中当前地址

3. 在新窗口打开,可打开多个

HTML: <a href="url" target="_blank">

js: open("url","_blank ")

4. 在新窗口打开,只能打开一个

HTML: <a href="url" target="自定义的窗口名">

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:<ANY  ... on事件名="js语句/js函数调用" ...>

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

三个典型:

1. <a href="#xxx"

默认: 跳到锚点,在url结尾增加#xxx

2. 阻止表单自动提交!

自定义表单提交: 2种:

1. <input type=button  js: form.submit()

2. <input type=submit

=>form.onsubmit(): e.preventDefault()

3. HTML5中做拖拽效果时

必须阻止浏览器默认的拖拽行为

=DAY04==============================================================

正课:

1. 定时器:

一次性:

2. *navigator:

3. ***event

1. 定时器:

一次性: 让程序先等待一段时间,再延迟执行一项任务

执行后,自动停止

何时: 只要让程序延迟执行一项任务,且不需要反复执行时

如何: 3步:

1. 任务函数:

2. 启动定时器: timer=setTimeout(任务函数,ms)

其中: ms指延迟的毫秒数

3. 停止定时器: clearTimeout(timer)

在执行前,停止等待,不再执行任务

鄙视: 定时器中的任务函数,必须等到主程序所有语句执行完,才能执行!

var a=10;

function fun(){

a++;

}

setTimeout(fun,0);

//fun无论等待多长时间都必须到最后才执行

console.log(a);//10

2. navigator:

什么是: 封装浏览器配置信息的对象

何时: 只要读取浏览器配置信息时

如何:

1. 判断是否启用cookie

什么是: 客户端持久存储用户私密信息的小文件

为什么: 内存中的数据都是临时的

何时: 只要在客户端持久保存数据时

如何判断是否启用cookie:

var bool=navigator.cookieEnabled

设置: 设置->高级->隐私->内容设置->查看和禁用cookie

2. 判断是否安装插件:

什么是: 为浏览器添加新功能的小软件

如何判断插件是否安装:

navigator.plugins["插件名"]!==undefined

3. 判断当前浏览器名称和版本号——鄙视

navigator.userAgent: 保存浏览器名称,版本,内核信息的字符串

何时: 只要判断浏览器名称和版本时

3. ***event: ——DOM

什么是: 用户手动触发的,或浏览器自动触发的页面状态的改变。

事件处理函数: 当事件发生时,自动执行的函数

绑定事件处理函数: 3种:

1. 在HTML中绑定事件处理函数:

<ANY  ... on事件名="js语句" ...>

问题: 1. 不便于集中管理事件

2. 不便于灵活重用

总之: 不符合内容与行为分离的原则

2. 在js中,用赋值方式绑定:

ANY.on事件名=function(){

this->ANY 当前触发事件的.前的元素

}

问题: 赋值是替换原函数。每个事件只能绑定一个处理函数

3. 在js中,为元素添加事件监听对象:

ANY.addEventListener("事件名",handler)

优: 一个事件,可同时添加多个处理函数

可随时添加和移除

如何移除:

ANY.removeEventListener("事件名",原handler);

问题: 如果一个处理函数,可能被移除,则绑定时,就必须用有名的函数。不能用匿名函数。

事件模型: 当事件发生时,浏览器触发事件的过程——鄙视

DOM标准认为: 点在内层元素上,也等效于点在外层元素上了

3个阶段:

1. 捕获: 由外向内记录各级父元素绑定的处理函数

捕获阶段只记录处理函数,不执行

2. 目标触发:

目标元素: 最初实际触发事件的元素

优先触发目标元素上的处理函数

3. 冒泡: 由内向外,按捕获阶段顺序的反向,依次触发父元素上的处理函数

事件对象:e

什么是: 事件发生时,自动创建的记录事件信息的对象

何时: 只要获得事件的信息,或修改事件的默认行为

如何:

创建: 自动创建

获取: 事件对象e总是作为处理函数的第一个参数,自动传入。

API:

取消冒泡: e.stopPropagation();

利用冒泡:

优化: 减少事件监听的个数

为什么: 浏览器触发事件处理函数,是用遍历方式找打处理函数并执行。

何时: 只要多个平级子元素,要绑定相同事件时

如何: 只要在父元素上绑定依次处理函数,所有子元素自动共用!

2大难题:

1. 获取目标元素:

错误: this->父元素

正确: e.target->记录了最初触发事件的元素

且不随冒泡而改变!

2. 鉴别e.target是否是想要的:

元素名,  class属性

阻止默认行为:

何时: 只要一个元素的事件中,带有默认行为,且默认行为不是想要的,就要阻止

如何: e.preventDefault();

三个典型:

1. <a href="#xxx"

默认: 跳到锚点,在url结尾增加#xxx

2. 阻止表单自动提交!

自定义表单提交: 2种:

1. <input type=button  js: form.submit()

2. <input type=submit

=>form.onsubmit(): e.preventDefault()

3. HTML5中做拖拽效果时

必须阻止浏览器默认的拖拽行为

=================================================================

正课:

1. ***event

鼠标坐标

页面滚动

项目:

详情页: 放大镜效果

首页楼层滚动:

1. ***event

  鼠标坐标: 3对儿:

  1、相对于屏幕左上角:

e.screenX, e.screenY;

  2、相对于文档显示区左上角:

e.clientX, e.clientY

  3、相对于当前元素左上角:

e.offsetX, e.offsetY

如何选择: 和主角的活动范围保持一致!

  页面滚动:

  事件: window.onscroll

  属性:滚动距离: scrollTop=

1、document.body.scrollTop

2、document.documentElement.scrollTop

  自定义控制滚动:

  1、写死了的位置

window.scrollTo(x方向的位置(通常为0),y方向的位置)

2、滴加

window.scrollBy(x方向的位置(通常为0),y方向的位置)

====JQUERY===========================================================================

==============================

正课:

1. 修改:

用class批量修改样式

2. 添加,删除,替换,克隆

3. 事件绑定

1. 修改:

用class批量修改样式:

1. 为元素追加一个class:  $(...).addClass("class名")

2. 为元素移除一个class:  $(...).removeClass("class名")

3. 判断是否包含一个class: $(...).hasClass("class名")

4. 为元素切换一个class: $(...).toggleClass("class名")

if($(...).hasClass("class名"))

$(...).removeClass("class名")

else

$(...).addClass("class名")

补: .index()

2种:

1. var i=$("selector").index(jq对象/DOM对象)

查找右边的jq对象或DOM对象,在左边的结果集合中的下标位置

2. 如果在同一个父元素下找某个子元素的位置

var i=$("child").index();

2. 添加,删除,替换,克隆:

添加: 2步:

1. 用$()创建一个新元素: var $新元素=$("html片段")

2. 将新元素添加到dom树:

$("parent").append($新元素)

.prepend($新元素)

$("child").before($新元素)

.after($新元素)

可以更简化: $("parent").append/prepend("html片段")

$("child").before/after("HTML片段")

删除: $(...).remove();

补: .is("selector") 判断当前元素是否符合selector的条件

替换: $("selector").replaceWith(jq对象|DOM对象)

克隆: var $clone_elem=$(...).clone();

强调: 默认浅克隆: 仅克隆样式和属性, 不可隆行为

深克隆: 即克隆样式和属性,又克隆行为

$(...).clone(true)

3. 事件绑定:

鄙视: jQuery中共有几种事件绑定方式,区别:

DOM: .addEventListener("事件名",handler)

.removeEventListener(...)

jq:

1. $("target").bind/unbind("事件名",handler)

同addEventListener()

  .unbind三种重载:

.unbind("事件名",handler) 移除当前元素上,指定事件上的名为handler的处理函数。

.unbind("事件名") 移除当前元素上,指定事件上的所有处理函数

.unbind() 移除当前元素上,所有事件的监听

2. $("target").one("事件名",handle) 同bind

区别: 只触发一次,触发后,自动解绑

3. .live/die("事件名",handle)——已废弃

原理: 将所有事件集中绑定在顶级document上

4. $("parent").delegate("selector","事件名",handler)

原理: 简化利用冒泡:

1. 获得目标元素: this->e.target

2. 筛选目标元素: 第一个参数: "selector"

只有满足"selector"要求的元素,才能触发事件

鄙视: .bind vs .delegate: 3点

1. .bind直接帮在目标元素上

.delegate 帮在父元素上

2. 监听个数: .bind 监听个数多——每个目标元素都添加

.delegate 监听个数少——只给父元素添加一个

3. 新增子元素自动获得事件处理函数:

.bind 只能对现有元素添加事件监听

新增元素无法自动获得监听

.delegate 只要父元素下的元素,无论现有,还是新增,都能自动获得父元素上统一的事件监听

5. .on/off:

1. 代替bind: .on("事件名",handler) 同bind

2. 代替delegate: .on("事件名","selector",handler)同delegate

6. .事件名:

强调: 仅对常用的事件提供了终极简化

  js 和css并行执行

  transition没显示

  放到window.onload=function(){页面内容加载完成后在执行的}中

4、页面加载后执行: 2种:

1. DOMContentLoaded: DOM内容加载完,就可提前执行

DOM内容仅包括: html和js

提前触发

何时: 只要不依赖于css和图片的所有操作都可在DOM内容加载后,提前绑定触发

比如: 事件绑定

jq: $(document).ready(()=>{//就等于DOMContentLoaded

  //DOM内容加载后,就可提前执行的操作

//比如: 事件绑定

})

简化: $().ready(()=>{

更简化: $(()=>{...})

其实: 写在body结尾的script中的代码默认就是DOM内容加载后自动执行

2. window.onload 在所有页面内容加载完成后自动触发

window.onload=function(){...}

问题:使用赋值的方式,多个的话,后面的会覆盖前面的

    $(window).load(function(){})

包括: html,css,js,图片

何时: 如果必须依赖css或图片的操作

鄙视:jQuery中$的原理:

查找,创建新元素,封装(DOM封装为jQuery对象那个),DOM加载后自动执行

是jQuery类型的工厂函数,用于创建jQuery类型的子对象

有四种重载:

  1、选择器作为参数:现有选择器查找DOM元素,在封装进jQuery中

jQuery对象其实就是一个类数组对象

为了加快(speed up)查找速度,

  如果只给 #id 则自动优先调用 getElementById

        tag                            TagName

.class                        Classname

其余复杂选择器都调用querySelectorAll

2、DOM对象作为参数:将DOM元素封装为jQuery对象

DOM不是jQuery类型的子对象,无法直接使用jQuery简化版本的API

需要先封装,在使用

3、html代码片段作为参数:创建新的DOM元素对象

4、回调函数作为参数:自动在DOM内容加载后,提前出该函数,

是一种事件绑定

鼠标事件:

1、mouseover  mouseout

进出子元素,会频繁触发父元素的处理函数

2、mouseenter  mouseleave

进出子元素,不再频繁触发父元素的处理函数------效率高

简写: 如果同时绑定鼠标进入和移出事件时,可简写为hover

$().hover(

  fun(){...}, //给mouseenter进,不触发父元素

fun(){...}  //给mouseleave出,不触发父元素

)

更简化:

如果两个处理函数,可用toggle统一为一个处理函数

    则只需要传一个参数即可

模拟触发:

虽然没有触发事件,但是依然可用程序模拟执行元素的事件处理函数

如何:  $(...).trigger("事件名")

其实可以更简单: $(...).事件名()

      4_trigger.html

总结: jQuery简化了DOM五大操作:

查找, 修改, 添加, 删除, 事件绑定

=====================================================

正课:

1. 动画:

2. 类数组对象:

3. 插件:

css不是CPU解析的,是GPU(显卡)解析的,GPU做绘图效率更高

1. 动画:

简单动画: 3种固定动画效果

1. 显示隐藏:  .show()  .hide()  .toggle()  记这个吧

默认:

不带参数,默认用display实现瞬间显示隐藏,不支持动画效果

带时间(ms)参数, 才有动画效果

总结: 通常都是用不带参数的方法,代替display,简化代码

2. 上滑下滑: .slideUp(ms)  .slideDown(ms)  .slideToggle(ms)

3. 淡入淡出: .fadeIn(ms)    .fadeOut(ms)    .fadeToggle(ms);

问题:

    1. 用js定时器实现动画效果,效率不如css的transition

2. 效果是在jQuery库中写死的,不便于维护

万能动画: 可自定义动画要修改的css属性

$(...).animate({

css属性: 目标值,

css属性: 目标值,

... : ...

},ms,callback)

问题:

  仅支持单个数值的css属性值

不支持颜色 不支持C3变换

    优点:

可随意停止


$(...).动画API(ms,callback)

jQuery动画API的最后一个参数是一个回调函数:

在动画播放后自动执行。callback专门用于在动画播放后执行善后处理。

回调函数中: this->正在播放动画的DOM元素

强调: 想用this,就不能用箭头函数

并发和排队:

排队: 对同一个元素调用的多个动画API,是排队执行。

原理: 动画API并不是启动动画。仅是将动画加入一个队列中顺序执行。

并发: 在一个animate函数内,修改的多个css属性,是同时变化。

2. 类数组对象操作: 4个:

1. [i] => .get(i) 返回的是DOM元素

2. .length => .size()

3. .forEach =>

.each(function(i当前位置,elem当前DOM元素){  this->当前DOM元素 })

鄙视:$.each(obj,fun)  vs  $(...).each(fun)

  1.存储位置:

  $.each() 直接存储在构造函数上

$(...).each 存储在jQuery类型的原型对象中

2.调用方法

  $.each() obj可以是任何类数组对象

$(...).each 必须是jQuery对象

4. .indexOf => $(全部).index($要查找的元素)

如果在同一个父元素内查找直接子元素 $(要查找的元素).index()

3.定义jQuery全局函数

  jQuery.fn.自定义函数=function(){

  // this ->将来调用该函数的jquery类型子对象

        //不用$封装  已经是jQuery对象了

}

插件: 基于jQuery开发的独立的小功能,效果

为什么: 复用功能和效果,极大节约代码量

何时: 只要发现多个项目中,都用到相同的功能和效果

如何: 3种:

1. jQuery UI: jQuery官方推出的插件

官网: jqueryui.com

快速入门: jqueryui.com

手册: 官网

如何使用:

下载js和css以及图片:

强调: jquery-ui.css 必须和images文件夹同目录

引入:

jquery-ui.css

jquery-3.2.1.js

jquery-ui.js

自定义脚本

包括: 交互, 效果, 部件

2. 第三方插件:

3. *****自定义插件:

=======================

正课:

1. 插件:

官方插件: jQuery UI

***封装自定义插件:

第三方插件:

2. jQuery的Ajax封装

*****跨域请求

什么是插件:具有完整样式和功能的小功能(函数)

为什么:开发中,很多效果/功能都是重复使用的

何时:只要项目中有反复使用的功能/样式,都要先封装为组件,再反复使用组件

包括:3中

  1.官方 2.第三方 3.自定义

1. 插件:

官方插件: jQuery UI

基于jQuery API实现的可复用的小功能库

如何:

  下载

包括:文件夹images jquery-ui.css  jquery-ui.js

  其中,images要和jquery-ui.css同一目录下

.autocomplete();

HTML: <input ... />

JS: $(文本框).autocomplete({

source:客户端数组|"远程.php路径"

})

PHP: 2种:

1. 返回只有一个键的关联数组, 只要键名为label,客户端autocomplete可自动识别

2. 返回包含多个键的关联数组: 2步:

1. 再次调用:

.autocomplete("instance")._renderItem=function($ul,item){

//$ul: 自动获得当前下拉列表的ul元素

//item: 获得结果结合中,当前正在加载的元素对象

//操作: 3件事:

//1. 创建一个li>div:

//从item中获取键的值,拼接到div中

//2. 将新的li追加到$ul中

//3. 返回新的li的对象

}

2. 在autocomplete中定义select处理函数:

在单击每个列表项时自动触发:

select:function(e,obj){

//obj.item是当前li对应的原集合中的对象

//常用操作: 2个

//1. 将item中的主要内容,显示在文本框中

//2. 用当前选中项作为参数,跳转到新的url

return false;//必须

}

2. 日期选择: datepicker

HTML: <input />

CSS:

JS: $(文本框).datepicker()

3. 对话款: 表单提交

HTML:

<div id="xxx" title="标题"

<form>

...

//不用写提交和重置按钮

</form>

</div>

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

  $(form).validate({

rule:{

  name1:"规则1",

name2:{

  规则1:值1,

规则1:值2,

}

},

messages:{

  消息

}

})

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()

=====================================

正课:

1. 插件:

官方插件: jQuery UI

***封装自定义插件:

第三方插件:

2. jQuery的Ajax封装

*****跨域请求

1. 插件:

官方插件: jQuery UI

.autocomplete();

  HTML: <input ... />

  JS: $(文本框).autocomplete({

      source:客户端数组|"远程.php路径"

    })

  PHP: 2种:

  1. 返回只有一个键的关联数组, 只要键名为label,客户端autocomplete可自动识别

  2. 返回包含多个键的关联数组: 2步:

    1. 再次调用:

      .autocomplete("instance")._renderItem=function($ul,item){

        //$ul: 自动获得当前下拉列表的ul元素

        //item: 获得结果结合中,当前正在加载的元素对象

        //操作: 3件事:

          //1. 创建一个li>div:

            //从item中获取键的值,拼接到div中

          //2. 将新的li追加到$ul中

          //3. 返回新的li的对象

      }

    2. 在autocomplete中定义select处理函数:

      在单击每个列表项时自动触发:

      select:function(e,obj){

        //obj.item是当前li对应的原集合中的对象

        //常用操作: 2个

          //1. 将item中的主要内容,显示在文本框中

          //2. 用当前选中项作为参数,跳转到新的url

        return false;//必须

      }

  2. 日期选择: datepicker

  HTML: <input />

  CSS:

  JS: $(文本框).datepicker()

  3. 对话款: 表单提交

  HTML:

    <div id="xxx" title="标题"

      <form>

        ...

        //不用写提交和重置按钮

      </form>

    </div>

  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()

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 第一章 1.什么是DOM DOM: Document Object Model(文档对象模型) 是JavaScri...
    fastwe阅读 790评论 0 0
  • (续jQuery基础(1)) 第5章 DOM节点的复制与替换 (1)DOM拷贝clone() 克隆节点是DOM的常...
    凛0_0阅读 1,317评论 0 8
  • jQuery模块 选择器、DOM操作、事件、AJAX与动画 匿名函数自执行 作用:解决命名空间与变量污染的问题 总...
    青青玉立阅读 872评论 0 0
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,095评论 0 3
  • 1.JQuery 基础 改变web开发人员创造搞交互性界面的方式。设计者无需花费时间纠缠JS复杂的高级特性。 1....
    LaBaby_阅读 1,162评论 0 1