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"

链接

Storage Access API

Storage Access API 允许第三方上下文中跨站内容(如嵌入 <iframe> 标签中)获取对未分区 cookie (即以传统方式存储的 cookie,仅允许在第一方上下文(直接加载在浏览器选项卡中)中访问)的访问权限

通常出于隐私性考虑,默认用户代理会阻止第三方上下文中跨站内容对未分区 cookie 的访问,但一些场景下,开发者期望启用第三方内容对未分区 cookie 访问的功能

Document 接口的 requestStorageAccess() 方法用于请求文档访问第三方 Cookie 权限

方法返回一个 Promise

方法可能抛出 InvalidStateError 异常,若当前文档尚未进入 active 状态

方法可能抛出 NotAllowedError 异常,若当前文档未处于安全上下文状态,或受权限策略阻止,或当前文档或其顶层文档的 origin 为空,或受 <iframe> 标签沙箱策略的限制,或请求权限被拒绝(如之前已被拒绝,或当前未处于用户激活状态且未授予权限)

1
2
3
4
5
document.requestStorageAccess().then(() => {
console.log('granted')
}).catch(() => {
console.log('denied')
})

Document 接口的 hasStorageAccess() 方法用于检测文档访问第三方 Cookie 权限

方法返回一个 Promise 的 boolean,表示是否已允许文档访问第三方 Cookie

方法可能抛出 InvalidStateError 异常,若当前文档尚未进入 active 状态

1
2
3
4
5
6
7
document.hasStorageAccess().then((sym) => {
if (sym) {
console.log('has access')
} else {
console.log('not has access')
}
})

沙箱策略

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

(同时为使用该 API,也需要指定 allow-scriptsallow-same-origin 沙箱策略)

权限策略

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

默认值是 *,即允许任意源的浏览上下文使用该 API

权限 API

该 API 调用需要用户授予 storage-access 权限,可以调用 Permission.query() 方法检查用户是否已授予了该权限

类型

1
2
3
4
interface Document {
hasStorageAccess(): Promise<boolean>
requestStorageAccess(): Promise<undefined>
}

链接

Storage API

Storage API 提供了多个存储 API 的共享的管理方法,允许获取存储的信息并控制存储的清除策略

通常管理的存储包括包括 IndexedDB 、 Cache Storage 、 ServiceWorker 脚本 、 Origin Private File System

该 API 通过 StorageManager 接口提供相关方法,并通过 navigator.storage 对开发者暴露

获取存储信息

StorageManager 接口的 estimate() 方法用于获取用户代理存储的信息

方法返回一个 Promise 的对象,其 usage 属性返回一个数字,代表当前网页使用的存储的大小;quota 属性返回一个数字,代表当前用户代理能为网页提供的存储的大小

1
2
3
4
const storage = await navigator.storage.estimate()

console.log('usage', storage.usage)
console.log('quota', storage.quota)

设置存储清除策略

默认存储策略为 "best-effort",即用户代理会尽可能地保存存储数据,但若需要清除时不会告知用户

而存储策略 "persistent",即用户代理会尽可能地保存存储数据,优先清除存储策略设置为 "best-effort" 的存储,但若需要清除时会主动告知用户

StorageManager 接口的 persisted() 方法检测是否已将存储策略更改为 "persistent",返回一个 Promise 的 boolean

StorageManager 接口的 persist() 方法用于将存储策略更改为 "persistent",返回一个 Promise 的 boolean

1
2
3
4
5
6
7
const result = await navigator.storage.persist()

if (result) {
console.log('persisted')
} else {
console.log('not persisted')
}

权限 API

该 API 调用需要用户 persistent-storage 授予权限,可以调用 Permission.query() 方法检查用户是否已授予了该权限

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface NavigatorStorage {
readonly storage: StorageManager
}

interface Navigator extends NavigatorStorage {}

interface WorkerNavigator extends NavigatorStorage {}

interface StorageManager {
persisted(): Promise<boolean>
persist(): Promise<boolean>

estimate(): Promise<StorageEstimate>
}

interface StorageEstimate {
usage: number
quota: number
}

链接

Permissions API

Permissions API 提供了编程式地检测当前浏览上下文 API 权限

可以用于确定对应的 API 是否已授予权限或被拒绝授予权限,或等待授权权限

同时 API 还会考虑其他因素,如若该 API 对 Secure Context 的要求、Permissions-Policy 的限制等待

该 API 通过 Permissions 接口提供相关功能,并通过 Navigator.permissionsWorkerNavigator.permissions 对外暴露 Permissions 实例

检测权限

调用 Permissions 接口的 query() 方法检测给定 API 的权限状态

方法传入一个 permissionDescriptor 的配置项,其唯一参数 name 反映与给定 API 相关的名称

方法返回一个 Promise 的 PermissionStatus,代表与给定 API 相关的权限状态

1
2
3
4
5
const status = await navigator.permissions.query({
name: 'geolocation',
})

console.log(status.state) // 'granted' 'denied' 'prompt'

PermissionStatusstate 属性返回一个 string,代表权限的状态,为 'granted' 'denied' 'prompt' 之一

PermissionStatusname 属性返回一个 string,代表权限的名称,与 query() 方法传入的 name 参数一致

PermissionStatuschange 事件在权限的状态改变时触发,返回一个 Event

权限名称列表

Permission name Permission description
accelerometer Sensor APIs
ambient-light-sensor Sensor APIs
background-sync Background Synchronization API
camera Media Capture and Streams API
clipboard-read Clipboard API
clipboard-write Clipboard API
geolocation Geolocation API
gyroscope Sensor APIs
local-fonts Local Font Access API
magnetometer Sensor APIs
microphone Media Capture and Streams API
midi Web MIDI API
notifications Notifications API
payment-handler Payment Handler API
persistent-storage Storage API
push Push API
speaker-selection Web Audio Output Devices API
storage-access Storage Access API
window-management Window Management API

不同浏览器支持的权限名称参考如下

类型

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
interface Navigator {
readonly permissions: Permissions
}

partial interface WorkerNavigator {
readonly permissions: Permissions
}

interface Permissions {
query(permissionDesc: PermissionDescriptor): Promise<PermissionStatus>
}

interface PermissionDescriptor {
name: string
}

interface PermissionStatus extends EventTarget {
readonly state: PermissionState
readonly name: string

onchange: ((this: PermissionStatus, ev: Event) => any) | null
}

enum PermissionState {
"granted",
"denied",
"prompt",
}

链接


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