Hộp thoại nhắc tùy chỉnh trong javascript

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
    this.elements = {}
    this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
    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ím
    this.elements = {}
    this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
    0
  • 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ực

Nế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à

  • this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
    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.elements = {}
    this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
    5 không được trình duyệt hỗ trợ
  • this.dialog.setAttribute['aria-labelledby', this.elements.message.id]
    9. Đây là nhãn của nút “Hủy”
  • this.elements.cancel.addEventListener['click', [] => { 
      this.dialog.dispatchEvent[new Event['cancel']] 
    }]
    0. Đây là lớp CSS tùy chỉnh được thêm vào phần tử
    this.elements = {}
    this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
    5
  • this.elements.cancel.addEventListener['click', [] => { 
      this.dialog.dispatchEvent[new Event['cancel']] 
    }]
    2. Đây là nội dung bên trong
    this.elements = {}
    this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
    5
  • this.elements.cancel.addEventListener['click', [] => { 
      this.dialog.dispatchEvent[new Event['cancel']] 
    }]
    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ại
  • this.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ào
    this.elements = {}
    this.dialog.querySelectorAll['[data-ref]'].forEach[el => this.elements[el.dataset.ref] = el]
    5

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ả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] // ... }

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']] 
}]
2

this.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à
2

Polyfilling 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"]]']]
}
7

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

đ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"]]']]
}
9

getFocusable[] {
  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ôi

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

Đố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

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

Phù. Đó 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[]
}
04

export default class Dialog {
constructor[settings = {}] {
  this.settings = Object.assign[
    {
      /* DEFAULT SETTINGS - see description below */
    },
    settings
  ]
  this.init[]
}
1

Phươ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ượng

export default class Dialog {
constructor[settings = {}] {
  this.settings = Object.assign[
    {
      /* DEFAULT SETTINGS - see description below */
    },
    settings
  ]
  this.init[]
}
2

Chú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

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ó đi

export default class Dialog {
constructor[settings = {}] {
  this.settings = Object.assign[
    {
      /* DEFAULT SETTINGS - see description below */
    },
    settings
  ]
  this.init[]
}
18 ở đâu?

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

Như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ình

export default class Dialog {
constructor[settings = {}] {
  this.settings = Object.assign[
    {
      /* DEFAULT SETTINGS - see description below */
    },
    settings
  ]
  this.init[]
}
4

Sau đó, 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ày

export default class Dialog {
constructor[settings = {}] {
  this.settings = Object.assign[
    {
      /* DEFAULT SETTINGS - see description below */
    },
    settings
  ]
  this.init[]
}
5

Chú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ật

Nhấp vào biểu tượng "con mắt"

Thê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 đầu

Hã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[]
}
6

Trong 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ày

export default class Dialog {
constructor[settings = {}] {
  this.settings = Object.assign[
    {
      /* DEFAULT SETTINGS - see description below */
    },
    settings
  ]
  this.init[]
}
7

Chú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[]
}
8

Trong 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]
4

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

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] // ... }
1

export 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ạt

Chú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] // ... }
2

Thử 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] // ... }
3

Sau đó 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[]
}
52

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] // ... }
4

Kiể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ác

Chú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ôi

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] // ... }
5

Tấ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] // ... }
7

Mộ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 MP3

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] // ... }
8

Bả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?

Làm cách nào để tùy chỉnh hộp nhắc trong JavaScript?

Tạo hộp thoại nhắc. Lời nhắc mới [{ tiêu đề. 'Tiêu đề gợi ý', nội dung. 'Thông báo Nhắc', văn bản giữ chỗ. sai, // văn bản giữ chỗ tùy chỉnh. sai, // văn bản gửi tùy chỉnh onSubmit[thành phần, giá trị] { console. nhật ký [giá trị] } }];

Làm cách nào để thêm hộp thoại trong JavaScript?

Bạn có thể tạo hộp thoại nhắc nhở bằng phương thức prompt[] . Phương thức này trả về văn bản đã nhập trong trường nhập liệu khi người dùng nhấp vào nút OK và trả về giá trị rỗng nếu người dùng nhấp vào nút Hủy. Nếu người dùng nhấp vào nút OK mà không nhập bất kỳ văn bản nào, một chuỗi trống sẽ được trả về.

Làm cách nào để nhắc đầu vào của người dùng trong JavaScript?

Trong JavaScript, chúng tôi sử dụng hàm prompt[] để yêu cầu người dùng nhập dữ liệu . Là một tham số, chúng tôi nhập văn bản mà chúng tôi muốn hiển thị cho người dùng. Sau khi người dùng nhấn “ok”, giá trị đầu vào được trả về. Chúng tôi thường lưu trữ đầu vào của người dùng trong một biến để chúng tôi có thể sử dụng thông tin trong chương trình của mình.

Làm cách nào để xác định lời nhắc trong JavaScript?

Định nghĩa và cách sử dụng . Phương thức prompt[] trả về giá trị đầu vào nếu người dùng bấm "OK", ngược lại trả về null. The prompt[] method displays a dialog box that prompts the user for input. The prompt[] method returns the input value if the user clicks "OK", otherwise it returns null .

Chủ Đề