3

文档树 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 属性访问器

两种访问方式:

  1. input.className;
  2. input["id"] = 'cute'

属性访问器的通用性拓展性不好。

3.getAttribute/setAttribute

  1. element.getAttribute(attritubeName)

eg: input.getAttribute("class");

  1. 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.datasetdataset是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

事件类型:

图片描述
注意:
mouseoutmouseleave的区别就是mouseleave不冒泡
mouseovermouseenter的区别也是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

jsfiddle拖拽div栗子

4.FocusEvent

事件类型:

图片描述

blur 元素失去焦点时
focus 元素获得焦点时
focusin 元素即将获得焦点时
focusout 元素将要失去焦点时

属性:

relatedTarget 作用:当一个元素失去焦点时,另外一个元素就要获得焦点。在blur失去焦点事件中,获得焦点的元素就是这个relatedTarget;在focus获得焦点事件中,失去焦点的元素就是这个relatedTarget

5.InputEvent

图片描述

6.KeyBoardEvent键盘事件

图片描述

属性
  • key 按下了什么键
  • code 按下了什么键

jsfiddle中MDN官方例子

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

图片描述

属性:
除了NameValue之外,还有
Domain
Path
浏览器会话时间:Expires(时间戳)/Max-Age(最长时间)

cookie进行增删改查:

document.cookie 属性看起来像是一个普通的文本字符串,其实它不是。
所以,很神奇的一点是:添加新的cookie只需要document.cookie = "newKey=newValue";就可以了,不需要document.cookie += ...,之前的cookie不会被覆盖。
还有一点是:即使在 document.cookie 中写入一个完整的 cookie 字符串, 当重新读取该 cookie 信息时,cookie 信息是以名/值对的形式展示的。

去jsfiddle里看吧

删除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");

基本绘图步骤:

图片描述

地球绕着太阳转实例代码:

图片描述

Mozilla官方教程

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

jsfiddle栗子

主要代码:

//向列表中填充数据
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(专门的数据绑定相关的库)去完成:

图片描述


一只瓦罐
954 声望47 粉丝