{"id":8260,"date":"2022-02-14T20:38:40","date_gmt":"2022-02-14T20:38:40","guid":{"rendered":"https:\/\/capitalsoftware.com.ni\/?p=8260"},"modified":"2022-02-14T20:38:42","modified_gmt":"2022-02-14T20:38:42","slug":"dialogos-de-javascript-con-el-nuevo-elemento-de-dialogo-html","status":"publish","type":"post","link":"https:\/\/capitalsoftware.com.ni\/?p=8260","title":{"rendered":"Di\u00e1logos de JavaScript con el nuevo elemento de di\u00e1logo HTML"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"538\" src=\"https:\/\/capitalsoftware.com.ni\/wp-content\/uploads\/2022\/02\/webentwicklung-t-1024x538.jpg\" alt=\"\" class=\"wp-image-8261\" srcset=\"https:\/\/capitalsoftware.com.ni\/wp-content\/uploads\/2022\/02\/webentwicklung-t-1024x538.jpg 1024w, https:\/\/capitalsoftware.com.ni\/wp-content\/uploads\/2022\/02\/webentwicklung-t-300x158.jpg 300w, https:\/\/capitalsoftware.com.ni\/wp-content\/uploads\/2022\/02\/webentwicklung-t-768x403.jpg 768w, https:\/\/capitalsoftware.com.ni\/wp-content\/uploads\/2022\/02\/webentwicklung-t.jpg 1200w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption><em>\u00bfSabe c\u00f3mo hay cuadros de di\u00e1logo de JavaScript para alertar, confirmar y solicitar acciones del usuario?\u00a0Digamos que desea reemplazar los di\u00e1logos de JavaScript con el nuevo elemento de di\u00e1logo HTML.<\/em><\/figcaption><\/figure><\/div>\n\n\n\n<p>Recientemente trabaj\u00e9 en un proyecto con muchas llamadas a la API y comentarios de los usuarios recopilados con cuadros de di\u00e1logo de JavaScript.\u00a0Mientras esperaba que otro desarrollador codificara el componente\u00a0,\u00a0<code>&lt;Modal \/><\/code> us\u00e9\u00a0y\u00a0en mi c\u00f3digo.\u00a0Por ejemplo: <code>alert()confirm()prompt()<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const deleteLocation = confirm('Delete location');\nif (deleteLocation) {\n  alert('Location deleted');\n}<\/code><\/pre>\n\n\n\n<p>Entonces me di cuenta: obtienes&nbsp;<strong>muchas funciones relacionadas con modales<\/strong>&nbsp;de forma gratuita con&nbsp;<code>alert()<\/code>,&nbsp;<code>confirm()<\/code>y&nbsp;<code>prompt()<\/code>que a menudo se pasan por alto:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Es un verdadero modal.\u00a0<\/strong>Como en, siempre estar\u00e1 en la parte superior de la pila, incluso en la parte superior\u00a0<code>&lt;div><\/code> con\u00a0<code>z-index: 99999;<\/code>.<\/li><li><strong>Es accesible con el teclado.\u00a0<\/strong>Pulse\u00a0<code>Enter<\/code> para aceptar y\u00a0<code>Escape<\/code> para cancelar.<\/li><li><strong>Es compatible con lectores de pantalla.\u00a0<\/strong>Mueve el foco y permite que el contenido modal se lea en voz alta.<\/li><li><strong>Atrapa el enfoque.\u00a0<\/strong>Presionar\u00a0<code>Tab<\/code> no alcanzar\u00e1 ning\u00fan elemento enfocable en la p\u00e1gina principal, pero en Firefox y Safari, de hecho, mueve el foco a la interfaz de usuario del navegador.\u00a0Sin embargo, lo extra\u00f1o es que no puede mover el foco a los botones \u00abaceptar\u00bb o \u00abcancelar\u00bb en\u00a0<em>ning\u00fan<\/em>\u00a0navegador usando la\u00a0<code>Tab<\/code> tecla.<\/li><li><strong>Es compatible con las preferencias del usuario.\u00a0<\/strong>Obtenemos soporte autom\u00e1tico de modo claro y oscuro desde el primer momento.<\/li><li><strong>Hace una pausa en la ejecuci\u00f3n del c\u00f3digo.\u00a0<\/strong>Adem\u00e1s, espera la entrada del usuario.<\/li><\/ul>\n\n\n\n<p>Estos tres m\u00e9todos de JavaScript funcionan el 99% del tiempo cuando necesito alguna de estas funcionalidades.&nbsp;Entonces, \u00bfpor qu\u00e9 yo, o cualquier otro desarrollador web, no los uso?&nbsp;Probablemente porque parecen errores del sistema a los que no se les puede aplicar estilo.&nbsp;Otra gran consideraci\u00f3n: ha habido un movimiento hacia su desaprobaci\u00f3n.&nbsp;<a href=\"https:\/\/css-tricks.com\/choice-words-about-the-upcoming-deprecation-of-javascript-dialogs\/\">Primera eliminaci\u00f3n de iframes de dominios cruzados<\/a>&nbsp;y, se dice, de la plataforma web por completo, aunque tambi\u00e9n&nbsp;<a href=\"https:\/\/css-tricks.com\/choice-words-about-the-upcoming-deprecation-of-javascript-dialogs\/#comment-1787573\">parece que<\/a>&nbsp;los planes para eso est\u00e1n en espera.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Con esa gran consideraci\u00f3n en mente&#8230;<\/h2>\n\n\n\n<p>\u00bfCu\u00e1les son\u00a0<code>alert()<\/code>y\u00a0<code>confirm()<\/code>las\u00a0<code>prompt()<\/code>alternativas que tenemos para reemplazarlos?\u00a0Es posible que ya haya escuchado sobre el\u00a0<a href=\"https:\/\/css-tricks.com\/some-hands-on-with-the-html-dialog-element\/\"><code>&lt;dialog><\/code>elemento HTML<\/a>\u00a0y eso es lo que quiero ver en este art\u00edculo, us\u00e1ndolo junto con JavaScript\u00a0<code>class<\/code>.<\/p>\n\n\n\n<p>Es imposible reemplazar por\u00a0<em>completo<\/em>\u00a0los di\u00e1logos de Javascript con una funcionalidad id\u00e9ntica, pero si usamos el\u00a0<code>showModal()<\/code>m\u00e9todo\u00a0<code>&lt;dialog><\/code> combinado con uno\u00a0<code>Promise<\/code> que puede\u00a0<code>resolve<\/code>(aceptar) o\u00a0<code>reject<\/code>(cancelar), entonces tenemos algo\u00a0<em>casi<\/em>\u00a0igual de bueno.\u00a0Diablos, mientras estamos en eso, agreguemos sonido al elemento de di\u00e1logo HTML, \u00a1al igual que los di\u00e1logos del sistema real!<\/p>\n\n\n\n<p>Si desea ver la demostraci\u00f3n de inmediato,&nbsp;<a href=\"https:\/\/codepen.io\/stoumann\/pen\/bGovmLa\">est\u00e1 aqu\u00ed<\/a>&nbsp;.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"a-dialog-class\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-a-dialog-class\"><\/a>Una clase de di\u00e1logo<\/h3>\n\n\n\n<p>Primero, necesitamos un JavaScript b\u00e1sico\u00a0<code>Class <\/code>con un\u00a0<code>settings<\/code> objeto que se fusionar\u00e1 con la configuraci\u00f3n predeterminada.\u00a0Estos ajustes se utilizar\u00e1n para todos los di\u00e1logos, a menos que los sobrescriba cuando los invoque (pero hablaremos de eso m\u00e1s adelante).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export default class Dialog {\nconstructor(settings = {}) {\n  this.settings = Object.assign(\n    {\n      <em>\/* DEFAULT SETTINGS - see description below *\/<\/em>\n    },\n    settings\n  )\n  this.init()\n}<\/code><\/pre>\n\n\n\n<p>Los ajustes son:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong><code>accept<\/code>:<\/strong>&nbsp;Esta es la etiqueta del bot\u00f3n \u201cAceptar\u201d.<\/li><li><strong><code>bodyClass<\/code>:<\/strong>&nbsp;Esta es una clase CSS que se agrega al&nbsp;<code>&lt;body&gt;<\/code>elemento cuando el di\u00e1logo&nbsp;no es compatible con el navegador&nbsp;<code>open<\/code>.<code>&lt;dialog&gt;<\/code><\/li><li><strong><code>cancel<\/code>:<\/strong>&nbsp;Esta es la etiqueta del bot\u00f3n \u00abCancelar\u00bb.<\/li><li><strong><code>dialogClass<\/code>:<\/strong>&nbsp;Esta es una clase CSS personalizada agregada al&nbsp;<code>&lt;dialog&gt;<\/code>elemento.<\/li><li><strong><code>message<\/code>:<\/strong>&nbsp;Este es el contenido dentro del&nbsp;<code>&lt;dialog&gt;<\/code>.<\/li><li><strong><code>soundAccept<\/code>:<\/strong>&nbsp;Esta es la URL del archivo de sonido que reproduciremos cuando el usuario presione el bot\u00f3n \u00abAceptar\u00bb.<\/li><li><strong><code>soundOpen<\/code>:<\/strong>&nbsp;Esta es la URL del archivo de sonido que reproduciremos cuando el usuario abra el cuadro de di\u00e1logo.<\/li><li><strong><code>template<\/code>:<\/strong>&nbsp;Esta es una peque\u00f1a plantilla HTML opcional que se inyecta en el archivo&nbsp;<code>&lt;dialog&gt;<\/code>.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-initial-template-to-replace-javascript-dialogs\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-the-initial-template-to-replace-javascript-dialogs\"><\/a>La plantilla inicial para reemplazar los di\u00e1logos de JavaScript<\/h3>\n\n\n\n<p>En el&nbsp;<code>init<\/code>m\u00e9todo, agregaremos una funci\u00f3n auxiliar para detectar la compatibilidad con el elemento de di\u00e1logo HTML en los navegadores y configuraremos el HTML b\u00e1sico:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>init() {\n  <em>\/\/ Testing for &lt;dialog&gt; support<\/em>\n  this.dialogSupported = typeof HTMLDialogElement === 'function'\n  this.dialog = document.createElement('dialog')\n  this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'\n  this.dialog.role = 'dialog'\n  \n  <em>\/\/ HTML template<\/em>\n  this.dialog.innerHTML = `\n  &lt;form method=\"dialog\" data-ref=\"form\"&gt;\n    &lt;fieldset data-ref=\"fieldset\" role=\"document\"&gt;\n      &lt;legend data-ref=\"message\" id=\"${(Math.round(Date.now())).toString(36)}\"&gt;\n      &lt;\/legend&gt;\n      &lt;div data-ref=\"template\"&gt;&lt;\/div&gt;\n    &lt;\/fieldset&gt;\n    &lt;menu&gt;\n      &lt;button data-ref=\"cancel\" value=\"cancel\"&gt;&lt;\/button&gt;\n      &lt;button data-ref=\"accept\" value=\"default\"&gt;&lt;\/button&gt;\n    &lt;\/menu&gt;\n    &lt;audio data-ref=\"soundAccept\"&gt;&lt;\/audio&gt;\n    &lt;audio data-ref=\"soundOpen\"&gt;&lt;\/audio&gt;\n  &lt;\/form&gt;`\n\n  document.body.appendChild(this.dialog)\n\n  <em>\/\/ ...<\/em>\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"checking-for-support\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-checking-for-support\"><\/a>Comprobaci\u00f3n de apoyo<\/h4>\n\n\n\n<p>El camino para que los navegadores sean compatibles\u00a0<code>&lt;dialog><\/code> ha sido largo.\u00a0Safari lo recogi\u00f3\u00a0<a href=\"https:\/\/webkit.org\/blog\/12033\/release-notes-for-safari-technology-preview-134\/\">recientemente<\/a>\u00a0.\u00a0Firefox\u00a0<a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1733536\">incluso m\u00e1s recientemente<\/a>\u00a0, aunque no la\u00a0<code>&lt;form method=\"dialog\"><\/code> parte.\u00a0Entonces, necesitamos agregar\u00a0<code>type=\"button\"<\/code>a los botones \u00abAceptar\u00bb y \u00abCancelar\u00bb que estamos imitando.\u00a0De lo contrario,\u00a0<code>POST<\/code> formar\u00e1n el formulario y causar\u00e1n una actualizaci\u00f3n de la p\u00e1gina y queremos evitar eso.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;button${this.dialogSupported ? '' : ` type=\"button\"`}...&gt;&lt;\/button&gt;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"dom-node-references\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-dom-node-references\"><\/a>Referencias de nodos DOM<\/h4>\n\n\n\n<p>\u00bfNotaste todos los&nbsp;<code>data-ref<\/code>-atributos?&nbsp;Los usaremos para obtener referencias a los nodos DOM:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this.elements = {}\nthis.dialog.querySelectorAll('&#91;data-ref]').forEach(el =&gt; this.elements&#91;el.dataset.ref] = el)<\/code><\/pre>\n\n\n\n<p>Hasta ahora,&nbsp;<code>this.elements.accept<\/code>es una referencia al bot\u00f3n \u00abAceptar\u00bb y&nbsp;<code>this.elements.cancel<\/code>se refiere al bot\u00f3n \u00abCancelar\u00bb.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"button-attributes\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-button-attributes\"><\/a>Atributos del bot\u00f3n<\/h4>\n\n\n\n<p>Para los lectores de pantalla, necesitamos un\u00a0<code>aria-labelledby<\/code> atributo que apunte al ID de la etiqueta que describe el cuadro de di\u00e1logo; esa es la\u00a0<code>&lt;legend><\/code> etiqueta y contendr\u00e1 el archivo\u00a0<code>message<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this.dialog.setAttribute('aria-labelledby', this.elements.message.id)<\/code><\/pre>\n\n\n\n<p>eso&nbsp;<code>id<\/code>?&nbsp;Es una referencia \u00fanica a esta parte del&nbsp;<code>&lt;legend&gt;<\/code>elemento:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this.dialog.setAttribute('aria-labelledby', this.elements.message.id)<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"the-cancel-button\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-the-cancel-button\"><\/a>El bot\u00f3n \u00abCancelar\u00bb<\/h4>\n\n\n\n<p>\u00a1Buenas noticias!&nbsp;El elemento de di\u00e1logo HTML tiene un&nbsp;<code>cancel()<\/code>m\u00e9todo integrado que facilita la sustituci\u00f3n de los di\u00e1logos de JavaScript que llaman al&nbsp;<code>confirm()<\/code>m\u00e9todo.&nbsp;Emitimos ese evento cuando hacemos clic en el bot\u00f3n \u00abCancelar\u00bb:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this.elements.cancel.addEventListener('click', () =&gt; { \n  this.dialog.dispatchEvent(new Event('cancel')) \n})<\/code><\/pre>\n\n\n\n<p>Ese es el marco para&nbsp;<code>&lt;dialog&gt;<\/code>que reemplacemos&nbsp;<code>alert()<\/code>,&nbsp;<code>confirm()<\/code>y&nbsp;<code>prompt()<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"polyfilling-unsupported-browsers\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-polyfilling-unsupported-browsers\"><\/a>Navegadores no compatibles con polyfilling<\/h3>\n\n\n\n<p>Necesitamos ocultar el elemento de di\u00e1logo HTML para los navegadores que no lo admiten.&nbsp;Para hacer eso, ajustaremos la l\u00f3gica para mostrar y ocultar el di\u00e1logo en un nuevo m\u00e9todo&nbsp;<code>toggle()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>toggle(open = false) {\n  if (this.dialogSupported &amp;&amp; open) this.dialog.showModal()\n  if (!this.dialogSupported) {\n    document.body.classList.toggle(this.settings.bodyClass, open)\n    this.dialog.hidden = !open\n    <em>\/* If a `target` exists, set focus on it when closing *\/<\/em>\n    if (this.elements.target &amp;&amp; !open) {\n      this.elements.target.focus()\n    }\n  }\n}\n<em>\/* Then call it at the end of `init`: *\/<\/em>\nthis.toggle()<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"keyboard-navigation\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-keyboard-navigation\"><\/a>Navegaci\u00f3n por teclado<\/h3>\n\n\n\n<p>A continuaci\u00f3n, implementemos una forma de atrapar el foco para que el usuario pueda tabular entre los botones del cuadro de di\u00e1logo sin salir del cuadro de di\u00e1logo sin darse cuenta.&nbsp;Hay muchas maneras de hacer esto.&nbsp;Me gusta&nbsp;<a href=\"https:\/\/css-tricks.com\/a-css-approach-to-trap-focus-inside-of-an-element\/\">la forma CSS<\/a>&nbsp;, pero desafortunadamente, no es confiable.&nbsp;En su lugar, tomemos todos los elementos enfocables del cuadro de di\u00e1logo&nbsp;<code>NodeList<\/code>y almacen\u00e9moslos en&nbsp;<code>this.focusable<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>getFocusable() {\n  return &#91;...this.dialog.querySelectorAll('button,&#91;href],select,textarea,input:not(&#91;type=&amp;quot;hidden&amp;quot;]),&#91;tabindex]:not(&#91;tabindex=&amp;quot;-1&amp;quot;])')]\n}<\/code><\/pre>\n\n\n\n<p>A continuaci\u00f3n, agregaremos un&nbsp;<code>keydown<\/code>detector de eventos, que manejar\u00e1 toda la l\u00f3gica de navegaci\u00f3n de nuestro teclado:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this.dialog.addEventListener('keydown', e =&gt; {\n  if (e.key === 'Enter') {\n    if (!this.dialogSupported) e.preventDefault()\n    this.elements.accept.dispatchEvent(new Event('click'))\n  }\n  if (e.key === 'Escape') this.dialog.dispatchEvent(new Event('cancel'))\n  if (e.key === 'Tab') {\n    e.preventDefault()\n    const len =  this.focusable.length - 1;\n    let index = this.focusable.indexOf(e.target);\n    index = e.shiftKey ? index-1 : index+1;\n    if (index &lt; 0) index = len;\n    if (index &gt; len) index = 0;\n    this.focusable&#91;index].focus();\n  }\n})<\/code><\/pre>\n\n\n\n<p>Para&nbsp;<kbd>Enter<\/kbd>, debemos evitar que se&nbsp;<code>&lt;form&gt;<\/code>env\u00ede en navegadores donde el&nbsp;<code>&lt;dialog&gt;<\/code>elemento no es compatible.&nbsp;<code>Escape<\/code>emitir\u00e1 un&nbsp;<code>cancel<\/code>evento.&nbsp;Al presionar la&nbsp;<kbd>Tab<\/kbd>tecla, encontrar\u00e1 el elemento actual en la lista de nodos de elementos enfocables&nbsp;<code>this.focusable<\/code>, y establecer\u00e1 el enfoque en el siguiente elemento (o en el anterior si mantiene presionada la&nbsp;<kbd>Shift<\/kbd>tecla al mismo tiempo).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"displaying-the-dialog\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-displaying-the-dialog\"><\/a>Mostrando el <code>&lt;dialog><\/code><\/h3>\n\n\n\n<p>\u00a1Ahora vamos a mostrar el di\u00e1logo!&nbsp;Para esto, necesitamos un peque\u00f1o m\u00e9todo que fusione un&nbsp;<code>settings<\/code>objeto opcional con los valores predeterminados.&nbsp;En este objeto, exactamente como el&nbsp;<code>settings<\/code>objeto predeterminado, podemos agregar o cambiar la configuraci\u00f3n de un cuadro de di\u00e1logo espec\u00edfico.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>open(settings = {}) {\n  const dialog = Object.assign({}, this.settings, settings)\n  this.dialog.className = dialog.dialogClass || ''\n\n  <em>\/* set innerText of the elements *\/<\/em>\n  this.elements.accept.innerText = dialog.accept\n  this.elements.cancel.innerText = dialog.cancel\n  this.elements.cancel.hidden = dialog.cancel === ''\n  this.elements.message.innerText = dialog.message\n\n  <em>\/* If sounds exists, update `src` *\/<\/em>\n  this.elements.soundAccept.src = dialog.soundAccept || ''\n  this.elements.soundOpen.src = dialog.soundOpen || ''\n\n  <em>\/* A target can be added (from the element invoking the dialog *\/<\/em>\n  this.elements.target = dialog.target || ''\n\n  <em>\/* Optional HTML for custom dialogs *\/<\/em>\n  this.elements.template.innerHTML = dialog.template || ''\n\n  <em>\/* Grab focusable elements *\/<\/em>\n  this.focusable = this.getFocusable()\n  this.hasFormData = this.elements.fieldset.elements.length &gt; 0\n  if (dialog.soundOpen) {\n    this.elements.soundOpen.play()\n  }\n  this.toggle(true)\n  if (this.hasFormData) {\n    <em>\/* If form elements exist, focus on that first *\/<\/em>\n    this.focusable&#91;0].focus()\n    this.focusable&#91;0].select()\n  }\n  else {\n    this.elements.accept.focus()\n  }\n}<\/code><\/pre>\n\n\n\n<p>\u00a1Uf!&nbsp;Eso fue&nbsp;<strong>mucho c\u00f3digo<\/strong>&nbsp;.&nbsp;Ahora podemos mostrar el&nbsp;<code>&lt;dialog&gt;<\/code>elemento en todos los navegadores.&nbsp;Pero a\u00fan necesitamos imitar la funcionalidad que espera la entrada de un usuario despu\u00e9s de la ejecuci\u00f3n, como los&nbsp;m\u00e9todos nativos&nbsp;<code>alert()<\/code>,&nbsp;<code>confirm()<\/code>y .&nbsp;<code>prompt()<\/code>Para eso, necesitamos&nbsp;<code>Promise<\/code>un nuevo m\u00e9todo al que llamo&nbsp;<code>waitForUser()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>waitForUser() {\n  return new Promise(resolve =&gt; {\n    this.dialog.addEventListener('cancel', () =&gt; { \n      this.toggle()\n      resolve(false)\n    }, { once: true })\n    this.elements.accept.addEventListener('click', () =&gt; {\n      let value = this.hasFormData ? \n        this.collectFormData(new FormData(this.elements.form)) : true;\n      if (this.elements.soundAccept.src) this.elements.soundAccept.play()\n      this.toggle()\n      resolve(value)\n    }, { once: true })\n  })\n}<\/code><\/pre>\n\n\n\n<p>Este m\u00e9todo devuelve un&nbsp;<code>Promise<\/code>.&nbsp;Dentro de eso, agregamos detectores de eventos para \u00abcancelar\u00bb y \u00abaceptar\u00bb que resuelven&nbsp;<code>false<\/code>(cancelan) o&nbsp;<code>true<\/code>(aceptan).&nbsp;Si&nbsp;<code>formData<\/code>existe (para cuadros de di\u00e1logo personalizados o&nbsp;<code>prompt<\/code>), estos se recopilar\u00e1n con un&nbsp;<a href=\"https:\/\/stackoverflow.com\/questions\/41431322\/how-to-convert-formdata-html5-object-to-json\/46774073#46774073\">m\u00e9todo auxiliar<\/a>&nbsp;y luego se devolver\u00e1n en un objeto:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>collectFormData(formData) {\n  const object = {};\n  formData.forEach((value, key) =&gt; {\n    if (!Reflect.has(object, key)) {\n      object&#91;key] = value\n      return\n    }\n    if (!Array.isArray(object&#91;key])) {\n      object&#91;key] = &#91;object&#91;key]]\n    }\n    object&#91;key].push(value)\n  })\n  return object\n}<\/code><\/pre>\n\n\n\n<p>Podemos eliminar los detectores de eventos inmediatamente, usando&nbsp;<code>{ once: true }<\/code>.<\/p>\n\n\n\n<p>Para mantenerlo simple, no uso&nbsp;<code>reject()<\/code>sino que simplemente resuelvo&nbsp;<code>false<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"hiding-the-dialog\">Ocultar el <code>&lt;dialog><\/code><\/h3>\n\n\n\n<p>Anteriormente, agregamos detectores de eventos para el evento integrado&nbsp;<code>cancel<\/code>.&nbsp;Llamamos a este evento cuando el usuario hace clic en el bot\u00f3n \u00abcancelar\u00bb&nbsp;<em>o<\/em>&nbsp;presiona la&nbsp;<kbd>Escape<\/kbd>tecla.&nbsp;El&nbsp;<code>cancel<\/code>evento elimina el&nbsp;<code>open<\/code>atributo en el&nbsp;<code>&lt;dialog&gt;<\/code>, por lo que lo oculta.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"where-to-focus\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-where-to-focus\"><\/a>\u00bfAd\u00f3nde<code>:focus<\/code>?<\/h3>\n\n\n\n<p>En nuestro&nbsp;<code>open()<\/code>m\u00e9todo, nos enfocamos en el primer campo de formulario enfocable&nbsp;<em>o<\/em>&nbsp;en el bot\u00f3n \u00abAceptar\u00bb:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (this.hasFormData) {\n  this.focusable&#91;0].focus()\n  this.focusable&#91;0].select()\n}\nelse {\n  this.elements.accept.focus()\n}<\/code><\/pre>\n\n\n\n<p>\u00bfPero es esto correcto?&nbsp;En el&nbsp;<a href=\"https:\/\/www.w3.org\/TR\/wai-aria-practices\/examples\/dialog-modal\/dialog.html\">ejemplo del \u00abDi\u00e1logo modal\u00bb de W3<\/a>&nbsp;, este es el caso.&nbsp;Sin embargo, en el&nbsp;<a href=\"https:\/\/scottaohara.github.io\/accessible_modal_window\/\">ejemplo de Scott Ohara<\/a>&nbsp;, la atenci\u00f3n se centra en el cuadro de di\u00e1logo en s\u00ed, lo que tiene sentido si el lector de pantalla debe leer el texto que definimos&nbsp;<code>aria-labelledby<\/code>anteriormente en el atributo.&nbsp;No estoy seguro de cu\u00e1l es el correcto o el mejor, pero si queremos usar el m\u00e9todo de Scott.&nbsp;necesitamos agregar&nbsp;<code>tabindex=\"-1\"<\/code>a&nbsp;<code>&lt;dialog&gt;<\/code>en nuestro&nbsp;<code>init<\/code>m\u00e9todo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this.dialog.tabIndex = -1<\/code><\/pre>\n\n\n\n<p>Luego, en el&nbsp;<code>open()<\/code>m\u00e9todo, reemplazaremos el c\u00f3digo de enfoque con esto:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this.dialog.focus()<\/code><\/pre>\n\n\n\n<p>Podemos verificar&nbsp;<code>activeElement<\/code>(el elemento que tiene el foco) en cualquier momento en DevTools haciendo clic en el icono del \u00abojo\u00bb y escribiendo&nbsp;<code>document.activeElement<\/code>en la consola.&nbsp;Intenta tabular para ver c\u00f3mo se actualiza:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/01\/s_8CDAB8A0C7FFEC1010ECB97057AEEA4FE9884EE3F7B9D2CC22A75478BC745422_1642492707991_activelement.png?resize=186%2C82&amp;ssl=1\" alt=\"Mostrando el \u00edcono del ojo en DevTools, resaltado en verde brillante.\" class=\"wp-image-362674\"\/><figcaption>Al hacer clic en el icono del \u00abojo\u00bb<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"adding-alert-confirm-and-prompt\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-adding-alert-confirm-and-prompt\"><\/a>Adici\u00f3n de alerta, confirmaci\u00f3n y aviso<\/h3>\n\n\n\n<p>Finalmente estamos listos para agregar&nbsp;<code>alert()<\/code>,&nbsp;<code>confirm()<\/code>y&nbsp;<code>prompt()<\/code>a nuestra&nbsp;<code>Dialog<\/code>clase.&nbsp;Estos ser\u00e1n peque\u00f1os m\u00e9todos de ayuda que reemplazan los di\u00e1logos de JavaScript y la sintaxis original de esos m\u00e9todos.&nbsp;Todos ellos llaman al&nbsp;<code>open()<\/code>m\u00e9todo que creamos anteriormente, pero con un&nbsp;<code>settings<\/code>objeto que coincide con la forma en que activamos los m\u00e9todos originales.<\/p>\n\n\n\n<p>Comparemos con la sintaxis original.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"alert-is-normally-triggered-like-this\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-alert-is-normally-triggered-like-this\"><\/a><code>alert()<\/code>normalmente se activa as\u00ed:<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>window.alert(message);<\/code><\/pre>\n\n\n\n<p>En nuestro Di\u00e1logo, agregaremos un&nbsp;<code>alert()<\/code>m\u00e9todo que imitar\u00e1 esto:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/* dialog.alert() *\/<\/em>\nalert(message, config = { target: event.target }) {\n  const settings = Object.assign({}, config, { cancel: '', message, template: '' })\n  this.open(settings)\n  return this.waitForUser()\n}<\/code><\/pre>\n\n\n\n<p>Establecemos&nbsp;<code>cancel<\/code>y&nbsp;<code>template<\/code>para vaciar cadenas, de modo que, incluso si hubi\u00e9ramos establecido valores predeterminados anteriormente, estos no se ocultar\u00e1n, y solo&nbsp;<code>message<\/code>se&nbsp;<code>accept<\/code>mostrar\u00e1n.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"confirm-is-normally-triggered-like-this\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-confirm-is-normally-triggered-like-this\"><\/a><code>confirm()<\/code>normalmente se activa as\u00ed:<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>window.confirm(message);<\/code><\/pre>\n\n\n\n<p>En nuestra versi\u00f3n, similar a&nbsp;<code>alert()<\/code>, creamos un m\u00e9todo personalizado que muestra los&nbsp;elementos&nbsp;y&nbsp;<code>message<\/code>:<code>cancel<\/code><code>accept<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/* dialog.confirm() *\/<\/em>\nconfirm(message, config = { target: event.target }) {\n  const settings = Object.assign({}, config, { message, template: '' })\n  this.open(settings)\n  return this.waitForUser()\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"prompt-is-normally-triggered-like-this\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-prompt-is-normally-triggered-like-this\"><\/a><code>prompt()<\/code>normalmente se activa as\u00ed:<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>window.prompt(message, default);<\/code><\/pre>\n\n\n\n<p>Aqu\u00ed, necesitamos agregar un&nbsp;<code>template<\/code>con un&nbsp;<code>&lt;input&gt;<\/code>que envolveremos en un&nbsp;<code>&lt;label&gt;<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/* dialog.prompt() *\/<\/em>\nprompt(message, value, config = { target: event.target }) {\n  const template = `\n  &lt;label aria-label=\"${message}\"&gt;\n    &lt;input name=\"prompt\" value=\"${value}\"&gt;\n  &lt;\/label&gt;`\n  const settings = Object.assign({}, config, { message, template })\n  this.open(settings)\n  return this.waitForUser()\n}<\/code><\/pre>\n\n\n\n<p><code>{ target: event.target }<\/code>es una referencia al elemento DOM que llama al m\u00e9todo.&nbsp;Lo usaremos para volver a centrarnos en ese elemento cuando cerremos el&nbsp;<code>&lt;dialog&gt;<\/code>, devolviendo al usuario a donde estaba antes de que se activara el cuadro de di\u00e1logo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"we-ought-to-test-this\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-we-ought-to-test-this\"><\/a>Deber\u00edamos probar esto<\/h3>\n\n\n\n<p>Es hora de probar y asegurarse de que todo funciona como se esperaba.&nbsp;Vamos a crear un nuevo archivo HTML, importar la clase y crear una instancia:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;script type=\"module\"&gt;\n  import Dialog from '.\/dialog.js';\n  const dialog = new Dialog();\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p>\u00a1Pruebe los siguientes casos de uso uno a la vez!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/* alert *\/<\/em>\ndialog.alert('Please refresh your browser')\n<em>\/* or *\/<\/em>\ndialog.alert('Please refresh your browser').then((res) =&gt; {  console.log(res) })\n\n<em>\/* confirm *\/<\/em>\ndialog.confirm('Do you want to continue?').then((res) =&gt; { console.log(res) })\n\n<em>\/* prompt *\/<\/em>\ndialog.prompt('The meaning of life?', 42).then((res) =&gt; { console.log(res) })<\/code><\/pre>\n\n\n\n<p>Luego observe la consola mientras hace clic en \u00abAceptar\u00bb o \u00abCancelar\u00bb.&nbsp;Vuelva a intentarlo mientras presiona las teclas&nbsp;<kbd>Escape<\/kbd>o&nbsp;<kbd>Enter<\/kbd>en su lugar.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"async-await\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-async-await\"><\/a>As\u00edncrono\/Espera<\/h3>\n\n\n\n<p>Tambi\u00e9n podemos usar la&nbsp;<code>async\/await<\/code>forma de hacer esto.&nbsp;Estamos reemplazando los di\u00e1logos de JavaScript a\u00fan m\u00e1s al imitar la sintaxis original, pero requiere que la funci\u00f3n de ajuste sea&nbsp;<code>async<\/code>, mientras que el c\u00f3digo interno requiere la&nbsp;<code>await<\/code>palabra clave:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>document.getElementById('promptButton').addEventListener('click', async (e) =&gt; {\n  const value = await dialog.prompt('The meaning of life?', 42);\n  console.log(value);\n});<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"cross-browser-styling\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-cross-browser-styling\"><\/a>Estilo de navegador cruzado<\/h3>\n\n\n\n<p>\u00a1Ahora tenemos un elemento de di\u00e1logo HTML compatible con todos los navegadores y lectores de pantalla que reemplaza los di\u00e1logos de JavaScript!&nbsp;Hemos cubierto mucho.&nbsp;Pero el estilo podr\u00eda usar mucho amor.&nbsp;Utilicemos los atributos&nbsp;<code>data-component<\/code>y existentes&nbsp;<code>data-ref<\/code>para agregar un estilo entre navegadores, \u00a1sin necesidad de clases adicionales u otros atributos!<\/p>\n\n\n\n<p>Usaremos el&nbsp;<code><a href=\"https:\/\/css-tricks.com\/almanac\/selectors\/w\/where\/\">:where<\/a><\/code>pseudo-selector de CSS para mantener nuestros estilos predeterminados&nbsp;<a href=\"https:\/\/css-tricks.com\/dont-fight-the-cascade-control-it\/#aa-o-cascade-where-art-thou\">libres de especificidad<\/a>&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>:where(&#91;data-component*=\"dialog\"] *) {  \n  box-sizing: border-box;\n  outline-color: var(--dlg-outline-c, hsl(218, 79.19%, 35%))\n}\n:where(&#91;data-component*=\"dialog\"]) {\n  --dlg-gap: 1em;\n  background: var(--dlg-bg, #fff);\n  border: var(--dlg-b, 0);\n  border-radius: var(--dlg-bdrs, 0.25em);\n  box-shadow: var(--dlg-bxsh, 0px 25px 50px -12px rgba(0, 0, 0, 0.25));\n  font-family:var(--dlg-ff, ui-sansserif, system-ui, sans-serif);\n  min-inline-size: var(--dlg-mis, auto);\n  padding: var(--dlg-p, var(--dlg-gap));\n  width: var(--dlg-w, fit-content);\n}\n:where(&#91;data-component=\"no-dialog\"]:not(&#91;hidden])) {\n  display: block;\n  inset-block-start: var(--dlg-gap);\n  inset-inline-start: 50%;\n  position: fixed;\n  transform: translateX(-50%);\n}\n:where(&#91;data-component*=\"dialog\"] menu) {\n  display: flex;\n  gap: calc(var(--dlg-gap) \/ 2);\n  justify-content: var(--dlg-menu-jc, flex-end);\n  margin: 0;\n  padding: 0;\n}\n:where(&#91;data-component*=\"dialog\"] menu button) {\n  background-color: var(--dlg-button-bgc);\n  border: 0;\n  border-radius: var(--dlg-bdrs, 0.25em);\n  color: var(--dlg-button-c);\n  font-size: var(--dlg-button-fz, 0.8em);\n  padding: var(--dlg-button-p, 0.65em 1.5em);\n}\n:where(&#91;data-component*=\"dialog\"] &#91;data-ref=\"accept\"]) {\n  --dlg-button-bgc: var(--dlg-accept-bgc, hsl(218, 79.19%, 46.08%));\n  --dlg-button-c: var(--dlg-accept-c, #fff);\n}\n:where(&#91;data-component*=\"dialog\"] &#91;data-ref=\"cancel\"]) {\n  --dlg-button-bgc: var(--dlg-cancel-bgc, transparent);\n  --dlg-button-c: var(--dlg-cancel-c, inherit);\n}\n:where(&#91;data-component*=\"dialog\"] &#91;data-ref=\"fieldset\"]) {\n  border: 0;\n  margin: unset;\n  padding: unset;\n}\n:where(&#91;data-component*=\"dialog\"] &#91;data-ref=\"message\"]) {\n  font-size: var(--dlg-message-fz, 1.25em);\n  margin-block-end: var(--dlg-gap);\n}\n:where(&#91;data-component*=\"dialog\"] &#91;data-ref=\"template\"]:not(:empty)) {\n  margin-block-end: var(--dlg-gap);\n  width: 100%;\n}<\/code><\/pre>\n\n\n\n<p>Puedes dise\u00f1arlos como quieras, por supuesto.&nbsp;Esto es lo que le dar\u00e1 el CSS anterior:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/01\/s_8CDAB8A0C7FFEC1010ECB97057AEEA4FE9884EE3F7B9D2CC22A75478BC745422_1641134617420_Screenshot2022-01-02at15.43.13.png?resize=668%2C306&amp;ssl=1\" alt=\"Mostrando c\u00f3mo reemplazar los di\u00e1logos de JavaScript que usan el m\u00e9todo de alerta.  El modal es blanco sobre un fondo gris.  El contenido dice, actualice su navegador, seguido de un bot\u00f3n azul con una etiqueta blanca que dice Aceptar.\" class=\"wp-image-362676\"\/><figcaption><code>alert()<\/code><\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/01\/D8xfi66Q.png?resize=199%2C104&amp;ssl=1\" alt=\"Mostrando c\u00f3mo reemplazar los di\u00e1logos de JavaScript que usan el m\u00e9todo de confirmaci\u00f3n.  El modal es blanco sobre un fondo gris.  El contenido dice por favor, \u00bfquieres continuar?  y le sigue un enlace negro que dice cancelar y un bot\u00f3n azul con una etiqueta blanca que dice Aceptar.\" class=\"wp-image-362677\"\/><figcaption><code>confirm()<\/code><\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/01\/s_8CDAB8A0C7FFEC1010ECB97057AEEA4FE9884EE3F7B9D2CC22A75478BC745422_1641134568390_Screenshot2022-01-02at15.42.04.png?resize=542%2C444&amp;ssl=1\" alt=\"Mostrando c\u00f3mo reemplazar los di\u00e1logos de JavaScript que usan el m\u00e9todo de solicitud.  El modal es blanco sobre un fondo gris.  El contenido lee el sentido de la vida, seguido de una entrada de texto con el n\u00famero 42, seguido de un enlace negro que dice cancelar y un bot\u00f3n azul con una etiqueta blanca que dice Aceptar.\" class=\"wp-image-362678\"\/><figcaption><code>prompt()<\/code><\/figcaption><\/figure>\n\n\n\n<p>Para sobrescribir estos estilos y usar los suyos propios, agregue una clase en&nbsp;<code>dialogClass<\/code>,<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dialogClass: 'custom'<\/code><\/pre>\n\n\n\n<p>\u2026 luego agregue la clase en CSS y actualice los valores de propiedad personalizados de CSS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.custom {\n  --dlg-accept-bgc: hsl(159, 65%, 75%);\n  --dlg-accept-c: #000;\n  <em>\/* etc. *\/<\/em>\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"a-custom-dialog-example\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-a-custom-dialog-example\"><\/a>Un ejemplo de di\u00e1logo personalizado<\/h3>\n\n\n\n<p>\u00bfQu\u00e9 pasa si el est\u00e1ndar&nbsp;<code>alert()<\/code>y&nbsp;<code>confirm()<\/code>los&nbsp;<code>prompt()<\/code>m\u00e9todos que estamos imitando no funcionan para su caso de uso espec\u00edfico?&nbsp;De hecho, podemos hacer un poco m\u00e1s para que sea&nbsp;<code>&lt;dialog&gt;<\/code>m\u00e1s flexible para cubrir m\u00e1s que el contenido, los botones y la funcionalidad que hemos cubierto hasta ahora, y no es mucho m\u00e1s trabajo.<\/p>\n\n\n\n<p>Anteriormente, me burl\u00e9 de la idea de agregar un sonido al di\u00e1logo.&nbsp;Vamos a hacer eso.<\/p>\n\n\n\n<p>Puede usar la&nbsp;<code>template<\/code>propiedad del&nbsp;<code>settings<\/code>objeto para inyectar m\u00e1s HTML.&nbsp;Aqu\u00ed hay un ejemplo personalizado, invocado desde un&nbsp;<code>&lt;button&gt;<\/code>con&nbsp;<code>id=\"btnCustom\"<\/code>que activa un peque\u00f1o sonido divertido de un archivo MP3:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>document.getElementById('btnCustom').addEventListener('click', (e) =&gt; {\n  dialog.open({\n    accept: 'Sign in',\n    dialogClass: 'custom',\n    message: 'Please enter your credentials',\n    soundAccept: 'https:\/\/assets.yourdomain.com\/accept.mp3',\n    soundOpen: 'https:\/\/assets.yourdomain.com\/open.mp3',\n    target: e.target,\n    template: `\n    &lt;label&gt;Username&lt;input type=\"text\" name=\"username\" value=\"admin\"&gt;&lt;\/label&gt;\n    &lt;label&gt;Password&lt;input type=\"password\" name=\"password\" value=\"password\"&gt;&lt;\/label&gt;`\n  })\n  dialog.waitForUser().then((res) =&gt; {  console.log(res) })\n});<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"live-demo\"><a href=\"https:\/\/css-tricks.com\/replace-javascript-dialogs-html-dialog-element\/#aa-live-demo\"><\/a>Demo en vivo<\/h3>\n\n\n\n<p>\u00a1Aqu\u00ed hay una pluma con todo lo que construimos!&nbsp;Abra la consola, haga clic en los botones y juegue con los cuadros de di\u00e1logo, haga clic en los botones y use el teclado para aceptar y cancelar.https:\/\/codepen.io\/anon\/embed\/bGovmLa?height=450&amp;theme-id=1&amp;slug-hash=bGovmLa&amp;default-tab=result<\/p>\n\n\n\n<p>\u00bfEntonces, qu\u00e9 piensas?&nbsp;\u00bfEs esta una buena manera de reemplazar los di\u00e1logos de JavaScript con el elemento de di\u00e1logo HTML m\u00e1s nuevo?&nbsp;\u00bfO has probado a hacerlo de otra forma?&nbsp;\u00a1H\u00e1zmelo saber en los comentarios!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recientemente trabaj\u00e9 en un proyecto con muchas llamadas a la API y comentarios de los usuarios recopilados con cuadros de di\u00e1logo de JavaScript.\u00a0Mientras esperaba que otro desarrollador codificara el componente\u00a0,\u00a0&lt;Modal \/> us\u00e9\u00a0y\u00a0en mi c\u00f3digo.\u00a0Por ejemplo: alert()confirm()prompt() Entonces me di cuenta: obtienes&nbsp;muchas funciones relacionadas con modales&nbsp;de forma gratuita con&nbsp;alert(),&nbsp;confirm()y&nbsp;prompt()que a menudo se pasan por alto: Es [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":8262,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[45,44],"tags":[],"class_list":["post-8260","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-html","category-javascript"],"_links":{"self":[{"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=\/wp\/v2\/posts\/8260","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=8260"}],"version-history":[{"count":1,"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=\/wp\/v2\/posts\/8260\/revisions"}],"predecessor-version":[{"id":8263,"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=\/wp\/v2\/posts\/8260\/revisions\/8263"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=\/wp\/v2\/media\/8262"}],"wp:attachment":[{"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8260"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=8260"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/capitalsoftware.com.ni\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=8260"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}