A partir de Chrome 120, hay una nueva opción de unsanitized
disponible en el portapapeles asíncrono
en la API de Cloud. Esta opción puede ayudar en situaciones especiales con HTML, en las que necesitas
y pegar el contenido del portapapeles idéntico al que tenía cuando se copió.
Es decir, sin ningún paso de limpieza intermedio
que suelen usar los navegadores
por buenas razones: postúlate. Aprende a usarlo en esta guía.
Cuando trabajes con API de Async Clipboard, en la mayoría de los casos, los desarrolladores no tienen que preocuparse por la integridad del el contenido en el portapapeles y pueden asumir que lo que escribe en el el portapapeles (copia) es lo mismo que obtendrán cuando lean los datos de al portapapeles (pégalas).
Esto es definitivamente cierto para el texto. Intenta pegar el siguiente código en las Herramientas para desarrolladores
Consola y, luego, vuelve a enfocar la página de inmediato. (El setTimeout()
es necesario
de modo que tengas tiempo suficiente para enfocar la página, lo que es un requisito del modelo
API de Portapapeles). Como puedes ver, la entrada es exactamente la misma que la salida.
setTimeout(async () => {
const input = 'Hello';
await navigator.clipboard.writeText(input);
const output = await navigator.clipboard.readText();
console.log(input, output, input === output);
// Logs "Hello Hello true".
}, 3000);
Con las imágenes, es un poco diferente. Para evitar las llamadas bombas de compresión, navegadores volver a codificar imágenes como PNG, pero las imágenes de entrada y salida son visualmente exactamente igual, píxel por píxel.
setTimeout(async () => {
const dataURL =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=';
const input = await fetch(dataURL).then((response) => response.blob());
await navigator.clipboard.write([
new ClipboardItem({
[input.type]: input,
}),
]);
const [clipboardItem] = await navigator.clipboard.read();
const output = await clipboardItem.getType(input.type);
console.log(input.size, output.size, input.type === output.type);
// Logs "68 161 true".
}, 3000);
Sin embargo, ¿qué sucede con el texto HTML? Como habrás adivinado, con HTML,
es diferente. Aquí, el navegador limpia el código HTML para evitar errores
que ocurra algo, por ejemplo, si se quitan las etiquetas <script>
del código HTML
(y otros como <meta>
, <head>
y <style>
) y mediante la intercalación de CSS.
Considera el siguiente ejemplo y pruébalo en la consola de Herramientas para desarrolladores. Lo que harás
Observa que el resultado difiere bastante de la entrada.
setTimeout(async () => {
const input = `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="ProgId" content="Excel.Sheet" />
<meta name="Generator" content="Microsoft Excel 15" />
<style>
body {
font-family: HK Grotesk;
background-color: var(--color-bg);
}
</style>
</head>
<body>
<div>hello</div>
</body>
</html>`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read();
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
console.log(input, output);
}, 3000);
En general, la limpieza de HTML es algo bueno. No quieres exponerte relacionados con la seguridad, ya que, en la mayoría de los casos, permite el uso de HTML sin depurar. Hay sin embargo, hay situaciones en las que el desarrollador sabe exactamente lo que está haciendo y en el que la integridad del código HTML de las entradas y salidas es crucial para el correcto el funcionamiento de la app. En estas circunstancias, tienes dos opciones:
- Si controlas tanto el extremo de copiado como el final, por ejemplo, si copias desde tu app para pegar el contenido dentro de ella, debes usar Formatos web personalizados para la API de Async Clipboard. Deja de leer aquí y revisa el artículo vinculado.
- Si solo controlas el extremo de pegado en tu app, pero no el extremo de copia,
tal vez porque la operación de copia ocurre en una app nativa que no admite
formatos personalizados de la Web, debes usar la opción
unsanitized
, que es se explica en el resto de este artículo.
La limpieza incluye la eliminación de etiquetas script
, los estilos de incorporación y
asegurándose de que el HTML tenga el formato correcto. Esta lista no es exhaustiva,
es posible que se agreguen pasos en el futuro.
Copia y pega el código HTML sin depurar
Cuando write()
(copias) HTML al portapapeles con la API de Async Clipboard,
el navegador se asegura de que tenga el formato correcto ejecutándolo a través de un analizador del DOM
y serializar la cadena HTML resultante, pero no se está realizando ninguna limpieza en
este paso. No es necesario que realices ninguna acción. Si read()
colocas el código HTML en
el portapapeles de otra aplicación y tu aplicación web acepta obtener la
contenido con fidelidad total y necesidad de hacer
cualquier limpieza en tu propio código,
Puedes pasar un objeto de opciones al método read()
con una propiedad.
unsanitized
y un valor de ['text/html']
. De forma aislada, se verá de la siguiente manera:
navigator.clipboard.read({ unsanitized: ['text/html'] })
La siguiente muestra de código
que se muestra a continuación es casi el mismo que se mostró antes, pero esta vez con el valor unsanitized
de 12 a 1 con la nueva opción de compresión. Cuando lo pruebes en la consola de Herramientas para desarrolladores, verás que la entrada y
los resultados sean los mismos.
setTimeout(async () => {
const input = `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="ProgId" content="Excel.Sheet" />
<meta name="Generator" content="Microsoft Excel 15" />
<style>
body {
font-family: HK Grotesk;
background-color: var(--color-bg);
}
</style>
</head>
<body>
<div>hello</div>
</body>
</html>`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read({
unsanitized: ['text/html'],
});
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
console.log(input, output);
}, 3000);
Compatibilidad con navegadores y detección de funciones
No hay una forma directa de comprobar si se admite la función, así que
la detección se basa
en observar el comportamiento. Por lo tanto, el siguiente ejemplo
se basa en la detección del hecho de si una etiqueta <style>
sobrevive, lo que
indica compatibilidad o está intercalada, lo que indica no compatibilidad. Ten en cuenta que
Para que esto funcione, la página ya debe tener el portapapeles
permiso.
const supportsUnsanitized = async () => {
const input = `<style>p{color:red}</style><p>a`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read({
unsanitized: ['text/html],
});
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
return /<style>/.test(output);
};
Demostración
Para ver la opción unsanitized
en acción, consulta la
demostración de Glitch y revisa su
código fuente.
Conclusiones
Como se indicó en la introducción, la mayoría de los desarrolladores nunca tendrán que preocuparse
la limpieza del portapapeles y puede funcionar con las opciones de limpieza predeterminadas
que creó el navegador. En los casos poco comunes en los que deban preocuparse a los desarrolladores,
Existe la opción unsanitized
.
Vínculos útiles
Agradecimientos
Anupam Snigdha revisó este artículo, y Rachel Andrew. Se especificó la API y que implementa el equipo de Microsoft Edge.