JavaScript高级程序设计第4版-第 12~16 章

67 阅读10分钟

第十二章,BOM

散碎

属性

window {
  self 								自己窗口
  parent 							父窗口
  top 								顶层窗口
  devicePixelRatio		通过这个判断是否高分辨率屏幕
  ...// MDN
}

方法

window {
  moveTo(x, y)		移动到指定位置(单位px(只对 window.open打开窗口有效
  moveBy(x, y)		移动指定距离(单位px(只对 window.open打开窗口有效
  ... // MDN
}

定时器(setInterval、setTimeOut、clearInterval、clearTimeOut

第一个参数可以传 js 代码字符串

setTimeOut('alert()', 1000)

模态框

同步对话框(alert、confirm、prompt

  1. 无法通过 css 修改,
  2. 显示时阻塞 js 运行
alert(tip)						// 单纯只警告
confirm(tip):boolean	// 按钮返回布尔值
prompt(tip, inputDefault)	// 返回输入框内容

异步对话框(find、print

  1. 无法通过 css 修改,
  2. 显示时不会阻塞 js 运行
find(msg)	// 返回布尔值,等价于 ctrl + F
print()	// 打印窗口

location

属性

image.png

原生的解析方法 window.URLSearchParams

简单重写

class URLSearchParams {
  params = {}
  constructor(qs) {
    if (typeof qs !== 'string') throw new TypeError('option must be a string')
    qs = qs.substring(1)
    qs.split('&').forEach(item => {
      const [key, value] = item.split('=')
      this.params[encodeURIComponent(key)] = encodeURIComponent(value)
    })
  }
  has(key) {
    return this.params[key] !== undefined
  }
  get(key) {
    return this.params[key]
  }
  delete(key) {
    delete this.params[key]
  }
  [Symbol.iterator] = () => {
    return Object.entries(this.params)[Symbol.iterator]()
  }
}

操作

  1. window.location & location.href
window.location = 'http://www.baidu.com'
location.href = 'http://www.baidu.com'
location.assign('http://www.baidu.com')

因为前两句改变后会触发 location.assign 同样的,改变其他属性也是如此;都是 push 模式

  1. replace(替换,无法后退
  2. reload(刷新,接受一个 boolean 参数,是否强制刷新,不用缓存

navigator

主要是一些浏览器信息,比如版本号,所在环境,GPU...

screen

显示器信息,什么位深,色深...

history

  1. 前进后退
.go(step: number) // 前进 or 回退 step 步
.back() // 回退
.forward() // 前进
  1. 历史状态管理

由于以前地址栏变化,一定会引起重新请求,后来推出了 历史状态管理 概念,也就是通过 history 的 pushState,replaceState 来改变地址栏而不发起请求。

.pushState(
  state: {data: 'data'}, // 序列化对象
  title: string,	// 页面标题,目前大部分浏览器忽略此参数,任然需要但不起效
  href: string	// 必须与源地址同源的地址,可以之间传文档路径
)
.replaceState // 同上,但不添加而是覆盖当前记录

既然这种方式替换路径不会引起请求,那么就适合用作单页应用的路由管理 但刷新后会真实请求当前路径,需要后端做重定向处理。

  1. 其他
.length // 会话历史长度
.state	// 当前会话状态对象

第十三章,客户端检测

主要是一些

  1. 能力检测:支不支持脚本啦,支不支持什么功能啦...
  2. 用户代理检测:浏览器的信息,什么名字,版本,...
  3. 一些操作系统、浏览器、硬件、设备位置、电池状态...

第十四章,DOM

散碎

NodeList 与 HTMLCollection、NamedNodeMap

  1. 类数组
  2. 实时数组,随文档变化
  3. HTMLCollection 多一个 namedItem 方法(以 name 属性值访问节点)
  4. querySelectorAll 返回 NodeList 快照,并非实时

Node 类型

DOM 一共有 12 中类型,以此为基类,确定类型的方法如下: Node.类型名 === node.nodeType

类型名如下: image.png

Element 类型(nodeType = 1

  1. 随便打印一个 div 元素,他的原型链是这样的:

HTMLDivElement --> HTMLElement --> Element --> Node --> EventTarget --> Object

  1. tagName 返回 标签名的大写形式

节点关系

.childNodes
  1. NodeList 类数组(这个有:forEach、values、keys、entries
  2. 实时反应此节点的子节点情况,并非快照
  3. 代码换行和空格会成为 text 节点出现在内(.children 不会)
  4. 里面的节点保持有双链表关系(非循环)(指针为:.previousSibling 和 .nextSibling)
.firstChild 和 .lastChild

分别指向 .childNodes[0] 和 .childNodes[childNodes.length - 1]

.hashChildNodes

是否有子节点;返回布尔值

由于空白节点会成为节点出现在 childNodes 上,所以在定位的时候会很麻烦 之前的 childNodes、firstChild、lastChild、previousSibling、nextSibling都有了替换项 分别是 children、firstElementChild、lastElementChild、previousElementSibling、nextElementSibling

节点操作

注:如果添加的节点已经存在文档中,那么会移动此节点
同一个节点不会在文档中同时出现在两个或更多个地方
parentNode.appendChild(newNode):newNode // 添加节点到所有子节点最后面
parentNode.insertBefore(newNode, childNode):childNode // 添加到指定节点最前面
parentNode.insertBefore(newNode, null):childNode // 添加到子节点最后面
parentNode.replaceChild(newChild, childNode):childNode // 替换子节点
parentNode.removeChild(childNode):childNode // 删除子节点
node.cloneNode(isDeep) // 拷贝节点(不会复制 js 添加的属性 和 事件处理程序
node.createElement('tagName') // 创建 Element 节点
//节点就可以使用(缩小范围),不一定非得 document.
node.getElementByClassName('className')
node.querySelector('css选择器')
//......

属性

下面两种属性大部分都是相通的,互相影响;特殊情况已列出

DOM 属性
const container = document.querySelector('.container')
container.xxx
HTML 属性
<div class="container" dir="rtl" style="color: red;" onclick="test()"></div>

HTML 属性操作(一般都用于自定义属性,其他用到的地方很少;但其实自定义属性也一般用 DOM 属性的 dataset 了。

container.getAttribute('key') // 获取
container.setAttribute('key', value) // 添加/替换
container.removeAttribute('key')
DOM 属性 与 HTML 属性差异
  1. class
container.className === container.getAttribute('class') // true
container.getAttribute('className') // null
  1. style
container.style // CSSStyleDeclaration 对象
container.getAttribute('style') // "color: red;"
  1. 事件处理程序
container.onclick // test 函数引用,没有则是 null
container.getAttribute('onclick') // "test()"
  1. 自定义属性
  • 自定义属性不会成为 DOM 属性(但会体现在 attributes 属性上)
  • HTML5 规定自定义属性以 data- 开头,不然不会添加到 DOM 属性的 dataset 对象中
<div class="container" data-self="msg"></div>

container.getAttribute('data-selef') === container.dataset['self'] // true
attributes

HTML 属性会反映在 DOM 属性 attributes 上 image.png

Document 类型(nodeType = 9

全局 document 原型链

HTMLDocument --> Document --> Node

子节点类型

document 的子节点类型只能是 DocumentType(比如) Element(html) ProcessingInstruction(xml) Comment(注释)

iframe 文档跨域

取决于响应头中的 Content-Security-Policy字段 image.png

其他集合

实时集合

  1. .anchors 所有带有 name 属性的 a 元素
  2. forms 所有 forms 元素
  3. images 所有 images 元素
  4. links 所有带有 href 的 a 元素

文档写入

document.write/writeln/open/close

write 和 writeln 就是后者写入后会多个换行;调用如果在加载页面后调用,页面则会被输出内容重写

调用 open 和 close 可以更好的控制何时写入何时结束,open 调用后文档子节点会清空

Text 类型(nodeType = 3

一般来说,大部分业务场景都能用 innerText 代替文本节点的操作方法 echars 的大量数据就用的 appendData

  1. 对于相邻的文本节点,可以执行以下 normalize 方法,将相邻文本节点合成一个
parentNode.normalize()
  1. 操作
textNode.appendData(text)  // 往后增加 text
textNode.deleteData(start, count) // 从 start 出开始删除 count 个字符
textNode.insertData(start, text) // 在 start 前添加 text
textNode.replaceData(start, count, text) // 用法类似 array.splice。替换 start 到 start + count 为 text(左闭右开
textNode.splitText(offset) // 从 offset 位置拆分文本,原文本节点保留 [0, offset),返回 [offset, length - 1]
textNode.substringData(start, count) // 从 start 开始提取 count 个字符 [) 
doc.createTextNode()

Comment 类型 (nodeType = 8

注释节点,和 Text 节点拥有类似方法,因为他们继承同一基类 CharacterData

document.createComment() // 创建注释

CDATSection 类型 (nodeType = 4

继承 Text 类型;只在 XML 中有效

DocumentType 类型(nodeType = 10

<!DOCTYPE xxx>

**DocumentFragment **类型(nodeType = 11

  1. 继承自 Node 所以可以像 document 那样使用,但是他只作为一个存放节点的仓库
  2. 如果文档中有节点被添加到文档片段,那么这个节点会在文档树中被删除
<div class="pop">hello</div>
//
const pop = document.querySelector('.pop')
const documentFragment = document.createDocumentFragment()
documentFragment.appendChild(pop)
// pop 会在页面被移除
  1. 添加进文档的时候,文档片段本身不会被添加,只是他的子节点会被添加,且文档片段的子节点会被清空
console.log(documentFragment.childNodes) // NodeList[div.pop]
parentNode.appendChild(documentFragment)
console.log(documentFragment.childNodes) // NodeList[]

Attr 类型(nodeType = 2

一般就是 attributes 属性

document.createAttribute() // 创建属性节点

表现形式:

将属性作为节点来访问多数情况下并无必要。推荐使用 getAttribute()、 removeAttribute() 和 setAttribute() 方法操作属性,而不是直接操作属性节点。

DOM 编程

动态脚本

  1. 添加外部 js
const script = document.createElement('script')
script.src = 'demo.js'
document.body.appendChild(script)
  1. 添加 js 源代码
const script = document.createElement('script')
script.text = 'function test(){ alert(1) }; test()'
document.body.appendChild(script)

用 innerHTML 添加的 script 代码永远不会执行

动态样式

也是同上可以通过创建 link 或者 style 节点进行添加,往 style 中添加内容可以用 innerText 或者 文本节点的方式都行。

操作表格

同上,但是相当繁琐,所以 table 元素自带了很多操作函数和访问属性 image.pngimage.png

MutationObserver

MutationEvent 已废弃

切勿以为每次改变后回调都会执行,他的形式是 回调+记录队列 的形式;也就是每次改变都会添加到记录队列 mutationRecords 里,回调只在本次执行上下文观察到改变后执行一次

<div class="container"></div>
<div class="footer"></div>
<script>
  const container = document.querySelector('.container')
  const footer = document.querySelector('.footer')

  // Boolean 配置默认值都是 false,至少有一个 Boolean 配置为 true
  const MutationObserverInit = {
    subtree: false, // 是否观察子节点的各种变化
    characterData: false, // 是否观察文本节点变化(注:是文本节点的文本,不是节点的文本
    attributes: true, // 是否观察属性值变化,不包括自定义 DOM 属性;包括 DOM、HTML、自定义 HTML 属性
    childList: false, // 是否观察子节点变化(自身的 childNodes
    characterDataOldValue: false, // 是否需要旧值,体现在 mutatedRecord 实例的 oldValue 上
    attributeOldValue: false // 是否需要旧值,体现在 mutatedRecord 实例的 oldValue 上
    // attributeFilter: [] // 字符串数组,指定需要观察的属性
  }

  // 异步回调,微任务;会在观察的对象发生范围内的变化时添加到微任务队列中
  const observer = new MutationObserver((mutationRecords, _observer) => {
    console.log(mutationRecords, _observer === observer) // ⑤:[MutationRecord, MutationRecord, MutationRecord] true
  })

  // ①每次改变,改变记录会被存在记录列表,当异步回调执行时传递记录列表过去
  observer.observe(container, MutationObserverInit)
  observer.observe(footer, MutationObserverInit)
  container.className = 'newClass' 
  container.id = 'app'
  container.dataset.msg = 'msg'
  container.自定义DOM属性 = '自定义DOM属性值' // 不会被观察到

  // ②取消观察,属于一刀切,会断开一切观察关系;前面的改变也不会被观察到了;不会结束实例生命周期
  observer.disconnect()
  container.className = 'disconnect-container' // 不会被观察到
  footer.className = 'discooent-footer' // 不会被观察到

  // ③任然可以继续观察
  observer.observe(container, MutationObserverInit)
  container.className = 'newClass' // 至于是在这段代码后执行后添加异步回调,还是同步代码执行完添加,这不重要!
  container.id = 'app'
  container.dataset.msg = 'msg'

  // ④提前取出记录列表,取出后回调不执行,但并未断开连接
  console.log(observer.takeRecords()) // [MutationRecord, MutationRecord, MutationRecord]

  // ⑤这才是回调所打印到的记录
  container.className = 'newClass' 
  container.id = 'app'
  container.dataset.msg = 'msg'
</script>
  1. mutationRecords

image.png

设置元素文本内容的标准方式是 textContent 属性,innerText 的定义不严谨,浏览器间的实现也存在兼容性问题,因此不建议再使用了。

第十五章,DOM扩展

元素查找

querySelectorAll 返回 NodeList 快照,并非实时 matches 返回 boolean 是否有这个元素

HTML5

  1. classList 不是数组

DOMTokenList实例 image.png

  1. document.activeElement

始终指向目前的焦点,默认情况下页面加载前为 null ,页面加载完后指向 body

多用于无障碍功能

  1. document.readyState

loading:文档正在加载

complete:文档加载完成

  1. document.compatMode

CSS1Compat:标准模式

BackCompat:混杂模式

  1. outerHTML

返回包括自身的序列化 DOM

<div class="box">1</div>
<script>
  const box = document.querySelector('.box')
  box.outerHTML = '<h1>he</h1>' // 会取代 <div class="box">1</div>
</script>
  1. scrollIntoView

相对于第一个可滚动容器

/**
  option 为布尔值
    true: 滚动到顶部
    false: 滚动到底部
    
  option 为配置对象
    {
      behavior: 'auto', // 滚动动画 smooth auto 	
      block: 'start', // 滚动到容器中垂直位置 start center end nearest
      inline: 'nearest' // 滚动到容器中水平位置 start center end nearest
    }
*/
element.scrollIntoView(option)

当元素聚焦时,聚焦元素也会滚动到可视范围,默认无动画,居中

  1. contains 与 compareDocumentPosition

判断参数和调用者关系

parentNode.contains(child: Node): boolean // 是否为后代
parentNode.compareDocumentPosition(child: Node): number
// 相对于 child 而言
0x1  不在
0x2  在前面
0x4  在后面	
0x8  包含
0x10 被包含

child 是 parentNode 的后代, compareDocumentPosition 会返回 20,0x14; 异于常规思想;因为 child 既被包含,也在parentNode后面

  1. innerText 和 outerText

非标准 在用于读取值时, innerText 会按照深度优先的顺序将子树中所有文本节点的值拼接起来。在用于写入值时,innerText 会移除元素的所有后代并插入一个包含该值的文本节点。

第十六章,DOM2和DOM3

XML:一种数据格式,多用于存储数据,配置(类似 JSON 的功能

HTML:一种数据格式,多用于定义页面

XHTML:HTML 的严格子集,比如必须闭合标签、必须小写....

本章关于 XML 和 XHTML 略

node.style.float // ❌
node.style.cssfloat // ✔
// 节点
node.isSameNode(node: Node): boolean // 是否相同节点(引用相同
node.isEqualNode(node: Node): boolean // 节点是否相等(深克隆

// iframe
iframe.contentDocument
iframe.contentWindow

......