Web App Launch Handler API

Web App Launch Handler API 允许管理 Progressive Web App 的启动导航方式(针对已开启的 PWA 应用)

配置启动行为

通过配置 manifest 文件的 launch_handler 字段来管理应用启动行为

该字段的唯一子字段 client_mode 指定应用启动时导航的行为,允许的值如下

  • auto 由用户代理根据平台等因素决定行为,亦是默认值(如移动端使用 navigate-existing,桌面端使用 navigate-new
  • navigate-new 将创建新的浏览上下文以加载目标 URL
  • navigate-existing 最近交互的浏览上下文将聚焦并导航至目标 URL
  • focus-existing 最近交互的浏览上下文将聚焦并用于处理目标 URL(但不会自动导航)

该子字段亦允许指定多个值,以提供后备的执行行为

手动执行启动行为

通过 LaunchQueue 接口使用,该接口实例通过 window.launchQueue 暴露

LaunchQueue 接口的 setConsumer() 方法用于管理应用的启动导航行为

该方法接收一个回调方法,代表在发生应用启动导航行为时需调用的回调方法,并传递一个 LaunchParams 实例

LaunchParams 接口的 targetURL 属性返回一个 string,代表导航的目标 URL,可能为 null

LaunchParams 接口的 files 属性返回一个 FileSystemHandle 数组,代表通过 POST 方法传递的文件

1
2
3
4
5
6
7
8
9
10
11
12
window.launchQueue.setConsumer((launchParams) => {
if (launchParams.targetURL) {
const params = new URL(launchParams.targetURL).searchParams

const track = params.get("track")
if (track) {
audio.src = track
title.textContent = new URL(track).pathname.substr(1)
audio.play()
}
}
})

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type LaunchConsumer = (params: LaunchParams) => any

interface LaunchParams {
readonly targetURL?: string
readonly files: ReadonlyArray<FileSystemHandle>
}

interface LaunchQueue {
setConsumer(consumer: LaunchConsumer): void
}

interface Window {
readonly launchQueue: LaunchQueue
}

链接

File System Access API

File System Access API 允许访问本地文件系统

获取选择资源的文件句柄

使用 Window 接口的 showOpenFilePicker() 方法选取单个或多个文件,返回对应的文件句柄

方法传入一组可选的配置项

  • excludeAcceptAllOption 可选选项指定选择器是否启用筛选文件类型的选项,默认值是 false
  • id 选项用于和当前目录配对,会自动记忆与之相关的目录,后续选择时若指定相同的 id 可以自动打开与之前选择相同的目录,接收一个字符串
  • startIn 选项指定初始打开目录,接收一个 FileSystemHandle 或预设的目录 "desktop" "documents" "downloads" "music" "pictures""videos"
  • multiple 可选选项指定是否允许多选,默认值是 false
  • types 选项指定允许选择的文件类型,传递一个数组,数组各项支持 accept 选项,指定文件类型的 MIME 类型和 description 可选选项,指定文件类型的描述

返回一个 Promise 的 FileSystemFileHandle 数组

方法在用户未选择目录或用户代理拒绝目录访问时抛出 AbortError 异常

方法要求在调用需基于发生用户交互

获取新增资源的文件句柄

使用 Window 接口的 showSaveFilePicker() 方法新增文件(可以是已有文件或新文件),返回对应的文件句柄

方法传入一组可选的配置项

  • excludeAcceptAllOption 可选选项同上
  • id 选项同上
  • startIn 选项同上
  • suggestedName 可选选项指定建议的新增文件名称
  • types 选项同上

返回一个 Promise 的 FileSystemFileHandle

方法在用户未选择目录或用户代理拒绝目录访问时抛出 AbortError 异常

方法要求在调用需基于发生用户交互

获取选择资源的目录句柄

使用 Window 接口的 showDirectoryPicker() 选取目录,返回对应的目录句柄

方法传入一组可选的配置项

  • id 选项同上
  • mode 可选选项用于指定权限模式,接收字符串枚举 "read""readwrite",默认值为 "read"
  • startIn 选项同上

返回一个 Promise 的 FileSystemDirectoryHandle

方法在用户未选择目录或用户代理拒绝目录访问时抛出 AbortError 异常

方法要求在调用需基于发生用户交互

获取拖动资源的文件句柄或目录句柄

使用 DataTransferItem 接口的 getAsFileSystemHandle() 方法获取拖动资源的文件句柄或目录句柄

返回一个 Promise 的 FileSystemFileHandleFileSystemDirectoryHandle

类型

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
interface Window {
showOpenFilePicker: (options?: OpenFilePickerOptions) => Promise<FileSystemFileHandle[]>
showSaveFilePicker: (options?: SaveFilePickerOptions) => Promise<FileSystemFileHandle>
showDirectoryPicker: (options?: DirectoryPickerOptions) => Promise<FileSystemDirectoryHandle>
}

interface FilePickerAcceptType {
description?: string
accept: Record<string, string | string[]>
}

interface FilePickerOptions {
excludeAcceptAllOption?: boolean
id?: string
types?: FilePickerAcceptType[]
startIn?: StartInDirectory
}

interface OpenFilePickerOptions extends FilePickerOptions {
multiple?: boolean
}

interface SaveFilePickerOptions extends FilePickerOptions {
suggestedName?: string
}

interface DirectoryPickerOptions {
id?: string
startIn?: StartInDirectory
mode?: FileSystemPermissionMode
}

type WellKnownDirectory = 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos'

type StartInDirectory = WellKnownDirectory | FileSystemHandle

type FileSystemPermissionMode = 'read' | 'readwrite'

链接

File API

File API 允许获取文件及其内容

二进制对象

Blob 接口用于表示二进制对象

Blob 接口的 Blob() 构造方法用于创建一个二进制对象

方法接受一个可选的对象数组,各项可以为 stringArrayBufferBlob 本身,代表创建的二进制对象的内容

方法接受一个可选的配置项:

type 可选参数接收一个 string,指定二进制对象的 MIME 类型,默认值为空字符串

endings 可选参数接收一个 string,指定二进制对象编码时对换行符的处理方式,值 transparent 代表对换行符不作处理,值 native 代表转换换行符为系统支持的形式,默认值为 transparent

1
const blob = new Blob(['text', new ArrayBuffer(10), new Blob()], { type: 'text/plain', endings: 'native' })

Blob 接口的 size 属性返回一个 number,代表二进制对象中数据的大小

Blob 接口的 type 属性返回一个 string,代表二进制对象的 MIME 类型

1
2
blob.size
blob.type

Blob 接口的 slice() 方法用于截取二进制对象

方法的 start 可选参数接收一个 number,代表截取的起始位置,默认值为 0

方法的 end 可选参数接收一个 number,代表截取的终止位置,默认值同 size 参数

方法的 contentType 可选参数接收一个 string,代表新二进制对象的 type 参数

方法返回一个新二进制对象

1
const b = blob.slice(1, 11)

Blob 接口的 stream() 方法用于将二进制对象转换为流,返回一个 ReadableStream

Blob 接口的 text() 方法用于将二进制对象转换为 UTF-8 格式的字符串,返回一个 Promise 的 string

Blob 接口的 arrayBuffer() 方法用于将二进制对象转换为 Buffer,返回一个 Promise 的 ArrayBuffer

1
2
3
const stream = blob.stream()
const text = await blob.text()
const arrayBuffer = await blob.arrayBuffer()

文件对象

File 接口用于表示文件对象

File 接口的 File() 构造方法用于创建一个文件对象

方法接受一个对象数组,各项可以为 stringArrayBufferBlob 本身,代表创建的文件对象的内容

方法接受一个可选的配置项:

type 可选参数接收一个 string,指定二进制对象的 MIME 类型,默认值为空字符串

endings 可选参数接收一个 string,指定二进制对象编码时对换行符的处理方式,值 transparent 代表对换行符不作处理,值 native 代表转换换行符为系统支持的形式,默认值为 transparent

lastModified 可选参数接收一个 number,指定文件最后更改时间

1
const file = new File(['text', new ArrayBuffer(10), new Blob()], 'file', { type: 'text/plain', endings: 'native', lastModified: Date.now() })

File 接口的 name 属性返回一个 string,代表文件对象的名称

File 接口的 lastModified 属性返回一个 number,代表文件对象的最后更改时间,若无则返回当前时间

1
2
file.name
file.lastModified

文件列表对象

FileList 接口用于表示文件列表对象

FileList 接口的 length 属性返回一个 number,代表文件列表长度

FileList 接口的 item() 方法根据给定的 index 返回对应的文件

FileList 接口支持使用 [] 访问。亦支持使用 iterator 遍历

文件异步读取对象

FileReader 接口用于异步地读取文件

FileReader 接口的 FileReader() 构造方法用于创建异步读取文件对象

FileReader 接口的 EMPTY 常量值为 0,表示实例刚刚创建但尚未开始读取文件

FileReader 接口的 LOADING 常量值为 1,表示正在开始读取文件

FileReader 接口的 DONE 常量值为 2,表示读取文件完成,无论是否读取成功或失败抑或手动终止

FileReader 接口的 readyState 属性返回一个 string,代表文件读取进度,值为 EMPTYLOADINGDONE 之一

FileReader 接口的 result 属性返回一个 stringArrayBuffernull,表示文件读取结果,具体返回值类型取决于调用的方法

FileReader 接口的 error 属性返回一个 DOMExceptionnull,表示文件读取错误

FileReader 接口的 readAsArrayBuffer() 方法用于异步地将二进制对象读取为 ArrayBuffer

FileReader 接口的 readAsDataURL() 方法用于异步地将二进制对象读取为 Object URL

FileReader 接口的 readAsText() 方法用于异步地将二进制对象读取为指定编码的 string

以上三个方法接收一个 Blob 对象,无返回值

FileReader 接口的 abort() 方法用于终止读取进程

FileReader 接口的 abort 事件在读取文件进程被终止时触发,即调用 abort() 方法

FileReader 接口的 error 事件在读取文件进程失败时触发

FileReader 接口的 load 事件在读取文件进程成功完成时触发

FileReader 接口的 loadend 事件在读取文件进程完成时触发,无论成功与否

FileReader 接口的 loadstart 事件在读取文件进程开始时触发

FileReader 接口的 progress 事件在读取文件进程中周期性触发

事件均返回一个 ProgressEvent

1
2
3
4
5
6
7
const reader = new FileReader()

reader.addEventListener('load', () => {
console.log(reader.result)
})

reader.readAsText(blob)

文件同步读取对象

FileReaderSync 接口用于同步地读取文件内容

该接口仅在除 ServiceWorker 外的其他 Worker 环境中可用

FileReaderSync 接口的 FileReaderSync() 构造方法用于创建同步读取文件对象

FileReaderSync 接口的 readAsArrayBuffer() 方法用于同步地将二进制对象读取为 ArrayBuffer

FileReaderSync 接口的 readAsDataURL() 方法用于同步地将二进制对象读取为 Object URL

FileReaderSync 接口的 readAsText() 方法用于同步地将二进制对象读取为指定编码的 string

以上三个方法接收一个 Blob 对象,返回一个 string

以上三个方法可能抛出 NotFoundError,若资源不存在(如已被删除)

以上三个方法可能抛出 NotReadableError,若资源因为权限问题无法被读取(如资源锁)或资源快照与本地存储不符

以上三个方法可能抛出 SecurityError,若资源被用户代理判断为网络使用不安全,或进行了过于频繁的读取,或已被第三方修改

以上三个方法可能抛出 EncodingError,若资源被编码为 data URL 且超出了用户代理限制

1
2
3
4
5
const reader = new FileReaderSync()

const buffer = reader.readAsArrayBuffer()
const url = reader.readAsDataURL()
const text = reader.readAsText()

Object URL

使用 URL 接口的 createObjectURL() 创建一个 Object URL

方法接收一个 BlobMediaSource 参数,代表源二进制对象

方法返回一个 string,代表创建的 Object URL

使用 URL 接口的 revokeObjectURL() 释放一个已创建的 Object URL

方法接收一个 string,代表需释放的 Object URL

以上两方法在除 ServiceWorker 外的其他环境中可用

类型

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
interface Blob {
readonly size: number
readonly type: string

slice(start?: number, end?: number, contentType?: string): Blob

stream(): ReadableStream
text(): Promise<string>
arrayBuffer(): Promise<ArrayBuffer>
}

declare var Blob: {
new (blobParts?: BlobPart[], options?: BlobPropertyBag): Blob
prototype: Blob
}

enum EndingType {
transparent = 'transparent',
native = 'native',
}

interface BlobPropertyBag {
type?: string
endings?: EndingType
}

type BlobPart = Blob | string | BufferSource

interface File extends Blob {
readonly name: string
readonly lastModified: number
}

declare var File: {
new (fileBits: BlobPart[], fileName: string, options?: FilePropertyBag): File
prototype: File
}

interface FilePropertyBag extends BlobPropertyBag {
lastModified?: number
}

interface FileList {
item(index: number): File | null
readonly length: number
[index: number]: File
[Symbol.iterator](): IterableIterator<File>
}

declare var FileList: {
prototype: FileList
}

interface FileReader extends EventTarget {
readAsArrayBuffer(blob: Blob): void
readAsBinaryString(blob: Blob): void
readAsText(blob: Blob, encoding?: string): void
readAsDataURL(blob: Blob): void

abort(): void

readonly EMPTY: 0
readonly LOADING: 1
readonly DONE: 2

readonly readyState: FileReader['EMPTY' | 'LOADING' | 'DONE']

readonly result: string | ArrayBuffer | null

readonly error: DOMException | null

onloadstart: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null
onprogress: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null
onload: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null
onabort: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null
onerror: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null
onloadend: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null
}

declare var FileReader: {
new(): FileReader
prototype: FileReader
readonly EMPTY: 0
readonly LOADING: 1
readonly DONE: 2
}

interface FileReaderSync {
readAsArrayBuffer(blob: Blob): ArrayBuffer
readAsBinaryString(blob: Blob): string
readAsText(blob: Blob, encoding?: string): string
readAsDataURL(blob: Blob): string
}

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

declare var URL: {
new(url: string | URL, base?: string | URL): URL
prototype: URL
createObjectURL(obj: Blob | MediaSource): string
revokeObjectURL(url: string): void
}

链接

Barcode Detection API

Barcode Detection API 支持检测并扫描条形码和二维码

该 API 主要通过 BarcodeDetector 类使用

获取支持扫描类型

通过 BarcodeDetector 类的 getSupportedFormats() 静态方法用于获取支持扫描的条形码和二维码类型

方法返回一个 Promise 的字符串数组,结果各项在 BarcodeFormat 中

1
2
3
4
5
const supportedFormats = await BarcodeDetector.getSupportedFormats()

supportedFormats.forEach((format) => {
console.log(format)
})

扫描条形码或二维码

调用 BarcodeDetector 类构造函数创建 BarcodeDetector 实例

构造函数允许传入一个配置项,其参数 formats 代表限制识别的范围,需为支持识别的类型之一,同时需在 BarcodeFormat 中

构造函数可能抛出 TypeError 异常,如传入参数为空列表或列表某项不受支持

1
const detector = new BarcodeDetector()

调用 BarcodeDetector 类的 detect() 方法识别条形码和二维码

方法传入一个 image 参数,可以为 ImageData Blob VideoFrame OffscreenCanvas ImageBitmap HTMLCanvasElement HTMLVideoElement HTMLImageElement SVGImageElement 类型

方法返回一个 Promise 的对象数组,代表识别结果

各项 rawValue 参数返回一个字符串,代表识别的结果

各项 format 参数返回一个字符串,代表识别的格式,为 BarcodeFormat 中

各项 boundingBox 参数返回一个 DOMRectReadOnly,代表识别的条形码或二维码相对于图像的相对定位

各项 cornerPoints 参数返回一个对象数组,各项均包含 x 属性和 y 属性,代表识别的条形码或二维码相对于图像的四个顶点的相对定位

方法可能抛出 SecurityError 异常,如传入图像具有源且与当前脚本执行源不相同,或传入图像为 HTMLCanvasElement 且其中图像 origin-clean 标识为 false

方法可能抛出 InvalidStateError 异常,如传入图像为 HTMLImageElement 且加载失败(error 状态)或未完全完成解码,或传入图像为 HTMLVideoElement 且视频未加载完成(HAVE_NOTHINGHAVE_METADATA 状态)

1
2
3
4
5
const results = await detector.detect(image)

for (const result of results) {
console.log(result.rawValue)
}

类型

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
interface BarcodeDetector {
detect(image: ImageBitmapSource): Promise<DetectedBarcode[]>
}

declare var BarcodeDetector: {
new (barcodeDetectorOptions?: BarcodeDetectorOptions): BarcodeDetector
prototype: BarcodeDetector

getSupportedFormats(): Promise<BarcodeFormat[]>
}

interface BarcodeDetectorOptions {
formats: BarcodeFormat[]
}

interface DetectedBarcode {
boundingBox: DOMRectReadOnly
rawValue: string
format: BarcodeFormat
cornerPoints: ReadonlyArray<Point2D>
}

enum BarcodeFormat {
"aztec",
"code_128",
"code_39",
"code_93",
"codabar",
"data_matrix",
"ean_13",
"ean_8",
"itf",
"pdf417",
"qr_code",
"unknown",
"upc_a",
"upc_e"
}

链接

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[]>
}

链接


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