Probé diferentes enfoques para evitar FOUT (Flash Of Unstyled Text) cuando cargo una fuente de Google diferente a través de una interfaz de usuario.
Inicialmente pensé que podría escuchar el evento de carga en el elemento de enlace que utilizo para solicitar la fuente de la API de Google. Sin embargo, este enlace no descarga la fuente en sí, simplemente descarga el @font-face que enlaza con el archivo de fuente real.
Además, cuando su navegador ha descargado @font-face, aparentemente no descargará la fuente real, a la que se vincula, hasta que la fuente sea realmente utilizada por un elemento de texto visible en la página. “Visible” significa que el elemento no debe ocultarse con display: none.
Por lo tanto, después de investigar un poco más, decidí usar la API de carga de fuentes CSS , ya que debería estar diseñada exactamente para encargarse de este tipo de cosas. Sin embargo, para mi decepción, sigo teniendo un FOUT corto. Mi código para cargar fuentes de Google:
function requestGoogleFont(fontFamily, fontWeight, fontStyle, doOnLoad) { let font = [fontStyle, fontWeight, '14px', fontFamily + ', ', 'sans-serif'].join(' '), headElement = pageFrameContents.head, fontVariant = fontStyle === 'italic' ? fontWeight + 'italic': fontWeight, url = 'https://fonts.googleapis.com/css?family=' + fontFamily.split(' ').join('+') + ':' + fontVariant, link = html.createAndAppend(headElement, '<link rel="stylesheet">'); // Adds a link element to the head element fontLoadInitiator.style.font = font; pageFrame.contentWindow.document.fonts.load(font) .then(function(returned) { doOnLoad(); }); link.setAttribute("href", url); }Como habrá adivinado por el código, las fuentes se agregan a un documento en un iframe. La devolución de llamada, doOnLoad, simplemente ajusta el CSS para aplicar la fuente elegida. El fontLoadInitiator se refiere a un elemento de párrafo que está en la página y tiene un estilo invisible con opacidad: 0. Tiene algo de texto y su función es hacer que el navegador descargue la fuente tan pronto como se haya descargado @font-face.
Me decepciona que sigo teniendo un FOUT corto incluso después de usar la API de carga de fuentes CSS. Puedo ocultar el flash usando un pequeño tiempo de espera, pero realmente no debería tener que hacerlo ya que la devolución de llamada solo debería ejecutarse después de que la fuente se haya descargado por completo. Se me acabaron las ideas. Espero que algunos de ustedes hayan luchado contra esta bestia en el pasado y tengan algunas experiencias útiles para compartir. De lo contrario, mi única opción es el tiempo de espera (¡escalofríos!). Por cierto, estoy usando Google Chrome versión 93.
Después de experimentar un poco más, encontré una solución, junto con algunas ideas sobre la API de carga de fuentes CSS. Aunque la API existe desde hace varios años, todavía es experimental y está claro que todavía falta un poco de documentación sobre MDM. Una vez más, he experimentado principalmente en Chrome.
Lo que encontré es que usar promesas no funciona como se esperaba. He probado la función load() así como la propiedad ready . Según la documentación, FontFaceSet.load() fuerza la descarga de las fuentes. Pensé que significaba que la promesa no se resuelve hasta que se realiza la carga (inspirado en varias publicaciones de blog sobre el tema). Pero en realidad es muy literal: obliga a que se descarguen las fuentes, lo que significa que no tengo que cambiar el estilo de fontLoadInitiator para provocar la descarga. Sin embargo, parece que la promesa se resuelve antes de que la fuente se descargue por completo, de ahí el FOUT.
Según la documentación , la propiedad FontFaceSet.ready debería contener una promesa que se resuelve una vez que se completan las operaciones de carga y diseño de fuentes. En mi experiencia, sin embargo, no lo hace. Más bien, se comporta exactamente como la promesa devuelta por la función load() con la excepción de que no toma las fuentes como argumento: se resuelve antes de que la fuente se descargue por completo y provoca un FOUT y también parece forzar la descarga de la fuente (debe ser de todas las fuentes pendientes, ya que no se pasa ninguna fuente específica) porque funciona sin diseñar fontLoadInitiator.
Lo que funcionó para mí fue el detector de eventos FontFaceSet.onloadingdone . Este depende del diseño previo de fontLoadInitiator. Si fontLoadInitiator no tiene un estilo previo, el evento loadingdone nunca se activa. Usar esto elimina el FOUT.