Page Visibility API

Page Visibility API 用于反馈当前文档的可见性

读取可见性

Document 接口的 visibilityState 属性返回一个 string,代表当前文档的可见性,值为 visiblehidden

Document 接口的 hidden 属性返回一个 boolean,代表当前文档是否隐藏

1
2
document.hidden
document.visibilityState

监视可见性改变

Document 接口的 visibilitychange 事件在当前文档的可见性改变时触发,返回一个 Event

1
2
3
document.addEventListener('visibilitychange', () => {
//
})

类型

1
2
3
4
5
6
7
interface Document {
readonly hidden: boolean
readonly visibilityState: DocumentVisibilityState
onvisibilitychange: ((this: Document, ev: Event) => any) | null
}

type DocumentVisibilityState = 'hidden' | 'visible'

链接

User Activation API

User Activation API 用于检测当前网页的用户激活状态

该 API 通过 UserActivation 提供相关功能,并通过 navigator.userActivation 暴露实例使用

一些 API 的调用受用户激活行为的限制,在不满足条件时会抛出异常,可以使用该 API 检测是否已满足调用的条件

用户激活状态

用户激活状态的改变受如下事件的影响:

事件的 isTrusted 属性被设置为 true

事件的 type 属性为 keydown(需要不为 Esc 键或用户代理保留的快捷键)、mousedownpointerdown(需要 pointerTypemouse)、pointerup(需要 pointerType 不为 mouse)、touchend

粘性激活

表示用户已经进行了交互行为

可通过 UserActivation 接口的 hasBeenActive 属性检测

瞬时激活

表示用户最近进行了交互行为

可通过 UserActivation 接口的 isActive 属性检测

示例

粘性激活 瞬时激活
false false

类型

1
2
3
4
5
6
7
8
9
10
11
12
interface UserActivation {
readonly hasBeenActive: boolean
readonly isActive: boolean
}

declare var UserActivation: {
prototype: UserActivation
}

interface Navigator {
readonly userActivation: UserActivation
}

链接

UI Events

UI Events 定义了与用户交互的事件

事件类型

事件类型 事件描述 事件列表
UIEvent 作为其他用户交互事件的基类的事件 load
unload
abort
error
select
FocusEvent 涉及页面的聚焦和失焦的事件 blur
focus
focusin
focusout
MouseEvent 涉及鼠标移动的事件 mousedown
mouseenter
mouseleave
mousemove
mouseout
mouseover
mouseup
auxclick
click
contextmenu
dblclick
WheelEvent 涉及鼠标滚动的事件 wheel
InputEvent 涉及输入的事件 beforeinput
input
KeyboardEvent 涉及键盘敲击相关的事件 keydown
keyup
CompositionEvent 涉及组合输入的事件 compositionstart
compositionupdate
compositionend

事件列表

事件名称 事件类型 事件目标 事件是否冒泡 事件是否可取消 事件描述
load UIEvent Window Document Element DOM 实现从环境中加载资源或依赖资源
unload UIEvent Window Document Element DOM 实现从环境中删除资源或依赖资源
abort UIEvent Window Element 用户代理加载资源被中止
error UIEvent Window Element 用户代理加载资源失败或解析执行资源失败
select UIEvent Element 用户选择一些文字
blur FocusEvent Window Element 事件目标失去焦点
focus FocusEvent Window Element 事件目标获得焦点
focusin FocusEvent Window Element 事件目标获得焦点
focusout FocusEvent Window Element 事件目标失去焦点
auxclick PointerEvent Element 按下并释放非主指针按钮
click PointerEvent Element 按下并释放主指针按钮
contextmenu PointerEvent Element 打开上下文菜单时触发
dblclick MouseEvent Element 两次按下并释放主指针按钮
mousedown MouseEvent Element 元素上按下指针按钮
mouseenter MouseEvent Element 指针按钮移入元素及其子元素
mouseleave MouseEvent Element 指针按钮移出元素及其子元素
mousemove MouseEvent Element 指针按钮在元素内移动
mouseout MouseEvent Element 指针按钮移出元素及其子元素
mouseover MouseEvent Element 指针按钮移入元素及其子元素
mouseup MouseEvent Element 元素上释放指针按钮
wheel WheelEvent Element 鼠标滚轮滚动
beforeinput InputEvent Element DOM 将要更新
input InputEvent Element DOM 已经更新
keydown KeyboardEvent Element 按下键盘按键
keyup KeyboardEvent Element 释放键盘按键
compositionstart CompositionEvent Element 组合输入将开始
compositionupdate CompositionEvent Element 组合输入中
compositionend CompositionEvent Element 组合输入结束

事件顺序

聚焦事件顺序

聚焦 A 元素后聚焦 B 元素

focus A -> focusin A -> blur A -> focusout A -> focus B -> focusin B

鼠标事件顺序

指针设备移入 A 元素后移出

mouseover A -> mouseenter A -> mousemove A -> mouseout A -> mouseleave A

指针设备移入 A 元素后移入 B 元素后移出,其中 B 元素为 A 元素的子元素

mouseover A -> mouseenter A -> mousemove A -> mouseout A -> mouseover B -> mouseenter B -> mousemove B -> mouseout B -> mouseleave B -> mouseover A -> mousemove A -> mouseout A -> mouseleave A

指针设备移入 C 元素后移出,且 B 元素为 A 元素的子元素,C 元素为 B 元素的子元素,A 元素 B 元素 C 元素在空间上完全重叠

mouseover C -> mouseenter A -> mouseenter B -> mouseenter C -> mousemove C -> mouseout C -> mouseleave -> mouseleave B -> mouseleave A

指针设备在元素上点击

mousedown {-> mousemove} -> mouseup -> click

指针设备在元素上双击

mousedown {-> mousemove} -> mouseup -> click {-> mousemove} -> mousedown {-> mousemove} -> mouseup -> click -> dblclick

输入事件顺序

用户输入

beforeinput -> input

键盘事件顺序

键盘按下按键

keydown {-> beforeinput} {-> input} -> keyup

键盘连续按下按键

keydown {-> beforeinput} {-> input} -> keydown {-> beforeinput} {-> input} -> … -> keyup

组合输入顺序

组合输入

compositionstart -> compositionupdate -> compositionend

连续组合输入

compositionstart -> compositionupdate -> compositionupdate -> … -> compositionend

键盘+组合输入顺序

keydown -> compositionstart -> compositionupdate -> keyup -> keydown -> compositionstart -> compositionupdate -> keyup -> … -> keydown -> compositionend -> keyup

输入+组合输入顺序

beforeinput -> compositionupdate -> input -> compositionend

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
interface UIEvent extends Event {
readonly detail: number
readonly view: Window | null
/** @deprecated */
readonly which: number
/** @deprecated */
initUIEvent(typeArg: string, bubblesArg?: boolean, cancelableArg?: boolean, viewArg?: Window | null, detailArg?: number): void
}

declare var UIEvent: {
prototype: UIEvent
new(type: string, eventInitDict?: UIEventInit): UIEvent
}

interface UIEventInit extends EventInit {
detail?: number
view?: Window | null
/** @deprecated */
which?: number
}

interface FocusEvent extends UIEvent {
readonly relatedTarget: EventTarget | null
}

declare var FocusEvent: {
prototype: FocusEvent
new(type: string, eventInitDict?: FocusEventInit): FocusEvent
}

interface FocusEventInit extends UIEventInit {
relatedTarget?: EventTarget | null
}

interface MouseEvent extends UIEvent {
readonly altKey: boolean
readonly button: number
readonly buttons: number
readonly clientX: number
readonly clientY: number
readonly ctrlKey: boolean
readonly metaKey: boolean
readonly movementX: number
readonly movementY: number
readonly offsetX: number
readonly offsetY: number
readonly pageX: number
readonly pageY: number
readonly relatedTarget: EventTarget | null
readonly screenX: number
readonly screenY: number
readonly shiftKey: boolean
readonly x: number
readonly y: number
getModifierState(keyArg: string): boolean
/** @deprecated */
initMouseEvent(typeArg: string, canBubbleArg: boolean, cancelableArg: boolean, viewArg: Window, detailArg: number, screenXArg: number, screenYArg: number, clientXArg: number, clientYArg: number, ctrlKeyArg: boolean, altKeyArg: boolean, shiftKeyArg: boolean, metaKeyArg: boolean, buttonArg: number, relatedTargetArg: EventTarget | null): void
}

declare var MouseEvent: {
prototype: MouseEvent
new(type: string, eventInitDict?: MouseEventInit): MouseEvent
}

interface MouseEventInit extends EventModifierInit {
button?: number
buttons?: number
clientX?: number
clientY?: number
movementX?: number
movementY?: number
relatedTarget?: EventTarget | null
screenX?: number
screenY?: number
}

interface EventModifierInit extends UIEventInit {
altKey?: boolean
ctrlKey?: boolean
metaKey?: boolean
modifierAltGraph?: boolean
modifierCapsLock?: boolean
modifierFn?: boolean
modifierFnLock?: boolean
modifierHyper?: boolean
modifierNumLock?: boolean
modifierScrollLock?: boolean
modifierSuper?: boolean
modifierSymbol?: boolean
modifierSymbolLock?: boolean
shiftKey?: boolean
}

interface WheelEvent extends MouseEvent {
readonly deltaMode: number
readonly deltaX: number
readonly deltaY: number
readonly deltaZ: number
readonly DOM_DELTA_PIXEL: 0x00
readonly DOM_DELTA_LINE: 0x01
readonly DOM_DELTA_PAGE: 0x02
}

declare var WheelEvent: {
prototype: WheelEvent
new(type: string, eventInitDict?: WheelEventInit): WheelEvent
readonly DOM_DELTA_PIXEL: 0x00
readonly DOM_DELTA_LINE: 0x01
readonly DOM_DELTA_PAGE: 0x02
}

interface WheelEventInit extends MouseEventInit {
deltaMode?: number
deltaX?: number
deltaY?: number
deltaZ?: number
}

interface InputEvent extends UIEvent {
readonly data: string | null
readonly dataTransfer: DataTransfer | null
readonly inputType: string
readonly isComposing: boolean
getTargetRanges(): StaticRange[]
}

declare var InputEvent: {
prototype: InputEvent
new(type: string, eventInitDict?: InputEventInit): InputEvent
}

interface InputEventInit extends UIEventInit {
data?: string | null
dataTransfer?: DataTransfer | null
inputType?: string
isComposing?: boolean
targetRanges?: StaticRange[]
}

interface KeyboardEvent extends UIEvent {
readonly altKey: boolean
/** @deprecated */
readonly charCode: number
readonly code: string
readonly ctrlKey: boolean
readonly isComposing: boolean
readonly key: string
/** @deprecated */
readonly keyCode: number
readonly location: number
readonly metaKey: boolean
readonly repeat: boolean
readonly shiftKey: boolean
getModifierState(keyArg: string): boolean
/** @deprecated */
initKeyboardEvent(typeArg: string, bubblesArg?: boolean, cancelableArg?: boolean, viewArg?: Window | null, keyArg?: string, locationArg?: number, ctrlKey?: boolean, altKey?: boolean, shiftKey?: boolean, metaKey?: boolean): void
readonly DOM_KEY_LOCATION_STANDARD: 0x00
readonly DOM_KEY_LOCATION_LEFT: 0x01
readonly DOM_KEY_LOCATION_RIGHT: 0x02
readonly DOM_KEY_LOCATION_NUMPAD: 0x03
}

declare var KeyboardEvent: {
prototype: KeyboardEvent
new(type: string, eventInitDict?: KeyboardEventInit): KeyboardEvent
readonly DOM_KEY_LOCATION_STANDARD: 0x00
readonly DOM_KEY_LOCATION_LEFT: 0x01
readonly DOM_KEY_LOCATION_RIGHT: 0x02
readonly DOM_KEY_LOCATION_NUMPAD: 0x03
}

interface KeyboardEventInit extends EventModifierInit {
/** @deprecated */
charCode?: number
code?: string
isComposing?: boolean
key?: string
/** @deprecated */
keyCode?: number
location?: number
repeat?: boolean
}

interface CompositionEvent extends UIEvent {
readonly data: string
/** @deprecated */
initCompositionEvent(typeArg: string, bubblesArg?: boolean, cancelableArg?: boolean, viewArg?: WindowProxy | null, dataArg?: string): void
}

declare var CompositionEvent: {
prototype: CompositionEvent
new(type: string, eventInitDict?: CompositionEventInit): CompositionEvent
}

interface CompositionEventInit extends UIEventInit {
data?: string
}

interface MutationEvent extends Event {
/** @deprecated */
readonly attrChange: number
/** @deprecated */
readonly attrName: string
/** @deprecated */
readonly newValue: string
/** @deprecated */
readonly prevValue: string
/** @deprecated */
readonly relatedNode: Node | null
/** @deprecated */
initMutationEvent(typeArg: string, bubblesArg?: boolean, cancelableArg?: boolean, relatedNodeArg?: Node | null, prevValueArg?: string, newValueArg?: string, attrNameArg?: string, attrChangeArg?: number): void
readonly MODIFICATION: 1
readonly ADDITION: 2
readonly REMOVAL: 3
}

/** @deprecated */
declare var MutationEvent: {
prototype: MutationEvent
new(): MutationEvent
readonly MODIFICATION: 1
readonly ADDITION: 2
readonly REMOVAL: 3
}

链接

Drag and Drop API

Drag and Drop API 允许网页应用使用拖放功能

拖放事件

dragstart 事件在用户开始拖动项目时在拖动项目上触发

drag 事件在用户拖动的项目正被拖动时在拖动项目上触发

dragend 事件在用户结束拖动项目时在拖动项目上触发

dragenter 事件在用户拖动的项目进入放置目标时在当前放置目标上触发

dragover 事件在用户拖动的项目在放置目标上移动时在当前放置目标上触发

dragleave 事件在用户拖动的项目离开放置目标时在之前放置目标上触发

drop 事件在用户拖动的项目放置于放置目标上时在当前放置目标上触发

以上事件均返回一个 DragEvent 事件,并暴露在 Window 接口、Document 接口及 HTMLElement 接口、MathMLElement 接口、SVGElement 接口之上

DragEvent 事件继承自 MouseEvent 事件
DragEvent 事件的 dataTransfer 只读属性返回一个 DataTransfer 实例,代表整个拖放过程的数据

特别注意的是,从设备中向浏览器中拖放文件吧不会触发 dragstart 事件与 dragend 事件

拖放元素

将元素的 draggable 属性设为 true 并指定 dragstart 事件的事件监听器以允许元素被拖动

draggable 属性的默认值为 auto

默认行为是除了 <img> 元素和 <a> 元素及被选中的文字允许拖放外,其余元素不允许拖放

1
<p id="el" draggable="true">this p element is draggable</p>

拖放数据

一般在 dragstart 事件中设置拖放过程中传递的数据(仅该事件允许设置数据)

DataTransfer 接口的 clearData() 方法用于清空拖放过程中的指定类型或所有数据

方法接收一个 string 可选参数,代表数据的类型

DataTransfer 接口的 setData() 方法用于设置拖放过程中传递的数据

方法接收两个 string 参数,分别代表数据的类型和数据的内容

1
2
3
4
5
6
7
el.addEventListener('dragstart', (e) => {
e.dataTransfer.clearData()

e.dataTransfer.setData('text/plain', e.target.innerText)
e.dataTransfer.setData('text/html', e.target.innerHTML)
e.dataTransfer.setData('text/uri-list', e.target.ownerDocument.location.href)
})

拖放效果

DataTransfer 接口的 effectAllowed 属性用于指定允许的拖放效果

该属性仅允许在 dragstart 事件中设置

可能的值包括 uninitialized none copy link move copyLink linkMove copyMoveall

DataTransfer 接口的 dropEffect 属性用于指定拖放过程的效果

它主要影响拖动过程中鼠标图标的展示

默认值 none 指示没有明显效果

copy 指示值将从当前位置复制至目标位置

move 指示值将从当前位置移动至目标位置

link 指示值将从当前位置链接至目标位置

1
2
3
el.addEventListener('dragstart', (e) => {
e.dataTransfer.dropEffect = 'move'
})

DataTransfer 接口的 setDragImage() 方法用于设置拖放过程中展示的图片

方法接收一个 Element 参数,代表自定义的拖动时展示的图片,可以是 <img> <canvas> 或其他类型元素

方法接收两个 number 参数,代表自定义的图片在 x 坐标和 y 坐标上的相对位置

需要注意的是,传递的元素若在文档中,则需可见;因此可创建一个 offscreen 的元素专供该方法使用

1
2
3
el.addEventListener('dragstart', (e) => {
e.dataTransfer.setDragImage(new Image(), 0, 0)
})

拖放目标

向元素指定 dragenter 事件及 dragover 事件或 drop 事件的事件监听器以允许元素作为拖放目标放置

并根据需要取消事件的默认行为

拖放完成

一般在 drop 事件中获取拖放过程中传递的数据(除此之外仅 dragstart 事件允许读取数据)

DataTransfer 接口的 getData() 方法用于根据指定的数据类型获取拖放过程中传递的数据

方法接收一个 string 参数,代表数据的类型

方法返回一个 string 参数,代表找到的数据;未找到返回空字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
el.addEventListener('dragover', (e) => {
e.preventDefault()

e.dataTransfer.dropEffect = 'move'
})

el.addEventListener('drop', (e) => {
e.preventDefault()

const id = e.dataTransfer.getData('text/plain')
e.target.appendChild(document.getElementById(id))

for (const item of e.dataTransfer.items) {
//
}

for (const item of e.dataTransfer.files) {
//
}
})

DataTransfer 接口的 types 只读属性返回一个只读字符串数组,代表在 dragstart 事件中设置的所有可用的数据的类型

DataTransfer 接口的 items 只读属性返回一个 DataTransferItemList,代表在 dragstart 事件中设置的所有可用的数据

DataTransfer 接口的 files 只读属性返回一个 FileList,代表被拖放入的文件列表

其他

DataTransferItemList 接口是 DataTransferItem 的列表,包含 length 属性,add() 方法 clear() 方法 remove() 方法,同时支持以方括号运算符访问

DataTransferItem 接口代表一个拖放数据,包含 kind 属性 type 属性,getAsFile() 方法 getAsString() 方法

拖放数据类型

数据类型 最佳实践
文字 text/plain
链接 text/uri-list & text/plain
HTML 或 XML 文本 text/html & text/plain
图片 image/jpeg 或 image/png 或 image/gif & application/x-moz-file & text/uri-list & text/plain
DOM 节点 application/x-moz-node & text/plain

示例

draggable text

drag zone

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
interface DataTransfer {
dropEffect: "none" | "copy" | "link" | "move"
effectAllowed: "none" | "copy" | "copyLink" | "copyMove" | "link" | "linkMove" | "move" | "all" | "uninitialized"
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
}

declare var DataTransfer: {
prototype: DataTransfer
new(): DataTransfer
}

interface DataTransferItem {
readonly kind: string
readonly type: string
getAsFile(): File | null
getAsString(callback: FunctionStringCallback | null): void
webkitGetAsEntry(): FileSystemEntry | null
}

declare var DataTransferItem: {
prototype: DataTransferItem
}

interface DataTransferItemList {
readonly length: number
add(data: string, type: string): DataTransferItem | null
add(data: File): DataTransferItem | null
clear(): void
remove(index: number): void
[index: number]: DataTransferItem
}

declare var DataTransferItemList: {
prototype: DataTransferItemList
}

interface DragEvent extends MouseEvent {
readonly dataTransfer: DataTransfer | null
}

declare var DragEvent: {
prototype: DragEvent
new(type: string, eventInitDict?: DragEventInit): DragEvent
}

interface DragEventInit extends MouseEventInit {
dataTransfer?: DataTransfer | null
}

interface GlobalEventHandlersEventMap {
"drag": DragEvent;
"dragend": DragEvent;
"dragenter": DragEvent;
"dragleave": DragEvent;
"dragover": DragEvent;
"dragstart": DragEvent;
"drop": DragEvent;
}

interface GlobalEventHandlers {
ondrag: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null
ondragend: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null
ondragenter: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null
ondragleave: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null
ondragover: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null
ondragstart: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null
ondrop: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null
}

链接

DeviceOrientation Event

DeviceOrientation Event 允许监测设备的物理朝向及物理加速度

物理加速度

Window 接口的 devicemotion 事件在设备的物理加速度(及旋转速率)变化时定期触发,返回一个 DeviceMotionEvent 事件

1
2
3
window.addEventListener('devicemotion', (e) => {
//
})

DeviceMotionEvent 接口的 acceleration 属性返回一个 DeviceMotionEventAcceleration 实例,表示设备的加速度(包含 x y z 三个参数)

DeviceMotionEvent 接口的 accelerationIncludingGravity 属性返回一个 DeviceMotionEventAcceleration 实例,表示受重力影响下设备的加速度(包含 x y z 三个参数)

DeviceMotionEvent 接口的 rotationRate 属性返回一个 DeviceMotionEventRotationRate 实例,表示设备的旋转角速度(包含 alpha beta gamma 三个参数)

DeviceMotionEvent 接口的 interval 属性返回一个 number,表示设备更新数据的间隔

物理朝向

Window 接口的 deviceorientation 事件在设备的物理朝向变化时定期触发,返回一个 DeviceOrientationEvent 事件

Window 接口的 deviceorientationabsolute 事件在设备的绝对物理朝向变化时触发,返回一个 DeviceOrientationEvent 事件

1
2
3
4
5
6
7
window.addEventListener('deviceorientation', (e) => {
//
})

window.addEventListener('deviceorientationabsolute', (e) => {
//
})

DeviceOrientationEvent 接口的 absolute 属性返回一个 boolean,表明当前是否提供绝对方向数据(地球坐标系),或设备任意坐标系

DeviceOrientationEvent 接口的 alpha 属性返回一个范围在 0360 之间的 number,表明设备绕 z 轴旋转的角度

DeviceOrientationEvent 接口的 beta 属性返回一个范围在 -180180 之间的 number,表明设备绕 x 轴旋转的角度

DeviceOrientationEvent 接口的 gamma 属性返回一个范围在 -9090 之间的 number,表明设备绕 y 轴旋转的角度

权限策略

该 API 调用受到 accelerometer gyroscope magnetometer 等权限策略的控制,可以通过 Permissions-Policy 响应头指定,或通过 <iframe> 标签的 allow 属性指定

默认值均是 self,即允许允许在顶层浏览上下文及其同源的嵌入浏览上下文中使用

deviceorientation 事件与 devicemotion 事件需要 accelerometer gyroscope 权限策略,而 deviceorientationabsolute 事件需要 accelerometer gyroscope magnetometer 权限策略

权限 API

该 API 调用时需要用户授予 accelerometer gyroscope magnetometer 等权限,可以调用 Permission.query() 方法检查用户是否已授予了该权限

deviceorientation 事件与 devicemotion 事件需要授予 accelerometer gyroscope 权限,而 deviceorientationabsolute 事件需要授予 accelerometer gyroscope magnetometer 权限

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
interface Window {
ondeviceorientation: ((this: Window, ev: DeviceOrientationEvent) => any) | null
ondeviceorientationabsolute: ((this: Window, ev: DeviceOrientationEvent) => any) | null
ondevicemotion: ((this: Window, ev: DeviceMotionEvent) => any) | null
}

interface DeviceOrientationEvent extends Event {
readonly alpha: number | null
readonly beta: number | null
readonly gamma: number | null
readonly absolute: boolean
}

declare var DeviceOrientationEvent: {
new (type: string, eventInitDict?: DeviceOrientationEventInit): DeviceOrientationEvent
prototype: DeviceOrientationEvent

requestPermission(): Promise<PermissionState>
}

interface DeviceOrientationEventInit extends EventInit {
alpha?: number | null
beta?: number | null
gamma?: number | null
absolute?: boolean
}

interface DeviceMotionEventAcceleration {
readonly x: number | null
readonly y: number | null
readonly z: number | null
}

interface DeviceMotionEventRotationRate {
readonly alpha: number | null
readonly beta: number | null
readonly gamma: number | null
}

interface DeviceMotionEvent extends Event {
readonly acceleration: DeviceMotionEventAcceleration | null
readonly accelerationIncludingGravity: DeviceMotionEventAcceleration | null
readonly rotationRate: DeviceMotionEventRotationRate | null
readonly interval: number
}

declare var DeviceMotionEvent: {
new (type: string, eventInitDict?: DeviceMotionEventInit): DeviceMotionEvent
prototype: DeviceMotionEvent

requestPermission(): Promise<PermissionState>
}

interface DeviceMotionEventAccelerationInit {
x?: number | null
y?: number | null
z?: number | null
}

interface DeviceMotionEventRotationRateInit {
alpha?: number | null
beta?: number | null
gamma?: number | null
}

interface DeviceMotionEventInit extends EventInit {
acceleration?: DeviceMotionEventAccelerationInit
accelerationIncludingGravity?: DeviceMotionEventAccelerationInit
rotationRate?: DeviceMotionEventRotationRateInit
interval?: number
}

链接

Contact Picker API

Contact Picker API 允许用户从通讯录选择记录并与网页应用分享

该 API 通过 ContactsManager 接口使用,并通过 Navigator.contacts 向用户暴露

检测支持的参数

调用 ContactsManager 接口的 getProperties() 方法以获取当前设备支持的参数列表

方法返回 Promise 的字符串列表,值具体在 "address" "email" "icon" "name" "tel" 之中,代表设备通讯录支持的参数类型

1
const properties = await navigator.contacts.getProperties()

获取通讯录记录

调用 ContactsManager 接口的 select() 方法以获取通讯录记录

方法接收一个字符串数组,值需要在 "address" "email" "icon" "name" "tel" 之中,代表需要获取的通讯录记录的参数

方法可以接收一个可选的配置项参数,其 multiple 选项指定是否支持选择多条记录,默认 false

方法返回一个对象数组,代表获取到的通讯录记录;数组各项均包含 "address" "email" "icon" "name" "tel" 字段

若方法未在顶层浏览上下文调用,或当前已有其他该方法的调用,或启动通讯录选择器失败,则抛出 InvalidStateError 异常

若方法非因为用户交互调用,则抛出 SecurityError 异常

若方法传入一个空数组,或传入的数组任一项当前设备不支持,则抛出 TypeError 异常

1
const contacts = await navigator.contacts.select(properties)

通讯录记录细节

  • "address" 返回 ContactAddress 数组,代表各记录的地址

  • "email" 返回 string 数组,代表各记录的邮箱

  • "icon" 返回 Blob 数组,代表各记录的图标

  • "name" 返回 string 数组,代表各记录的名称

  • "tel" 返回 string 数组,代表各记录的电话

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
interface Navigator {
readonly contacts: ContactsManager
}

type ContactProperty = "address" | "email" | "icon" | "name" | "tel"

interface ContactAddress {
toJSON(): Object
readonly city: string
readonly country: string
readonly dependentLocality: string
readonly organization: string
readonly phone: string
readonly postalCode: string
readonly recipient: string
readonly region: string
readonly sortingCode: string
readonly addressLine: string[]
}

interface ContactInfo {
address: ContactAddress[]
email: string[]
icon: Blob[]
name: string[]
tel: string[]
}

interface ContactsSelectOptions {
multiple?: boolean
}

interface ContactsManager {
getProperties(): Promise<ContactProperty[]>
select(properties: ContactProperty[], options?: ContactsSelectOptions): Promise<ContactInfo[]>
}

链接

Cookie Store API

Cookie Store API 提供了异步地管理 Cookie 的方式,同时允许在 ServiceWorker 中使用

在该 API 之前,使用 cookie 的方式是通过读写 document.cookie 属性,但其是单线程同步的,可能会阻碍事件循环;并且其无法在 ServiceWorker 中使用

Window 环境中通过 Window 接口的 cookieStore 属性使用

Window 环境中通过 ServiceWorkerGlobalScope 接口的 cookieStore 属性使用

1
2
window.cookieStore
self.cookieStore

CookieStore 接口的 get() 方法用于获取单条 Cookie

方法接收一个字符串,代表 Cookie 的名称;或接收一个对象,其 name 参数与 url 参数均需指定

方法返回一个 Promise 的 CookieListItem 结构,代表匹配到的 Cookie 信息;反之返回 undefined

1
2
3
4
5
const cookie = await window.cookieStore.get('cookie')
const cookie = await window.cookieStore.get({
name: 'cookie',
url: 'https://www.example.com',
})

CookieStore 接口的 getAll() 方法用于获取单条 Cookie

方法接收一个字符串,代表 Cookie 的名称;或接收一个对象,其 name 参数与 url 参数均需指定

方法返回一个 Promise 的 CookieList 结构,代表匹配到的所有 Cookie

1
2
3
4
5
const cookies = await window.cookieStore.getAll('key')
const cookies = await window.cookieStore.getAll({
name: 'key',
url: 'https://www.example.com',
})

CookieStore 接口的 set() 方法用于设置 Cookie

方法可以接收两个字符串,分别代表 Cookie 的名称与值;亦可以接收一个配置项,各项如下所示:

  • name 属性必需
  • value 属性必需
  • expires 属性可选,默认 null
  • domain 属性可选,默认 null
  • path 属性可选,默认 "/"
  • sameSite 属性可选,默认 "strict"
  • partitioned 属性可选,默认 false

方法返回一个 Promise

1
2
3
4
5
6
7
8
9
10
await window.cookieStore.set('key', 'value')
await window.cookieStore.set({
name: 'key',
value: 'value',
expires: null,
domain: null,
path: '/',
sameSite: 'strict',
partitioned: false,
})

CookieStore 接口的 delete() 方法用于移除 Cookie

方法接收一个字符串,代表 Cookie 的名称;或接收一个对象,其 name 参数必需指定;path 参数 domain 参数 partitioned 参数可选

方法返回一个 Promise

1
2
3
4
5
6
7
await window.cookieStore.delete('key')
await window.cookieStore.delete({
name: 'key',
path: '/',
domain: null,
partitioned: false,
})

CookieList 结构相当于 CookieListItem 结构的数组

CookieListItem 结构反映了 Cookie 的详细信息

CookieListItem 结构的 name 属性返回一个字符串,代表 Cookie 的名称

CookieListItem 结构的 value 属性返回一个字符串,代表 Cookie 的值

CookieListItem 结构的 domain 属性返回一个字符串,代表 Cookie 的域,该属性可能返回 null

CookieListItem 结构的 path 属性返回一个字符串,代表 Cookie 的路径

CookieListItem 结构的 expires 属性返回一个数字,代表 Cookie 的过期时间,该属性可能返回 null

CookieListItem 结构的 secure 属性返回一个布尔值,代表 Cookie 的严格上下文策略

CookieListItem 结构的 sameSite 属性返回一个字符串,代表 Cookie 的同域策略,为 "strict" "lax" "none" 之一

CookieListItem 结构的 partitioned 属性返回一个布尔值,代表 Cookie 的分区策略

CookieStore 接口的 change 事件在任一 Cookie 变化时触发,返回一个 CookieChangeEvent 事件

CookieChangeEvent 接口的 changed 属性返回一个 CookieListItem 结构只读数组,表示被修改的 Cookie

CookieChangeEvent 接口的 deleted 属性返回一个 CookieListItem 结构只读数组,表示被移除的 Cookie

1
2
3
4
window.cookieStore.addEventListener('change', (e) => {
e.changed
e.deleted
})

但该事件仅在 Window 环境中可用

ServiceWorker 中允许通过 CookieStoreManager 接口的方法动态控制 Cookie 变化的订阅

可通过 ServiceWorkerRegistration 接口的 cookies 属性获取到 CookieStoreManager 实例

1
self.registration.cookies

获取订阅

CookieStoreManager 接口的 getSubscriptions() 方法用于获取当前所有的订阅

方法返回一个对象数组,数组各项包含 name 参数和 url 参数

1
const subscriptions = await self.registration.cookies.getSubscriptions()

添加订阅

CookieStoreManager 接口的 subscribe() 方法用于添加订阅

方法传入一个对象数组参数,各项的 name 参数与 url 参数均需指定

方法返回一个 Promise

1
2
3
4
5
6
self.registration.cookies.subscribe([
{
name: 'key',
url: '/',
},
])

移除订阅

CookieStoreManager 接口的 unsubscribe() 方法用于移除订阅

方法传入一个对象数组参数,各项的 name 参数与 url 参数均需指定

方法返回一个 Promise

1
2
3
4
5
6
self.registration.cookies.unsubscribe([
{
name: 'key',
url: '/',
},
])

ServiceWorkerGlobalScope 接口的 cookiechange 事件在订阅的 Cookie 发生变化时触发,返回一个 ExtendableCookieChangeEvent 事件

ExtendableCookieChangeEvent 接口的 changed 属性返回一个 CookieListItem 结构只读数组,表示被修改的 Cookie

ExtendableCookieChangeEvent 接口的 deleted 属性返回一个 CookieListItem 结构只读数组,表示被移除的 Cookie

1
2
3
4
self.addEventListener('cookiechange', (e) => {
e.changed
e.deleted
})

示例

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
interface CookieStore extends EventTarget {
get(name: string): Promise<CookieListItem | undefined>
get(options?: CookieStoreGetOptions): Promise<CookieListItem | undefined>

getAll(name: string): Promise<CookieList>
getAll(options?: CookieStoreGetOptions): Promise<CookieList>

set(name: string, value: string): Promise<undefined>
set(options: CookieInit): Promise<undefined>

delete(name: string): Promise<undefined>
delete(options: CookieStoreDeleteOptions): Promise<undefined>

onchange: ((this: CookieStore, ev: CookieChangeEvent) => any) | null
}

interface CookieStoreGetOptions {
name: string
url: string
}

enum CookieSameSite {
"strict",
"lax",
"none"
}

interface CookieInit {
name: string
value: string
expires?: DOMHighResTimeStamp | null
domain?: string | null
path?: string
sameSite?: CookieSameSite
partitioned?: boolean
}

interface CookieStoreDeleteOptions {
name: string
domain?: string | null
path?: string
partitione?: boolean
}

interface CookieListItem {
name: string
value: string
domain?: string
path: string
expires?: DOMHighResTimeStamp
secure: boolean
sameSite: CookieSameSite
partitioned: boolean
}

type CookieList = Array<CookieListItem>

interface CookieStoreManager {
subscribe(subscriptions: Array<CookieStoreGetOptions>): Promise<undefined>
getSubscriptions(): Promise<Array<CookieStoreGetOptions>>
unsubscribe(subscriptions: Array<CookieStoreGetOptions>): Promise<undefined>
}

interface ServiceWorkerRegistration {
readonly cookies: CookieStoreManager
}

interface CookieChangeEvent extends Event {
constructor(type: string, eventInitDict?: CookieChangeEventInit)
readonly changed: ReadonlyArray<CookieListItem>
readonly deleted: ReadonlyArray<CookieListItem>
}

interface CookieChangeEventInit extends EventInit {
changed: CookieList
deleted: CookieList
}

interface ExtendableCookieChangeEvent extends ExtendableEvent {
constructor(type: string, eventInitDict?: ExtendableCookieChangeEventInit)
readonly changed: ReadonlyArray<CookieListItem>
readonly deleted: ReadonlyArray<CookieListItem>
}

interface ExtendableCookieChangeEventInit extends ExtendableEventInit {
changed: CookieList
deleted: CookieList
}

interface Window {
readonly cookieStore: CookieStore
}

interface ServiceWorkerGlobalScope {
readonly cookieStore: CookieStore

oncookiechange: ((this: ServiceWorkerGlobalScope, ev: ExtendableCookieChangeEvent) => any) | null
}

链接

User-Agent Client Hints API

User-Agent Client Hints API 扩展了 HTTP Client Hints 以提供允许 JavaScript API 读取浏览器和操作系统信息的方式

该 API 通过 NavigatorUAData 接口提供相关功能,并通过 navigator.userAgentData 暴露

读取基本信息

NavigatorUAData 接口的 brands 属性返回一个 object array,代表浏览器信息,各项包含 brand 属性和 version 属性,均为字符串,分别代表浏览器的名称和版本

NavigatorUAData 接口的 mobile 属性返回一个 boolean,代表当前设备是否为移动端设备

NavigatorUAData 接口的 platform 属性返回一个 string,代表当前设备的操作系统名称

NavigatorUAData 接口的 toJSON() 方法返回一个 JSON 对象,表示可序列化的浏览器的信息

读取细节信息

NavigatorUAData 接口的 getHighEntropyValues() 方法获取浏览器的详细信息

方法传入一个 string array 参数,值允许为 architecture bitness formFactor fullVersionList model platformVersion uaFullVersion wow64 之一

方法返回一个 Promise 的 object,根据参数不同,值可能包含 architecture bitness brands formFactor fullVersionList mobile model platform platformVersion uaFullVersion wow64 等项

方法可能抛出 NotAllowedError,若用户代理认为任一参数不应当被返回

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
interface NavigatorUABrandVersion {
brand: string
version: string
}

interface UADataValues {
architecture: string
bitness: string
brands: NavigatorUABrandVersion[]
formFactor: string[]
fullVersionList: NavigatorUABrandVersion[]
model: string
mobile: boolean
platform: string
platformVersion: string
/** @deprecated */ uaFullVersion: string
wow64: boolean
}

interface UALowEntropyJSON {
brands: NavigatorUABrandVersion[]
mobile: boolean
platform: string
}

interface NavigatorUAData {
readonly brands: ReadonlyArray<>
readonly mobile: boolean
readonly platform: string
getHighEntropyValues(hints: string[]): Promise<UADataValues>
toJSON(): UALowEntropyJSON
}

interface NavigatorUA {
readonly userAgentData: NavigatorUAData
}

interface Navigator extends NavigatorUA {}
interface WorkerNavigator extends NavigatorUA {}

链接

Prioritized Task Scheduling API

Prioritized Task Scheduling API 提供了标准化的任务的优先级排序的方式

通过该 API 能够设定和改变任务优先级,延迟将任务添加至调度程序,终止任务或监听任务优先级改变事件

该 API 定义的任务优先级分类是比较粗糙的,开发者可以根据需要自定义更加细化的任务优先级分类

该 API 通过 scheduler 全局属性暴露 Scheduler 实例使用

创建任务

调用 Scheduler 接口的 postTask() 方法以创建任务

方法传入一个回调函数,代表该任务的内容

方法亦传入一个可选的配置项:

priority 选项指定任务的优先级,接收一个字符串,值为 user-blocking user-visible background,默认值为 user-visible

signal 选项提供任务的控制器,可以为一个 TaskSignalAbortSignal,用于终止任务或调整任务优先级

delay 选项指定任务添加至任务队列的延迟时间,默认为 0

方法的返回值是一个 Promise,其具体值取决于回调函数的返回值或抛出的异常

1
2
3
4
5
6
7
8
9
scheduler.postTask(() => 'a task', {
priority: 'user-visible',
})
.then((result) => {
console.log('execute success with:', result)
})
.catch((error) => {
console.error('execute fail with:', error)
})

任务优先级

user-blocking 优先级最高,表明该任务可能阻止用户与网页交互

user-visible 优先级一般,表面该任务会让用户察觉但不致阻止用户与网页交互

background 优先级最低,表面该用户不会对用户产生影响

任务的执行顺序优先按任务优先级执行,其次按照定义顺序执行

延迟任务

在任务创建时向 delay 选项传入一个正整数

1
2
3
scheduler.postTask(() => 'a task', {
delay: 1000,
})

终止任务

在任务创建时向 signal 选项传入一个 TaskSignalAbortSignal 参数

在必要时候调用 AbortControllerTaskControllerabort() 方法以终止任务

1
2
3
4
5
6
7
const controllor = new AbortController()

scheduler.postTask(() => 'a task', {
signal: controllor.signal,
})

controllor.abort()

更改任务优先级

在任务创建时向 signal 选项传入一个 TaskSignal 参数

在必要时候调用 TaskControllersetPriority() 方法以重新设置任务的优先级

1
2
3
4
5
6
7
const controller = new TaskController()

scheduler.postTask(() => 'a task', {
signal: controllor.signal,
})

controller.setPriority("background")

调用 TaskController() 构造函数创建一个 TaskController 实例

构造函数接收一个可选的配置项,其参数 priority 指定初始的任务优先级,默认值为 "user-visible"

TaskController 接口的 signal 属性返回一个 TaskSignal 实例,反映与当前 TaskController 相对的 TaskSignal 实例

使用 TaskController 接口的 setPriority() 方法更改当前任务的优先级

方法接收一个字符串,代表任务新的优先级

此外,TaskSignal 实例的 priority 只读属性反映 signal 对应的任务的优先级

TaskSignal 实例的 prioritychange 事件在 signal 对应的任务的优先级改变时触发,返回一个 TaskPriorityChangeEvent 事件,其 previousPriority 参数指定改变前的任务的优先级

1
2
3
4
controller.signal.addEventListener("prioritychange", (e) => {
console.log('previous', e.previousPriority)
console.log('current', controller.signal.priority /* 或 e.target.priority */)
})

输入检测

Scheduling 接口提供了管理计划任务的方法,通过 navigator.scheduling 属性暴露

Scheduling 接口的 isInputPending() 方法判断当前事件队列中是否有输入事件,该方法可以用于判断用户是否在与网页交互

方法接收一个可选的配置项,其参数 includeContinuous 代表是否需考虑持续性触发的事件

方法返回一个 boolean

1
2
3
const flag = navigator.scheduling.isInputPending({
includeContinuous: false,
})

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
type TaskPriority = "user-blocking" | "user-visible" | "background"

interface SchedulerPostTaskOptions {
signal?: AbortSignal
priority?: TaskPriority
delay?: number
}

type SchedulerPostTaskCallback = Function

interface Scheduler {
postTask(callback: SchedulerPostTaskCallback, options?: SchedulerPostTaskOptions): Promise<any>
}

interface TaskPriorityChangeEvent extends Event {
constructor(type: string, priorityChangeEventInitDict: TaskPriorityChangeEventInit)

readonly previousPriority: TaskPriority
}

interface TaskPriorityChangeEventInit extends EventInit {
previousPriority: TaskPriority
}

interface TaskControllerInit {
priority: TaskPriority
}

interface TaskController extends AbortController {
constructor(init: TaskControllerInit)

setPriority(priority: TaskPriority): void
}

interface TaskSignal extends AbortSignal {
readonly priority: TaskPriority

onprioritychange: ((this: TaskSignal, ev: TaskPriorityChangeEvent) => any) | null
}

interface WindowOrWorkerGlobalScope {
readonly scheduler: Scheduler
}

interface Navigator {
readonly scheduling: Scheduling
}

interface Scheduling {
isInputPending(isInputPendingOptions?: IsInputPendingOptions): boolean
}

interface IsInputPendingOptions {
includeContinuous: boolean
}

链接

Screen Orientation API

Screen Orientation API 允许获取屏幕朝向信息和监听屏幕朝向信息变化

该 API 通过 ScreenOrientation 接口提供服务,并通过 window.screen.orientation 暴露该接口的实例

获取屏幕朝向信息

ScreenOrientation 接口的 type 只读属性返回一个字符串,表示当前屏幕朝向的类型,值为 portrait-primary portrait-secondary landscape-primary landscape-secondary 之一

ScreenOrientation 接口的 angel 只读属性返回一个数字,表示当前屏幕朝向的角度

监听屏幕朝向信息变化

ScreenOrientation 接口的 change 事件在当前屏幕朝向变化时触发,返回一个 Event

锁定屏幕朝向信息变化

ScreenOrientation 接口的 lock() 方法锁定系统默认的屏幕朝向变化

方法传入一个字符串参数,代表锁定的类型,值可以为 any landscape landscape-primary landscape-secondary natural portrait portrait-primary portrait-secondary 之一

方法返回一个 Promise

1
await window.screen.orientation.lock('any')

ScreenOrientation 接口的 unlock() 方法解除锁定系统默认的屏幕朝向变化

1
window.screen.orientation.unlock()

沙箱策略

该 API 在 <iframe> 标签中的调用受到 allow-orientation-lock 沙箱策略的控制,需要将 sandbox 的属性指定为允许

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Screen {
readonly orientation: ScreenOrientation
}

interface ScreenOrientation extends EventTarget {
readonly angle: number
readonly type: OrientationType
onchange: ((this: ScreenOrientation, ev: Event) => any) | null
lock(orientation: OrientationLockType): Promise<void>
unlock(): void
}

type OrientationLockType = "any" | "landscape" | "landscape-primary" | "landscape-secondary" | "natural" | "portrait" | "portrait-primary" | "portrait-secondary"

type OrientationType = "landscape-primary" | "landscape-secondary" | "portrait-primary" | "portrait-secondary"

链接


:D 一言句子获取中...