DOM
文档对象模型 (DOM) 将 web 页面与到脚本或编程语言连接起来。通常是指 JavaScript,但将 HTML、SVG 或 XML 文档建模为对象并不是 JavaScript 语言的一部分。DOM模型用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点(node),每个节点都包含着对象(objects)。DOM的方法(methods)让你可以用特定方式操作这个树,用这些方法你可以改变文档的结构、样式或者内容。节点可以关联上事件处理器,一旦某一事件被触发了,那些事件处理器就会被执行
Document
document 是 Document 的一个实例, document.constructor 为 HTMLDocument, 继承了 Document
同时 Document 还继承了 Node 和 ParentNode, 因此有一些处理节点的方法
-
基本属性
interface Document extends Node, ParentNode, ... { readonly URL: string; readonly documentURI: string; domain: string; cookie: string; readonly head: HTMLHeadElement; title: string; readonly scripts: HTMLCollectionOf<HTMLScriptElement>; body: HTMLElement; readonly documentElement: HTMLElement; readonly characterSet: string; readonly charset: string; readonly contentType: string; readonly doctype: DocumentType | null; designMode: string; // 复制辣鸡百度文档神器 readonly fullscreenEnabled: boolean; location: Location readonly origin: string; readonly readyState: DocumentReadyState; readonly referrer: string; // ... } -
基本方法
interface Document extends Node, ParentNode, ... { // 1. 查询元素, querySelector 在 ParentNode 中介绍 getElementById(elementId: string): HTMLElement | null; getElementsByClassName(classNames: string): HTMLCollectionOf<Element>; getElementsByName(elementName: string): NodeListOf<HTMLElement>; getElementsByTagName(qualifiedName: string): HTMLCollectionOf<Element>; // 2. 创建元素 createAttribute(localName: string): Attr; createComment(data: string): Comment; createDocumentFragment(): DocumentFragment; // 创建新的文档 createElement(tagName: string, options?: ElementCreationOptions): HTMLElement; createTextNode(data: string): Text; // 3. 事件 addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; createEvent(eventInterface: 'xxx'): xxx; // 例如 createEvent(eventInterface: 'FocusEvent'): FocusEvent // ... }
Node
代表一个 DOM 节点
-
属性
interface Node extends EventTarget { // 节点信息 readonly nodeName: string; readonly nodeType: number; nodeValue: string | null; textContent: string | null; readonly ownerDocument: Document | null; readonly isConnected: boolean; // 节点是否连接在 DOM 上 readonly baseURI: string; // 子 readonly childNodes: NodeListOf<ChildNode>; readonly firstChild: ChildNode | null; readonly lastChild: ChildNode | null; // 兄弟 readonly nextSibling: ChildNode | null; // 下一个兄弟节点 readonly previousSibling: ChildNode | null; // 上一个兄弟节点 // 父 readonly parentElement: HTMLElement | null; readonly parentNode: Node & ParentNode | null; } -
方法
interface Node extends EventTarget { // 增 appendChild<T extends Node>(newChild: T): T; insertBefore<T extends Node>(newChild: T, refChild: Node | null): T; // 查 contains(other: Node | null): boolean; getRootNode(options?: GetRootNodeOptions): Node; hasChildNodes(): boolean; isEqualNode(otherNode: Node | null): boolean; isSameNode(otherNode: Node | null): boolean; // 更新 replaceChild<T extends Node>(newChild: Node, oldChild: T): T; // 删 removeChild<T extends Node>(oldChild: T): T; // 克隆 cloneNode(deep?: boolean): Node; // ... }
ParentNode
Document 同时继承了 Node 和 ParentNode
ParetnNode 代表一个父节点, 提供了一些节点作为父节点特有的属性和方法
-
属性
interface ParentNode { readonly childElementCount: number; readonly children: HTMLCollection; readonly firstElementChild: Element | null; // 不算 Text, Comment ... 的第一个元素类型的儿子 readonly lastElementChild: Element | null; } -
方法
interface ParentNode { /** * Inserts nodes after the last child of node, while replacing strings in nodes with equivalent Text nodes. */ append(...nodes: (Node | string)[]): void; /** * Inserts nodes before the first child of node, while replacing strings in nodes with equivalent Text nodes. */ prepend(...nodes: (Node | string)[]): void; // 返回第一个匹配选择器的元素 querySelector<E extends Element = Element>(selectors: string): E | null; // 返回所有匹配 querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>; }
ChildNode
Element 接口 (下一章讲到) 继承了 Node 和 ChildNode 和 ParentNode
ChildNode 代表一个子节点, 提供了一些子节点特有方法
interface ChildNode extends Node {
// 添加节点到当前节点之后
after(...nodes: (Node | string)[]): void;
// 添加节点到当前节点之前
before(...nodes: (Node | string)[]): void;
// 移除当前节点
remove(): void;
// 用 nodes 替换当前节点
replaceWith(...nodes: (Node | string)[]): void;
}
Element
Element 接口继承了 Node, ChildNode, ParentNode, 和 InnerHTML
先从最简单的 InnerHTML 开始, 它定义了一个 innerHTML: string 属性, 用于返回元素内部的 html
下面来看 Element 接口
-
属性
interface Element extends Node, ChildNode, ParentNode, InnerHTML /*, ...... */ { readonly tagName: string; // 大写 tag name, 如 DIV readonly localName: string; // tag 的本地名, 如 div id: string; readonly attributes: NamedNodeMap; readonly classList: DOMTokenList; className: string; readonly ownerDocument: Document; outerHTML: string; // 对于没有定义 CSS 或 内联布局盒子的元素为0 // 否则值为元素内部的高度和宽度 (单位为px), 包含内边距 // 即 content + padding // 得到的值会被四舍五入, 得到精确值需要使用 // getBoundingClientReact() readonly clientHeight: number; readonly clientWidth: number; // 表示一个元素的左边和顶部边框宽度(px) readonly clientLeft: number; readonly clientTop: number; // 一个元素高度和宽度的度量 // 包括由于溢出导致的视图中不可见内容 readonly scrollHeight: number; readonly scrollWidth: number; // 用于读取或设置元素滚动条到元素左边或上边的距离 scrollLeft: number; scrollTop: number; readonly shadowRoot: ShadowRoot | null; readonly assignedSlot: HTMLSlotElement | null; slot: string; } -
方法
interface Element extends Node, ChildNode, ParentNode, InnerHTML /*, ...... */ { // 属性操作 getAttribute(qualifiedName: string): string | null; getAttributeNames(): string[]; getAttributeNode(name: string): Attr | null; hasAttribute(qualifiedName: string): boolean; hasAttributes(): boolean; setAttribute(qualifiedName: string, value: string): void; setAttributeNode(attr: Attr): Attr | null; removeAttribute(qualifiedName: string): void; removeAttributeNode(attr: Attr): Attr; // DOMRect getBoundingClientRect(): DOMRect; getClientRects(): DOMRectList; // 查询元素 getElementsByClassName(classNames: string): HTMLCollectionOf<Element>; getElementsByTagName<K extends keyof HTMLElementTagNameMap>(qualifiedName: K): HTMLCollectionOf<HTMLElementTagNameMap[K]>; addEventListener<K extends keyof ElementEventMap>(type: K, listener: (this: Element, ev: ElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; }
HTMLElement
interface HTMLElement extends Element, DocumentAndElementEventHandlers, ElementCSSInlineStyle, ElementCSSInlineStyle, ElementContentEditable, GlobalEventHandlers, HTMLOrSVGElement {
draggable: boolean;
hidden: boolean;
innerText: string;
lang: string;
readonly offsetHeight: number;
readonly offsetLeft: number;
readonly offsetParent: Element | null;
readonly offsetTop: number;
readonly offsetWidth: number;
spellcheck: boolean;
title: string;
translate: boolean;
click(): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
Event
interface Event {
initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;
readonly type: string;
readonly bubbles: boolean;
cancelBubble: boolean;
readonly cancelable: boolean;
readonly eventPhase: number; // 返回用数字代表的事件阶段
readonly target: EventTarget | null; // 返回发布事件的对象
readonly currentTarget: EventTarget | null; // 返回处理事件的当前对象
readonly defaultPrevented: boolean;
preventDefault(): void;
stopImmediatePropagation(): void; // 阻止事件冒泡并阻止该元素上同类事件监听器被触发
stopPropagation(): void; // 阻止事件冒泡
readonly AT_TARGET: number;
readonly BUBBLING_PHASE: number;
readonly CAPTURING_PHASE: number;
readonly NONE: number;
}
EventTarget
interface EventTarget {
addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void;
dispatchEvent(event: Event): boolean;
removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean): void;
}
事件类型
不同类型的事件接口都继承了 Event, 例如
-
DragEventinterface DragEvent extends MouseEvent { readonly dataTransfer: DataTransfer | null; } interface DataTransfer { dropEffect: 'none' | 'copy' | 'link' | 'move'; effectAllowed: 'none' | 'copy' | 'copyLink' | 'copyMove' | 'link' | 'linkMove' | 'move' | 'all'; readonly files: FileList; readonly items: DataTransferItemList; readonly types: ReadonlyArray<string>; clearData(format?: string): void; getData(format: string): string; setData(format: string, data: string): void; setDragImage(image: Element, x: number, y: number): void; } -
UIEventinterface UIEvent extends Event { readonly detail: number; readonly view: Window | null; } -
FocusEventinterface FocusEvent extends UIEvent { readonly relatedTarget: EventTarget | null; } -
InputEventinterface InputEvent extends UIEvent { readonly data: string | null; readonly inputType: string; readonly isComposing: boolean; } -
KeyboardEventinterface KeyboardEvent extends UIEvent { readonly altKey: boolean; readonly code: string; readonly ctrlKey: boolean; readonly isComposing: boolean; readonly key: string; readonly location: number; readonly metaKey: boolean; readonly repeat: boolean; readonly shiftKey: boolean; getModifierState(keyArg: string): boolean; readonly DOM_KEY_LOCATION_LEFT: number; readonly DOM_KEY_LOCATION_NUMPAD: number; readonly DOM_KEY_LOCATION_RIGHT: number; readonly DOM_KEY_LOCATION_STANDARD: number; } -
MouseEventinterface MouseEvent extends UIEvent { readonly altKey: boolean; readonly ctrlKey: boolean; readonly shiftKey: boolean; readonly metaKey: boolean; // 0 主键(左键), 1 辅键 (中键), 2 次键 (右键) // 4 : 第四个按钮, 通常指浏览器后退按键 // 5 : 第五个按键, 通常指浏览器前进按键 // 对于配置为左手用的鼠标对应刚好相反 readonly button: number; readonly buttons: number; // 按下按键数字之和 readonly relatedTarget: EventTarget | null; // 鼠标事件发生时应用客户端区域的 x 和 y // 例如, 不管页面是否有水平垂直滚动 // 点击左上角 clientX 和 clientY 都为 0 readonly clientX: number; readonly clientY: number; // 提供了当前事件和上一个 "mousemove" 事件之间鼠标在 x 和 y 方向的移动值 readonly movementX: number; readonly movementY: number; // 这两个属性还在实验阶段 readonly offsetX: number; readonly offsetY: number; // 返回相对于整个文档的 x 和 y 值 // 例如, 页面向右滚动 200px, 点击距离窗口左边 100px // pageX 为 300px readonly pageX: number; readonly pageY: number; // 返回鼠标基于全局 (整个屏幕) 的 x 和 y 坐标 readonly screenX: number; readonly screenY: number; // 实验中的属性, clientX 和 clientY 的别名 readonly x: number; readonly y: number; // ... }
BOM
浏览器对象模型, 描述了与浏览器进行交互的方法和接口, 由
windowscreenhistorynavigatorlocation
这几个关键对象组成
window
全局 window 对象为一个 Window 实例
-
属性
/** A window containing a DOM document; the document property points to the DOM document loaded in that window. */ interface Window extends EventTarget, AnimationFrameProvider, GlobalEventHandlers, WindowEventHandlers, WindowLocalStorage, WindowOrWorkerGlobalScope, WindowSessionStorage { name: string; // 获得或设置窗口名 readonly closed: boolean; // 窗口是否关闭 readonly document: Document; readonly parent: Window; readonly top: Window; // 窗口层级最顶层窗口的引用 readonly window: Window & typeof globalThis; readonly self: Window & typeof globalThis; readonly frameElement: Element; // 嵌入窗口的元素 readonly frames: Window; // 返回窗口中所有子窗口 readonly length: number; // 窗口中 frames 数 readonly screen: Screen; readonly history: History; readonly navigator: Navigator; location: Location; // 获得窗口内容区的高度和宽度, 包含垂直或水平滚动条 (如果有) readonly innerHeight: number; readonly innerWidth: number; // 获得整个浏览器窗口的高度和宽度 readonly outerHeight: number; readonly outerWidth: number; // 页面 x y 方向偏移 // 例如往下滚动 100px, 那么 pageYOffset 就为 100px readonly pageXOffset: number; readonly pageYOffset: number; // 页面垂直或水平方向的滚动值, pageXOffset 和 pageYOffset 的别名 readonly scrollX: number; readonly scrollY: number; readonly screenLeft: number; readonly screenTop: number; // 浏览器左边界到操作系统桌面左边界的距离 // 浏览器上边界到操作系统桌面上边界的距离 readonly screenX: number; readonly screenY: number; // UI readonly locationbar: BarProp; readonly menubar: BarProp; readonly statusbar: BarProp; readonly toolbar: BarProp; readonly personalbar: BarProp; readonly scrollbars: BarProp; } -
方法
interface Window extends EventTarget, AnimationFrameProvider, GlobalEventHandlers, WindowEventHandlers, WindowLocalStorage, WindowOrWorkerGlobalScope, WindowSessionStorage { // 弹窗 alert(message?: any): void; confirm(message?: string): boolean; prompt(message?: string, _default?: string): string | null; console: Console // 焦点控制 blur(): void; focus(): void; // 窗口打开/关闭/停止 open(url?: string, target?: string, features?: string, replace?: boolean): Window | null; close(): void; // 关闭窗口, 等同于 ctrl + w stop(): void; // 相当于点击浏览器停止按钮, 方法不能阻止已经包含在加载中的文档, 但能够阻止图片, 新窗口, 和一些会延迟加载的对象的加载 print(): void; // 等同于 ctrl + p // 滚动当前页面到指定位置 scroll(x: number, y: number): void; // 根据当前页面位置滚动 scrollBy(x: number, y: number): void; // 等价于 scroll scrollTo(x: number, y: number): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; [index: number]: Window; }
其中, 接口 WindowLocalStorage 和 WindowSessionStorage 分别声明了 localStorage 和 sessionStorage
WindowOrWorkerGlobalScope
Window 接口继承了这个接口
interface WindowOrWorkerGlobalScope {
readonly caches: CacheStorage;
readonly crypto: Crypto;
readonly indexedDB: IDBFactory;
readonly isSecureContext: boolean;
readonly origin: string;
readonly performance: Performance;
atob(data: string): string;
btoa(data: string): string;
clearInterval(handle?: number): void;
clearTimeout(handle?: number): void;
setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
queueMicrotask(callback: VoidFunction): void;
}
screen
interface Screen {
readonly availHeight: number;
readonly availWidth: number;
readonly height: number;
readonly width: number;
readonly colorDepth: number;
readonly pixelDepth: number;
readonly orientation: ScreenOrientation;
}
location
/** The location (URL) of the object it is linked to. Changes done on it are reflected on the object it relates to. Both the Document and Window interface have such a linked Location, accessible via Document.location and Window.location respectively. */
interface Location {
href: string; // 获取 url 或设置以导航到给定 url
// 返回 url 的 origin, origin = protocol + hostname + port
readonly origin: string;
protocol: string; // 获取或设置协议
host: string; // 获取 host 或设置以导航到相同 URL 但不同的主机和端口
hostname: string; // 获取主机名或设置以导航到同 URL 但不同主机名
port: string; // 获取或设置端口
// 获取或设置当前 url 下的路径
// 不包括 hash 和 query
pathname: string;
// 获取 hash 或 设置以导航到 URL 中的 hash 处
// 注意修改 hash 不会发送请求到服务器
hash: string;
// 获取或设置 query, 例如
// 在 htts://cn.bing.com/search?q=react
// 设置 location.search = '?q=vue'
search: string
// 导航到给定 url
assign(url: string): void;
reload(): void; // 等价于 f5 快捷键
// 从会话历史中删除该页面并导航到指定 url
replace(url: string): void;
}
history
interface History {
readonly length: number;
scrollRestoration: 'auto' | 'manual';
readonly state: any; // 存储传递的 data
back(): void;
forward(): void;
go(delta?: number): void;
pushState(data: any, title: string, url?: string | null): void;
replaceState(data: any, title: string, url?: string | null): void;
}
HTML5 引入了 pushState 和 replaceState, 通常配合 window.onpopstate 使用
pushState
pushState 可以改变 referrer, 他在用户发送 XMLHttpRequest 请求时在 HTTP 头部使用, 改变 state 后创建的 XMLHttpRequest 对象的 referrer 都会被改变. 因为 referrer 是标识创建 XMLHttpRequest 对象时 this 所代表的 window 对象中 document 的 URL
在某种意义上, 调用 pushState 与设置 window.location = '#foo' 类似, 两者都会在当前页面创建并激活新的历史记录, 但 pushState 有以下优点 :
- 新 URL 可以是与当前 URL 同源的任意URL. 相反, 修改 hash 时, 设置
window.location才能是同一个document - 不想更改 URL 时可以不用更改 (省略第三个参数). 而设置
window.location = '#foo'在当前hash 不为#foo才能创建新的历史记录项- 可以将任意数据通过
data参数与历史记录项关联. 而基于 hash 的方式需要将所有数据编码为短字符串
- 可以将任意数据通过
- 以后还可以使用
title(虽然这个值现在被浏览器忽略)
注意 :
pushState即使新 URL 和 旧 URL 仅 hash 不同也不会触发hashchange事件
例如在 : www.xxx.com/foo.html 下
history.pushState({ a: 1, b: 2 }, '', 'bar.html')
地址栏将变成 www.xxx.com/bar.html
并且 history.state 为 { a: 1, b: 2 }
replaceState
与 pushState 类似, 但仅修改当前历史记录项而不新建
navigator
// 表示用户代理的状态和表示, 它允许脚本查询它和注册自己进行一些活动
interface Navigator extends NavigatorStorage /*, ... */ {
readonly maxTouchPoints: number;
readonly mediaDevices: MediaDevices;
readonly permissions: Permissions;
readonly serviceWorker: ServiceWorkerContainer;
// ...
}