Bạn biết làm thế nào có các hộp thoại JavaScript để cảnh báo, xác nhận và nhắc nhở hành động của người dùng?
Hãy để tôi giải thích
Gần đây tôi đã làm việc trên một dự án có rất nhiều lệnh gọi API và phản hồi của người dùng được thu thập bằng hộp thoại JavaScript. Trong khi tôi đang đợi một nhà phát triển khác viết mã thành phần
init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
9, tôi đã sử dụng
0,
1 và
2 trong mã của mình. Ví dụconst deleteLocation = confirm['Delete location'];
if [deleteLocation] {
alert['Location deleted'];
}
Sau đó, nó đánh tôi. bạn nhận được rất nhiều tính năng liên quan đến phương thức miễn phí với
0,
1 và
2 thường bị bỏ qua- Đó là một phương thức thực sự. Như trong, nó sẽ luôn ở trên cùng của ngăn xếp — thậm chí ở trên cùng của
6 với
7 - Nó có thể truy cập bằng bàn phím. Nhấn
8 để chấp nhận và
9 để hủy bỏ - Nó thân thiện với trình đọc màn hình. Nó di chuyển tiêu điểm và cho phép đọc to nội dung phương thức
- Nó bẫy tập trung. Nhấn
0 sẽ không đến được bất kỳ thành phần có thể đặt tiêu điểm nào trên trang chính, nhưng trong Firefox và Safari, nó thực sự di chuyển tiêu điểm đến giao diện người dùng của trình duyệt. Tuy nhiên, điều kỳ lạ là bạn không thể di chuyển tiêu điểm đến các nút “chấp nhận” hoặc “hủy” trong bất kỳ trình duyệt nào bằng cách sử dụng phímthis.elements = {} this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
0this.elements = {} this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
- Nó hỗ trợ tùy chọn người dùng. Chúng tôi nhận được hỗ trợ chế độ sáng và tối tự động ngay lập tức
- Nó tạm dừng thực thi mã. , Ngoài ra, nó chờ đầu vào của người dùng
Ba phương thức JavaScript này hoạt động 99% khi tôi cần bất kỳ chức năng nào trong số này. Vậy tại sao tôi - hoặc bất kỳ nhà phát triển web nào khác - không sử dụng chúng? . Một cân nhắc lớn khác. đã có phong trào hướng tới sự phản đối của họ. Lần đầu tiên xóa khỏi iframe tên miền chéo và, từ đó, hoàn toàn khỏi nền tảng web, mặc dù nó cũng có kế hoạch cho việc đó đang bị tạm dừng
Với sự cân nhắc lớn đó, chúng ta phải thay thế các phương án thay thế
0,
1 và
2 là gì? Không thể thay thế hoàn toàn các hộp thoại Javascript bằng chức năng giống hệt nhau, nhưng nếu chúng ta sử dụng phương pháp
this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
7 của this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5 kết hợp với một this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
9 có thể this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
0 [chấp nhận] hoặc this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
1 [hủy bỏ] — thì chúng ta có một thứ gần như tốt. Rất tiếc, trong khi chúng ta đang ở đó, hãy thêm âm thanh vào phần tử hộp thoại HTML - giống như các hộp thoại hệ thống thựcNếu bạn muốn xem bản demo ngay lập tức, nó ở đây
Một lớp hộp thoại
Trước tiên, chúng tôi cần một JavaScript cơ bản
this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
2 với đối tượng this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
3 sẽ được hợp nhất với cài đặt mặc định. Các cài đặt này sẽ được sử dụng cho tất cả các hộp thoại, trừ khi bạn ghi đè lên chúng khi gọi chúng [nhưng sẽ nói thêm về điều đó sau]export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
Các cài đặt là
4. Đây là nhãn của nút “Chấp nhận”this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
5. Đây là một lớp CSS được thêm vào phần tửthis.dialog.setAttribute['aria-labelledby', this.elements.message.id]
6 khi hộp thoại làthis.dialog.setAttribute['aria-labelledby', this.elements.message.id]
7 vàthis.dialog.setAttribute['aria-labelledby', this.elements.message.id]
5 không được trình duyệt hỗ trợthis.elements = {} this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
9. Đây là nhãn của nút “Hủy”this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
0. Đây là lớp CSS tùy chỉnh được thêm vào phần tửthis.elements.cancel.addEventListener['click', [] => { this.dialog.dispatchEvent[new Event['cancel']] }]
5this.elements = {} this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
2. Đây là nội dung bên trongthis.elements.cancel.addEventListener['click', [] => { this.dialog.dispatchEvent[new Event['cancel']] }]
5this.elements = {} this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
4. Đây là URL của tệp âm thanh mà chúng tôi sẽ phát khi người dùng nhấn nút “Chấp nhận”this.elements.cancel.addEventListener['click', [] => { this.dialog.dispatchEvent[new Event['cancel']] }]
5. Đây là URL của tệp âm thanh mà chúng tôi sẽ phát khi người dùng mở hộp thoạithis.elements.cancel.addEventListener['click', [] => { this.dialog.dispatchEvent[new Event['cancel']] }]
6. Đây là một mẫu HTML nhỏ, tùy chọn được đưa vàothis.elements.cancel.addEventListener['click', [] => { this.dialog.dispatchEvent[new Event['cancel']] }]
5this.elements = {} this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
Mẫu ban đầu để thay thế hộp thoại JavaScript
Trong phương pháp
this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
8, chúng tôi sẽ thêm chức năng trợ giúp để phát hiện hỗ trợ cho phần tử hộp thoại HTML trong trình duyệt và thiết lập HTML cơ bảninit[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
Kiểm tra hỗ trợ
Con đường để các trình duyệt hỗ trợ
this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5 đã dài. Safari đã chọn nó khá gần đây. Firefox thậm chí còn xuất hiện gần đây hơn, mặc dù không phải là phần toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
0. Vì vậy, chúng tôi cần thêm toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
1 vào các nút “Chấp nhận” và “Hủy” mà chúng tôi đang bắt chước. Nếu không, họ sẽ toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
2 biểu mẫu và làm mới trang và chúng tôi muốn tránh điều đó
Tham chiếu nút DOM
Bạn có nhận thấy tất cả các thuộc tính
toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
3 không? this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
Cho đến nay,
toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
4 là tham chiếu đến nút “Chấp nhận” và toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
5 là tham chiếu đến nút “Hủy”Thuộc tính nút
Đối với trình đọc màn hình, chúng tôi cần một thuộc tính
toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
6 trỏ đến ID của thẻ mô tả hộp thoại — đó là thẻ toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
7 và nó sẽ chứa this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
2this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
Đó là
toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
9? Nút “Hủy”
Tin tốt. Phần tử hộp thoại HTML có một phương thức
getFocusable[] {
return [...this.dialog.querySelectorAll['button,[href],select,textarea,input:not[[type="hidden"]],[tabindex]:not[[tabindex="-1"]]']]
}
1 tích hợp giúp việc thay thế các hộp thoại JavaScript gọi phương thức
1 trở nên dễ dàng hơn. Hãy phát ra sự kiện đó khi chúng ta nhấp vào nút “Hủy”this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
Đó là khuôn khổ để
this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5 của chúng ta thay thế cho
0,
1 và
2Polyfilling các trình duyệt không được hỗ trợ
Chúng ta cần ẩn phần tử hộp thoại HTML đối với những trình duyệt không hỗ trợ. Để làm điều đó, chúng ta sẽ gói logic để hiển thị và ẩn hộp thoại trong một phương thức mới,
getFocusable[] {
return [...this.dialog.querySelectorAll['button,[href],select,textarea,input:not[[type="hidden"]],[tabindex]:not[[tabindex="-1"]]']]
}
7toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
điều hướng bàn phím
Tiếp theo, hãy thực hiện một cách bẫy tiêu điểm để người dùng có thể tab giữa các nút trong hộp thoại mà không vô tình thoát khỏi hộp thoại. Có rất nhiều cách để làm điều này. Tôi thích cách CSS, nhưng thật không may, nó không đáng tin cậy. Thay vào đó, hãy lấy tất cả các yếu tố có thể đặt tiêu điểm từ hộp thoại dưới dạng
getFocusable[] {
return [...this.dialog.querySelectorAll['button,[href],select,textarea,input:not[[type="hidden"]],[tabindex]:not[[tabindex="-1"]]']]
}
8 và lưu trữ nó trong getFocusable[] {
return [...this.dialog.querySelectorAll['button,[href],select,textarea,input:not[[type="hidden"]],[tabindex]:not[[tabindex="-1"]]']]
}
9getFocusable[] {
return [...this.dialog.querySelectorAll['button,[href],select,textarea,input:not[[type="hidden"]],[tabindex]:not[[tabindex="-1"]]']]
}
Tiếp theo, chúng tôi sẽ thêm trình xử lý sự kiện
this.dialog.addEventListener['keydown', e => {
if [e.key === 'Enter'] {
if [!this.dialogSupported] e.preventDefault[]
this.elements.accept.dispatchEvent[new Event['click']]
}
if [e.key === 'Escape'] this.dialog.dispatchEvent[new Event['cancel']]
if [e.key === 'Tab'] {
e.preventDefault[]
const len = this.focusable.length - 1;
let index = this.focusable.indexOf[e.target];
index = e.shiftKey ? index-1 : index+1;
if [index < 0] index = len;
if [index > len] index = 0;
this.focusable[index].focus[];
}
}]
0, xử lý tất cả logic điều hướng bàn phím của chúng tôithis.dialog.addEventListener['keydown', e => {
if [e.key === 'Enter'] {
if [!this.dialogSupported] e.preventDefault[]
this.elements.accept.dispatchEvent[new Event['click']]
}
if [e.key === 'Escape'] this.dialog.dispatchEvent[new Event['cancel']]
if [e.key === 'Tab'] {
e.preventDefault[]
const len = this.focusable.length - 1;
let index = this.focusable.indexOf[e.target];
index = e.shiftKey ? index-1 : index+1;
if [index < 0] index = len;
if [index > len] index = 0;
this.focusable[index].focus[];
}
}]
Đối với Enter, chúng tôi cần ngăn
this.dialog.addEventListener['keydown', e => {
if [e.key === 'Enter'] {
if [!this.dialogSupported] e.preventDefault[]
this.elements.accept.dispatchEvent[new Event['click']]
}
if [e.key === 'Escape'] this.dialog.dispatchEvent[new Event['cancel']]
if [e.key === 'Tab'] {
e.preventDefault[]
const len = this.focusable.length - 1;
let index = this.focusable.indexOf[e.target];
index = e.shiftKey ? index-1 : index+1;
if [index < 0] index = len;
if [index > len] index = 0;
this.focusable[index].focus[];
}
}]
1 gửi trong trình duyệt nơi phần tử this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5 không được hỗ trợ.
9 sẽ phát ra một sự kiện this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
9. Nhấn phím Tab sẽ tìm phần tử hiện tại trong danh sách nút gồm các phần tử có thể đặt tiêu điểm, getFocusable[] {
return [...this.dialog.querySelectorAll['button,[href],select,textarea,input:not[[type="hidden"]],[tabindex]:not[[tabindex="-1"]]']]
}
9 và đặt tiêu điểm vào mục tiếp theo [hoặc mục trước đó nếu bạn giữ phím Shift cùng lúc]Hiển thị this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5
this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
Bây giờ hãy hiển thị hộp thoại. Đối với điều này, chúng ta cần một phương thức nhỏ kết hợp một đối tượng tùy chọn
this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
3 với các giá trị mặc định. Trong đối tượng này — chính xác như đối tượng this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
3 mặc định — chúng ta có thể thêm hoặc thay đổi cài đặt cho một hộp thoại cụ thểexport default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
0Phù. Đó là rất nhiều mã. Bây giờ chúng ta có thể hiển thị phần tử
this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5 trong tất cả các trình duyệt. Nhưng chúng ta vẫn cần bắt chước chức năng đợi đầu vào của người dùng sau khi thực thi, như các phương thức gốc
0,
1 và
2. Để làm được điều đó, chúng ta cần một this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
9 và một phương thức mới mà tôi đang gọi là export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
04export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
1Phương thức này trả về một
this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
9. Trong đó, chúng tôi thêm trình xử lý sự kiện cho “hủy” và “chấp nhận” để giải quyết export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
06 [hủy] hoặc export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
07 [chấp nhận]. Nếu export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
08 tồn tại [đối với hộp thoại tùy chỉnh hoặc export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
09], chúng sẽ được thu thập bằng một , sau đó được trả về trong một đối tượngexport default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
2Chúng tôi có thể xóa trình lắng nghe sự kiện ngay lập tức bằng cách sử dụng
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
10Để đơn giản, tôi không sử dụng
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
11 mà chỉ đơn giản giải quyết export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
06Ẩn this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5
this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
Trước đó, chúng tôi đã thêm trình xử lý sự kiện cho sự kiện
this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
9 tích hợp. Chúng tôi gọi sự kiện này khi người dùng nhấp vào nút “hủy” hoặc nhấn phím Escape. Sự kiện this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
9 loại bỏ thuộc tính this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
7 trên this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5, do đó ẩn nó điexport default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
18 ở đâu?
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
Trong phương pháp
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
19 của chúng tôi, chúng tôi tập trung vào trường biểu mẫu có thể đặt tiêu điểm đầu tiên hoặc nút “Chấp nhận”export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
3Nhưng điều này có đúng không? . Tuy nhiên, trong ví dụ của Scott Ohara, trọng tâm là chính hộp thoại — điều này hợp lý nếu trình đọc màn hình nên đọc văn bản mà chúng ta đã xác định trong thuộc tính
toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
6 trước đó. Tôi không chắc cái nào đúng hay tốt nhất, nhưng nếu chúng ta muốn sử dụng phương pháp của Scott. chúng ta cần thêm một export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
21 vào this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5 trong phương thức this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
8 của mìnhexport default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
4Sau đó, trong phương pháp
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
19, chúng tôi sẽ thay thế mã tiêu điểm bằng mã nàyexport default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
5Chúng ta có thể kiểm tra
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
25 [yếu tố có tiêu điểm] tại bất kỳ thời điểm nào trong DevTools bằng cách nhấp vào biểu tượng “con mắt” và nhập export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
26 trong bảng điều khiển. Hãy thử tab xung quanh để xem nó cập nhậtThêm cảnh báo, xác nhận và nhắc nhở
Cuối cùng, chúng tôi đã sẵn sàng để thêm
0,
1 và
2 vào lớp học export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
30 của chúng tôi. Đây sẽ là các phương thức trợ giúp nhỏ thay thế các hộp thoại JavaScript và cú pháp gốc của các phương thức đó. Tất cả đều gọi phương thức export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
19 mà chúng ta đã tạo trước đó, nhưng với một đối tượng this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
3 khớp với cách chúng ta kích hoạt các phương thức ban đầuHãy so sánh với cú pháp ban đầu
0 thường được kích hoạt như thế này
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
6Trong Hộp thoại của chúng tôi, chúng tôi sẽ thêm một phương thức
0 sẽ bắt chước điều nàyexport default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
7Chúng tôi đặt
this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
9 và this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
6 thành các chuỗi trống, để — ngay cả khi chúng tôi đã đặt các giá trị mặc định trước đó — các giá trị này sẽ không bị ẩn và chỉ this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
2 và this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
4 được hiển thị
1 thường được kích hoạt như thế này
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
8Trong phiên bản của chúng tôi, tương tự như
0, chúng tôi tạo một phương thức tùy chỉnh hiển thị các mục this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
2, this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
9 và this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
4export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
9
2 thường được kích hoạt như thế này
init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
0Ở đây, chúng ta cần thêm một
this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
6 với một export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
46 mà chúng ta sẽ bọc trong một export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
47init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
1export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
48 là tham chiếu đến phần tử DOM gọi phương thức. Chúng tôi sẽ sử dụng điều đó để tập trung lại vào yếu tố đó khi chúng tôi đóng this.elements = {}
this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
5, đưa người dùng trở lại vị trí của họ trước khi hộp thoại được kích hoạtChúng ta nên kiểm tra điều này
Đã đến lúc kiểm tra và đảm bảo mọi thứ hoạt động như mong đợi. Hãy tạo một tệp HTML mới, nhập lớp và tạo một thể hiện
init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
2Thử lần lượt các trường hợp sử dụng sau
init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
3Sau đó xem bảng điều khiển khi bạn nhấp vào “Chấp nhận” hoặc “Hủy. ” Thay vào đó, hãy thử lại trong khi nhấn phím Escape hoặc Enter
Không đồng bộ/Đang chờ
Chúng ta cũng có thể sử dụng cách
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
50 để thực hiện việc này. Chúng tôi đang thay thế các hộp thoại JavaScript nhiều hơn bằng cách bắt chước cú pháp ban đầu, nhưng nó yêu cầu hàm gói phải là export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
51, trong khi mã bên trong yêu cầu từ khóa export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
52init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
4Kiểu dáng trình duyệt chéo
Giờ đây, chúng tôi có một phần tử hộp thoại HTML thân thiện với trình duyệt chéo và trình đọc màn hình đầy đủ chức năng thay thế các hộp thoại JavaScript. Chúng tôi đã bảo hiểm rất nhiều. Nhưng kiểu dáng có thể sử dụng rất nhiều tình yêu. Hãy sử dụng các thuộc tính
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
53 và toggle[open = false] {
if [this.dialogSupported && open] this.dialog.showModal[]
if [!this.dialogSupported] {
document.body.classList.toggle[this.settings.bodyClass, open]
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if [this.elements.target && !open] {
this.elements.target.focus[]
}
}
}
/* Then call it at the end of `init`: */
this.toggle[]
3 hiện có để thêm kiểu dáng cho nhiều trình duyệt — không cần thêm các lớp hoặc thuộc tính khácChúng tôi sẽ sử dụng bộ chọn giả CSS
export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
55 để giữ các kiểu mặc định của chúng tôiinit[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
5Tất nhiên, bạn có thể tạo kiểu cho chúng theo ý muốn. Đây là những gì CSS trên sẽ cung cấp cho bạn
0
1
2Để ghi đè lên các kiểu này và sử dụng kiểu của riêng bạn, hãy thêm một lớp vào
this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
0,init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
6…sau đó thêm lớp trong CSS và cập nhật các giá trị thuộc tính tùy chỉnh của CSS
init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
7Một ví dụ về hộp thoại tùy chỉnh
Điều gì sẽ xảy ra nếu các phương pháp
0,
1 và
2 tiêu chuẩn mà chúng tôi đang bắt chước không thực hiện được mẹo cho trường hợp sử dụng cụ thể của bạn? Trước đó, tôi đã đề xuất ý tưởng thêm âm thanh vào hộp thoại. Hãy làm điều đó
Bạn có thể sử dụng thuộc tính
this.elements.cancel.addEventListener['click', [] => {
this.dialog.dispatchEvent[new Event['cancel']]
}]
6 của đối tượng this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
3 để chèn thêm HTML. Đây là một ví dụ tùy chỉnh, được gọi từ export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
66 với export default class Dialog {
constructor[settings = {}] {
this.settings = Object.assign[
{
/* DEFAULT SETTINGS - see description below */
},
settings
]
this.init[]
}
67 kích hoạt âm thanh nhỏ vui nhộn từ tệp MP3init[] {
// Testing for support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement['dialog']
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
`
document.body.appendChild[this.dialog]
// ...
}
8Bản thử trực tiếp
Đây là một cây bút với mọi thứ chúng tôi đã xây dựng. Mở bảng điều khiển, nhấp vào các nút và khám phá các hộp thoại, nhấp vào các nút và sử dụng bàn phím để chấp nhận và hủy bỏ
Dự phòng nhúng CodePen
Vậy bạn nghĩ như thế nào?