文档树 DOM:Document Object Model
即:用对象的形式表示HTML、CSS。
DOM包含:
- DOM Core
- DOM HTML
- DOM Style
- DOM Event
1.节点遍历:ヾ(o◕∀◕)ノ
node.parentNode
.childNodes //得到node的全部子节点,包括各种类型
.firstChild
.lastChild
.previousSibling
.nextSibling //下一个兄弟节点
2.元素类型节点
遍历:
p.parentElement //父元素节点
.children //返回指定节点的所有element子节点的活HTMLCollection,可以children[0].nodeName获取
.firstElementChild
.lastElementChild
.previousElementSibling
.nextElementSibling
节点操作
1.获取节点
getElementById
//返回live(实时变化)的动态集合
getElementsByClassName("classA")
//获取同时有两个类名的元素节点
getElementsByClassName("classA classB")
//返回live(实时变化)的动态集合
getElementsByTagName()
//特别注意:querySelectorAll是non-live(非实时变化)的。 ヾ(o◕∀◕)ノ
querySelector("#users") 获取第一个符合条件的元素
querySelector("input[type='text']") 可以根据属性进行选择,很方便
querySelectorAll(".user") 获取所有匹配的元素
querySelectorAll("#users .user")
注意:以上选择器(getElementById
除外),除了可以在整个文档中寻找,比如: document.getElementsByClassName('className')
,还可以在某个节点下寻找,比如:element.getElementsByClassName('className')
。
2.增加节点
创建节点 ヾ(o◕∀◕)ノ
//创建指定标签名称的节点
element = document.createElement(tagName)
var li = document.createElement("li");
var a = document.createElement("a");
设置节点内容
//获取节点及其后代节点的文本内容或为节点添加内容
element.textContent
element.innerText
element.textContent = "newValue";
element.innerText = "newValue";
插入节点到文档中 ヾ(o◕∀◕)ノ
//插入节点
//appendChild会添加到parentElement结束标签之前,也就是变成parentElement元素的最后一个子元素
parentElement.appendChild(childElement);
//insertBefore会添加newElement到parentElement下referenceElement元素前面
parentElement.insertBefore(newElement, referenceElement)
3.移动&克隆节点 ヾ(o◕∀◕)ノ
如果想把一个节点从原来的位置移动到指定位置。那么只需要:
const myElementClone = document.getElementById("myElement");
document.getElementById("new-position").appendChild(myElementClone);
但如果不想移动原来节点的位置,而是想克隆一个新的节点出来,那么需要用到cloneNode(true)
const myElementClone = document.getElementById("myElement").cloneNode(true);
document.getElementById("new-position").appendChild(myElementClone);
4.删除节点 ヾ(o◕∀◕)ノ
parentElement.removeChild(child);
通常不用专门获取parentElement,直接写child.parentNode即可
5.可同时用于添加节点,设置节点内容,插入节点,删除节点
//获取节点内部的所有HTML结构代码,或为节点添加内部的html代码
element.innerHTML
element.innerHTML = "<a href="#">hahaha</a>"
//可能有内存泄漏和安全问题,因此仅建议用于新建节点,并尽量不用于用户填的内容
属性操作
1.HTML attribute -> DOM property
input元素
- id - id
- type - type
- class - className
label元素
- for - htmlFor
2.property accessor 属性访问器
两种访问方式:
input.className;
input["id"] = 'cute'
属性访问器的通用性
和拓展性
不好。
3.getAttribute/setAttribute
element.getAttribute(attritubeName)
eg: input.getAttribute("class");
element.setAttribute(name, value)
eg: input.setAttribute("id", "unique")
//会将id
设置为unique
特例:disabled
属性
//以下三种都会将disabled设置为生效input.setAttribute("disabled", true)
input.setAttribute("disabled", "")
input.setAttribute("disabled", false)
因为setAttribute
只是字符串的操作,所以想要移除disabled
属性只能input.removeAttribute("disabled");
缺点:仅仅是字符串的操作。
优点:通用性好,直接把HTML属性名传进去就行了。
4.自定义属性: dataset
HTMLElement.dataset
:dataset
是HTML元素上的一个属性,是data-*
属性的一个集合,主要的用途是在元素上保存数据。一般用来做自定义的数据属性。
<div id="users" data-id="123456" data-account-name="darcy">Darcy
</div>
<p id="info"></p>
div.dataset.
- id: 123456
- accountName: darcy
//在JS中可以这样获取:
var data = div.dataset;
//然后这样用
var dataId = data.id;
document.getElementById("info").innerText = data.accountName;
5.修改class列表: classList
element.classList.add("classA"); // 为元素添加一个class
element.classList.remove("classA"); // 删除元素上名为classA的类
element.classList.toggle("classA");
样式操作(通过JS动态修改样式)
style, style.cssText, class, styleSheet, window.getComputedStyle
1. element.style.cssProperty
<div id="users" style="color:red;">
Darcy
</div>
var div = document.getElementById("users");
console.log(div.style.color); // red
2.更新样式
element.style.cssProperty
element.style.borderColor = "red";
element.style.color = "red";
缺点:更新每一个属性都需要单独的一条语句。
改进:用element.style.cssText = "border-colot: red; color: red;"
缺点:样式混在逻辑中。
再次改进:更新class(推荐方法)
.invalid {
border-color: red;
color: red;
}
element.className += " invalid"
存在的问题:一次更新很多元素的样式时会很麻烦。
改进(一次更新很多元素的样式):更换样式表
//html
<link id="skin" rel="stylesheet" href="skin.spring.css">
//js
document.getElementById("skin").href = "skin.summer.css";
3.获取样式
element.style.cssProperty
只能获取到写在HTML元素上的样式,若写在<style>标签中,或外联的css文件中就无法获取到了。
window.getComputedStyle("element")
(推荐 ヾ(o◕∀◕)ノ)
//html
<div id="users">Darcy</div>
//css
#users{ color: blue; }
//js
var color = window.getComputedStyle("element").color;
color;// rgb(0, 0, 255);
事件
1.事件流
DOM事件流就是DOM事件处理执行的过程。分为三个过程:
- capture phase (捕获过程:从DOM树最顶端也就是window元素往下捕获,直到触发事件的元素的父元素为止)
- target phase(事件触发过程:在触发事件的节点上进行)
- bubble phase(冒泡过程:从触发事件的节点的父节点开始,冒泡到顶层的window对象)
点击<a>
标签:
如何捕捉一个事件,我们讲DOM事件最终是为了编程,那么如何去捕获一个事件,如何去处理一个事件呢?
2.事件注册
事件注册,取消与触发的主体
都是事件对象的DOM元素
。
1.事件注册
eventTarget.addEventListener(type, listener[, useCapture])
type
: 事件类型listener
:事件处理函数useCapture
:是否在捕获阶段触发。默认是false,DOM事件处理的是冒泡过程,只有设置这个值为true,才会处理捕获过程。
var elem = document.getElementById("div1");
//定义一个事件处理函数,即当事件被触发时希望做的事情
var clickHandler = functin(event){
//TODO
}
//注册事件
elem.addEventListener('click', clickHandler, false);
2.取消事件注册
eventTarget.removeEventListener(type, listener[, useCapture]);
3.事件触发
- 点击元素
- 按下按键
- 用代码触发:
eventTarget.dispatchEvent(type);
- eg.用代码触发一个click事件:
elem.dispatchEvent('click')
4.事件对象
当事件被触发时,会调用事件处理函数
,在调用的时候会传入一些信息,这些信息代表了当前事件的一些状态,这就是事件对象
。调用事件处理函数
的时候,引擎会传入一个对象给我们,就是事件对象
。我们在编程的时候,会用到这个事件对象的一些属性和方法。
var elem = document.getElementById("div1");
var clickHandler = functin(event){ //这个event就是事件对象
//TODO
}
elem.addEventListener('click', clickHandler, false);
第二行的event
就是事件对象。当我们用鼠标点击的时候,这个event对象可能包含了鼠标的位置,x,y坐标等等。
- 事件对象的属性:
-
type
,事件类型,比如click -
target
,事件触发的节点,比如点击一个<a>
元素,target就是<a>
元素 -
currentTarget
,是我们当前处理事件节点的元素。适用于这样一种情况:当我们需要注册一个click事件的时候,我们不一定需要将事件注册在target上,也可以注册在target的父节点上。因为当事件冒泡到target的父节点的时候,仍然可以处理这个事件。所以,如果我们把事件注册在target的父节点上,仍然点击target元素,event.target任然是target元素,那么currentTarget就是它的父节点这个元素。只有当事件处于目标阶段(target phase)被触发的时候,currentTarget才与target的值一样。
- 事件对象的方法:
-
stopPropagation
阻止冒泡,有时候我们不想事件继续冒泡上去了。 -
preventDefault
阻止默认行为 -
stopImmediatePropagation
阻止冒泡
调用方法:
-
event.stopPropagation();
阻止事件传递到父节点。 -
event.stopImmediatePropagation();
除了阻止事件传递到父节点,还阻止了当前节点的后续事件。 -
event.preventDefault()
阻止默认行为
3.事件类型
1.Event
应用:<img>
标签中,当图片找不到时,显示默认图片onerror
事件
<img alt="photo" src="http://www.xxx.com/a.jpg" onerror="this.src='http://www.xxx.com/default.jpg'"/>
2.UIEvent
resize
改变页面或窗体大小时触发的事件scroll
滚动事件
3.MouseEvent
事件类型:
注意:mouseout
与mouseleave
的区别就是mouseleave
不冒泡mouseover
与mouseenter
的区别也是mouseenter
不冒泡
MouseEvent
事件属性:
- clientX(触发事件的点到页面最左端的距离), clientY(触发事件的点到页面最上方的距离)
- screenX(触发事件的点到屏幕最左端的距离), screenY(触发事件的点到屏幕最上方的距离)
MouseEvent
事件顺序:
比如:
1.从元素A上方移过时
mousemove -> mouseover(A) -> mouseenter(A) -> mousemove(A)[很多个] -> mouseout(A) -> mouseleave(A)
2.点击元素
mousedown -> [mousemove] -> mouseup -> click
例子:拖拽div
需求:按下鼠标并移动时开始拖拽div,松开鼠标时停止拖拽div。mousedown -> mousemove -> mouseup
4.FocusEvent
事件类型:
blur
元素失去焦点时focus
元素获得焦点时focusin
元素即将获得焦点时focusout
元素将要失去焦点时
属性:
relatedTarget
作用:当一个元素失去焦点时,另外一个元素就要获得焦点。在blur
失去焦点事件中,获得焦点的元素就是这个relatedTarget
;在focus
获得焦点事件中,失去焦点的元素就是这个relatedTarget
。
5.InputEvent
6.KeyBoardEvent
键盘事件
属性
-
key
按下了什么键 -
code
按下了什么键
4.事件代理
需求:一个列表,点击哪一行,这一行背景就变灰色,其余全是白色。
问题变为:想要给一个ul
下的多个li
注册同一个click
事件(让背景变灰),如果li
个数不多还好,如果li
很多,那么为每个li
都注册一遍显然太浪费了,那么怎样解决呢?
回忆我们的事件流中的事件冒泡
,可以发现触发事件的元素
触发了事件后,它会往上去冒泡,一直冒泡到window对象。而事件是在冒泡阶段被触发的,所以它的所有父元素也是可以接收到这个click
事件被触发了的。所以如果我们把事件注册到 触发事件的元素 的父元素上,它也是会执行的。因此,我们可以把事件注册在元素的父节点上来实现,可以是最近的父节点,也可以是再上层的父节点。这里我们通过在ul
上注册来实现这一功能,然后通过事件处理函数中的event.target
可以拿到当前触发的li
元素。
//html
<ul id="myUl">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
//js
var myUl = document.getElementById("myUl");
myUl.addEventListener("click", function(event){
var target = event.target;//当前点击的li,就是事件触发的节点
//TODO
});
优点:
1.要管理的handle更少
2.分配的内存更少
3.当增加或删除子节点时不用处理事件,上例中的li
缺点:
1.注册到父元素上的事件的回调函数的逻辑更复杂
用的比较多的地方是列表eg. ul>li*4
数据通信
1.http协议
常用http方法
URL构成
常见HTTP状态码
2.ajax请求
1.打开浏览器
var xhr = new XMLHttpRequest();
2.在地址栏输入地址
xhr.open('get', '1.txt', true);
2.1[可选]设置请求头
xhr.setRequestHeader(header, value);
eg:xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded" );
3.提交,发送请求
xhr.send([data = null]);
data
可以是string
类型,也可以是form-data
类型
4.等待服务器返回内容
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
console.log(xhr.responseText);
} else {
console.log('Request was unsuccessful:' + xhr.status)
}
}
}
还可以监听xhr.onload
事件,这个事件是当xhr.readyState = 4
并且xhr.status = 200
才会触发的。
同源策略
不满足同源策略的访问,就叫跨域资源访问
,遵循W3C定义的CORS
标准。
流程图:
数据存储
cookie
document.cookie
:是一段小型的文本文件。大小在4kb左右。由键值对(name-value)构成,键值对中间由;
和空格
隔开。
eg:
"SRCHD=AF=NOFORM; SRCHUSR=DOB=20160521; SRCHUID=V=2&GUID=48081778AFB747C3925DAF775B98D983; MUID=29B9478F16FC621D1C93409312FC61CF; ipv6=hit=1; _FP=hta=on; SRCHHPGUSR=CW=1349&CH=638&DPR=1.25&UTC=480; WLS=TS=63629214318; _SS=SID=18C4028A873563381E5C08FF86946247&HV=1493617535&bIm=475643"
虽然cookie
是存储在浏览器端,但大部分时候cookie
是在服务器端进行设置。服务端通过在HTTP返回的Response Headers里面通过设置set-cookie
这个字段让浏览器知道需要存储的cookie
。
属性:
除了Name
和Value
之外,还有Domain
Path
浏览器会话时间:Expires
(时间戳)/Max-Age
(最长时间)
对cookie
进行增删改查:
document.cookie
属性看起来像是一个普通的文本字符串,其实它不是。
所以,很神奇的一点是:添加新的cookie只需要document.cookie = "newKey=newValue";
就可以了,不需要document.cookie += ...
,之前的cookie
不会被覆盖。
还有一点是:即使在 document.cookie
中写入一个完整的 cookie
字符串, 当重新读取该 cookie
信息时,cookie
信息是以名/值对的形式展示的。
删除cookie
:将它的max-age
属性设置为0
就可以删除了。
缺陷:流量代价,安全性问题,大小限制。
storage [localStorage & sessionStorage]
localStorage有效期:永久
sessionStorage有效期:浏览器会话期间,不同窗口见不共享sessionStorage
大小在5MB左右。
localStorage.name
delete localStorage.name
使用API
//读取值
localStorage.getItem("name"); //传入name来访问对应的值
localStorage.key(i); //传入索引值来访问对应的值
//设置值
localStorage.setItem("name", "realValue");
//删除值
localStorage.removeItem("name");
//清空所有的数据
localStorage.clear();
动画
三要素:对象,属性,定时器
setInterval, setTimeout, requestAnimationFrame()
var intervalId = setInterval(func, delay); //delay 为触发的间隔
clearInterval(intervalId);
var intervalId2 = setTimeout(func, delay); //delay默认是0,如果不写就立即触发,是一个异步的过程
clearTimeout(intervalId2);
区别在于:setTimeout
只执行一次,setInterval
会重复执行。
eg1:var intervalId = setInterval(function(){ //关于func的写法,我是一颗常见的栗子 }, delay);
eg2:var funcA = function(){//我是第二颗常见栗子 };
var intervalId = setInterval(funcA, delay);
var requestId = requestAnimationFrame(func);
cancelAnimationFrame(requestId);
间隔时间不由用户控制,由显示器的刷新频率控制,大概1s刷新60次。每次浏览器刷新会触发这个操作,不需要用户关心间隔时间,且动画更流畅,不会出现掉帧的情况。
常见动画:
形变
位移
旋转
透明度
复杂动画也都是由简单动画构成的。
音频与视频
<audio src=""></audio>
<video src="" width=320 height=240></video>
控制播放的方法和属性:
查询媒体状态:
canvas
不建议用css指定宽高,直接在<canvas>
标签中指定宽和高。
<canvas id="tutorial" width=320 height=240><</canvas>
怎样获取canvas的渲染上下文对象?
通过getContext("2d")
方法:
var canvas = document.getElementById("tutorial");
var context = canvas.getContext("2d");
基本绘图步骤:
地球绕着太阳转实例代码:
BOM
BOM是代表浏览器窗口对象的一组API
Screen,navigator,location,history对话框,窗体互操作,load,beforeunload,scroll,resize等事件
1.属性:
navigator
window.navigator.platform // "Win32"
window.navigator.userAgent // 包含浏览器内核信息 "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36"
可以通过userAgent
判断当前运行在什么设备上。
location
protocol + host(hostname + port) + pathname + search + hash
window.location.href
表示当前页面完整路径,也可以通过修改这个属性让浏览器跳转。
方法:
window.location.assign(url)
跳转页面,记录历史。window.location.replace(url)
跳转页面,不记录历史。window.location.reload()
重载当前页面。
history
属性:length
方法:
back(n)
//传入正整数,后退n步forward(n)
//传入负整数,前进n步go(n)
//传入整数,正数前进,负数倒退
screen
属性:
availHeight
availWidth
加了avail的是可用的屏幕属性,没加的是显示器的属性。
2.方法:
3.事件
注意beforeunload
事件,询问了用户是否决定离开。
表单操作
示例:
服务器接收的信息:
请求的url:https://www.xxx.com/order
格式:application/x-www-form-urlencoded
参数:custname,size,...
<form method="post" action="https://www.xxx.com/order" enctype="application/x-www-form-urlencoded">
<label>姓名:<input name="custname"></label>
<div>披萨大小:
<label>小<input type="radio" name="size" value="small"></label>
<label>中<input type="radio" name="size" value="medium"></label>
<label>大<input type="radio" name="size" value="big"></label>
</div>
...
</form>
注意添加required
属性:<input required>
,可以让在submit
时就检测出没有填写的表单并给用户以提示。
1.表单元素
1.1form
的属性
name
属性用处是可以直接拿到form节点元素:var pizzaForm = document.forms.pizza;
1.2form
的接口
reset()
可重置元素:input
,keygen
,output
,select
,textarea
触发表单reset事件,阻止该事件的默认行为可取消重置。
可以用于删除文件选择器<input type="file">
中已选择的文件哟 ʅ(´◔౪◔)ʃ
1.3select
的属性和方法
方法:
add(element[, before])
remove([index])
创建选项:
- 示例:
document.createElement('option');
等于new Option();
- 语法:
new Option(text[, value[, defaultSelected[, selected]]])
添加选项:
- 示例:
element.insertAdjacentElement("beforeBegin", option)
select.add(option, before)
- 语法:
selectObject.add(optionElement,beforeElement)
,element.insertAdjacentElement(position, element);
删除选项:
- 示例:
opt12.parentNode.removeChild(opt12)
等于select.remove(2)
- 语法:
select.remove(index)
select
级联下拉选择器
知识点:
- onchange
- remove
- add
主要代码:
//向列表中填充数据
function fillSelect(select, list){
//先倒序遍历清空列表
for(var i=select.length-1; i>0; i--){
select.remove(i);
}
//然后把传入的数据列表list通过循环添加到option上去
list.forEach(function(data){
var option = new Option(data.text, data.value)
select.add(option);
})
}
fillSelect(demoForm.chapter, chapters);
demoForm.chapter.addEventListener("click", function(event){
var value = event.target.value;
//另一种获取value的方式:demoForm.chapter.value
var list = sections[value] || [];
fillSelect(demoForm.section, list);
})
2.表单验证
可验证元素:button input select textarea
element的属性和方法:
-
willValidite
可用于判断元素是否会被验证 -
checkValidity()
验证某个元素 -
validity
存储验证结果,错误信息等 -
validationMessage
验证信息 -
setCustomValidity(message)
设置自定义的异常显示信息
自定义异常:
如果不需要验证,就在<form>
上添加一个 novalidate
属性,就禁止了该表单下所有的验证。
3.表单提交
submit()
方法可以手动提交onsubmit
事件绑定的方法中,可以通过阻止默认事件触发来阻止表单提交刷新页面
4.表单应用
验证手机号可以在标签中写pattern
:
<input name="telephone" type="tel" pattern="^0?(1[34578])[0-9]{9}$">
验证表单应该在表单的submit
事件被触发时进行,而不是按钮被点击时进行。因为还有其他的触发表单提交的方式。
列表操作
列表的显示、添加、删除、更新、选择
数据结构:
事件代理:
如果需要在<ul>
的每项<li>
上都添加点击事件,那么就添加到<ul>
上,在事件处理函数中通过event.target
去获取当前的点击的<li>
元素。
编程方式 : 面向视图-> 面向数据
面向视图编程
view与controller直接关联在一起。
因为视图层变化会很频繁,这直接导致控制层的变化。
而且从自动化测试的角度来说,因为视图层没办法完全自动化,而视图层与控制层的耦合性如此高,导致控制层也没办法做完全的自动化测试。
面向数据
将视图(View)抽象成数据模型(ViewModel),后续所有的操作都针对数据模型来进行。只需要关注数据模型,不关注视图上怎样展示。
具体视图上怎样展示,交给Binder(专门的数据绑定相关的库)去完成:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。