Supera los obstáculos con la API de DataTransfer

Permite que el usuario comparta datos fuera de la ventana del navegador.

Es posible que hayas oído hablar sobre la API de DataTransfer, que forma parte de la API de arrastrar y soltar de HTML5 y los eventos de portapapeles. Se puede usar para transferir datos entre los destinos de origen y de recepción.

Navegadores compatibles

  • 3
  • 12
  • 3.5
  • 4

Origen

Las interacciones de arrastrar y soltar y copiar y pegar suelen usarse para interacciones dentro de una página que permiten transferir texto simple de A a B. Sin embargo, lo que a menudo se pasa por alto es la capacidad de usar estas mismas interacciones para ir más allá de la ventana del navegador.

Tanto las interacciones de arrastrar y soltar integradas del navegador como las de copiar y pegar pueden comunicarse con otras aplicaciones, la Web o de otra manera, y no están vinculadas a ningún origen. La API admite varias entradas de datos con diferentes comportamientos según el lugar al que se transfieren los datos. Tu aplicación web puede enviar y recibir los datos transferidos cuando escucha eventos entrantes.

Esta capacidad puede cambiar la forma en que pensamos sobre el uso compartido y la interoperabilidad de las aplicaciones web en computadoras de escritorio. La transferencia de datos entre aplicaciones ya no necesita depender de integraciones estrechamente vinculadas. En cambio, puedes brindarles a los usuarios el control total para transferir datos donde quieran.

Un ejemplo de las interacciones que se pueden realizar con la API de DataTransfer. (El video no incluye sonido).

Transferencia de datos

Para comenzar, deberás implementar la función de arrastrar y soltar o copiar y pegar. En los siguientes ejemplos, se muestran interacciones de arrastrar y soltar, pero el proceso para copiar y pegar es similar. Si no estás familiarizado con la API de arrastrar y soltar, puedes consultar un excelente artículo en el que se explica la función de arrastrar y soltar de HTML5, en el que se explican los pormenores.

Cuando proporcionas datos con clave de tipo MIME, puedes interactuar libremente con aplicaciones externas. La mayoría de los editores, editores de texto y navegadores WYSIWYG responden a los tipos de MIME "primitivos" que se usan en el siguiente ejemplo.

document.querySelector('#dragSource')
.addEventListener('dragstart', (event) => {
  event.dataTransfer.setData('text/plain', 'Foo bar');
  event.dataTransfer.setData('text/html', '<h1>Foo bar</h1>');
  event.dataTransfer.setData('text/uri-list', 'https://example.com');
});

Observa la propiedad event.dataTransfer. Esto muestra una instancia de DataTransfer. Como verás, a veces las propiedades con otros nombres muestran este objeto.

Recibir la transferencia de datos funciona casi de la misma manera que proporcionarla. Escucha los eventos de recepción (drop o paste) y lee las teclas. Cuando arrastras un elemento, el navegador solo tiene acceso a las teclas type de los datos. Solo se puede acceder a los datos después de una caída.

document.querySelector('#dropTarget')
.addEventListener('dragover', (event) => {
  console.log(event.dataTransfer.types);
  // Without this, the drop event won't fire.
  event.preventDefault();
});

document.querySelector('#dropTarget')
.addEventListener('drop', (event) => {
  // Log all the transferred data items to the console.
  for (let type of event.dataTransfer.types) {
    console.log({ type, data: event.dataTransfer.getData(type) });
  }
  event.preventDefault();
});

Las aplicaciones admiten tres tipos de MIME ampliamente:

  • text/html: Renderiza la carga útil HTML en elementos contentEditable y editores de texto enriquecido (WYSIWYG), como Documentos de Google, Microsoft Word y otros.
  • text/plain: Establece el valor de los elementos de entrada, el contenido de los editores de código y el resguardo de text/html.
  • text/uri-list: Navega a la URL cuando lo sueltas en la barra de URL o en la página del navegador. Se creará un acceso directo de URL cuando se suelte un elemento en un directorio o el escritorio.

La adopción generalizada de text/html por parte de los editores WYSIWYG lo hace muy útil. Al igual que en los documentos HTML, puedes incorporar recursos con URLs de datos o URLs de acceso público. Esto funciona bien con la exportación de imágenes (por ejemplo, desde un lienzo) a editores como Documentos de Google.

const redPixel = 'data:image/gif;base64,R0lGODdhAQABAPAAAP8AAAAAACwAAAAAAQABAAACAkQBADs=';
const html = '<img src="' + redPixel + '" width="100" height="100" alt="" />';
event.dataTransfer.setData('text/html', html);

Cómo transferir mediante la función de copiar y pegar

A continuación, se muestra el uso de la API de DataTransfer con interacciones de copiar y pegar. Ten en cuenta que una propiedad llamada clipboardData muestra el objeto DataTransfer para los eventos del portapapeles.

// Listen to copy-paste events on the document.
document.addEventListener('copy', (event) => {
  const copySource = document.querySelector('#copySource');
  // Only copy when the `activeElement` (i.e., focused element) is,
  // or is within, the `copySource` element.
  if (copySource.contains(document.activeElement)) {
    event.clipboardData.setData('text/plain', 'Foo bar');
    event.preventDefault();
  }
});

document.addEventListener('paste', (event) => {
  const pasteTarget = document.querySelector('#pasteTarget');
  if (pasteTarget.contains(document.activeElement)) {
    const data = event.clipboardData.getData('text/plain');
    console.log(data);
  }
});

Formatos de datos personalizados

No estás limitado a los tipos de MIME primitivos, pero puedes usar cualquier clave para identificar los datos transferidos. Esto puede ser útil para las interacciones entre navegadores dentro de tu aplicación. Como se muestra a continuación, puedes transferir datos más complejos con las funciones JSON.stringify() y JSON.parse().

document.querySelector('#dragSource')
.addEventListener('dragstart', (event) => {
  const data = { foo: 'bar' };
  event.dataTransfer.setData('my-custom-type', JSON.stringify(data));
});

document.querySelector('#dropTarget')
.addEventListener('dragover', (event) => {
  // Only allow dropping when our custom data is available.
  if (event.dataTransfer.types.includes('my-custom-type')) {
    event.preventDefault();
  }
});

document.querySelector('#dropTarget')
.addEventListener('drop', (event) => {
  if (event.dataTransfer.types.includes('my-custom-type')) {
    event.preventDefault();
    const dataString = event.dataTransfer.getData('my-custom-type');
    const data = JSON.parse(dataString);
    console.log(data);
  }
});

Conexión a la Web

Si bien los formatos personalizados son excelentes para la comunicación entre aplicaciones que tú controlas, también limitan el usuario cuando se transfieren datos a aplicaciones que no usan tu formato. Si quieres conectarte con aplicaciones de terceros en la Web, necesitas un formato de datos universal.

El estándar JSON-LD (datos vinculados) es un gran candidato para hacerlo. Es liviana y fácil de leer y escribir en JavaScript. Schema.org contiene muchos tipos predefinidos que se pueden usar, y las definiciones de esquema personalizado también son una opción.

const data = {
  '@context': 'https://schema.org',
  '@type': 'ImageObject',
  contentLocation: 'Venice, Italy',
  contentUrl: 'venice.jpg',
  datePublished: '2010-08-08',
  description: 'I took this picture during our honey moon.',
  name: 'Canal in Venice',
};
event.dataTransfer.setData('application/ld+json', JSON.stringify(data));

Cuando usas los tipos de Schema.org, puedes comenzar con el tipo Thing genérico o usar algo más cercano a tu caso de uso, como Event, Person, MediaObject, Place o incluso tipos muy específicos, como MedicalEntity, si es necesario. Cuando utilizas TypeScript, puedes usar las definiciones de interfaz de las definiciones de tipo schema-dts.

Si transmites y recibes datos de JSON-LD, admitirás una Web más conectada y abierta. Como las aplicaciones hablan el mismo lenguaje, puedes crear integraciones profundas con aplicaciones externas. No necesitas integraciones de API complicadas; toda la información necesaria se incluye en los datos transferidos.

Piensa en todas las posibilidades de transferir datos entre cualquier aplicación (web) sin restricciones: compartir eventos de un calendario en tu app de tareas pendientes favorita, adjuntar archivos virtuales a correos electrónicos y compartir contactos. Sería grandioso, ¿verdad? Esto comienza por ti. 🙌

Problemas

Si bien la API de DataTransfer está disponible en la actualidad, debes tener en cuenta algunos aspectos antes de realizar la integración.

Compatibilidad del navegador

Todos los navegadores para computadoras de escritorio son muy compatibles con la técnica descrita anteriormente, mientras que los dispositivos móviles no. Esta técnica se probó en todos los navegadores más populares (Chrome, Edge, Firefox y Safari) y en los sistemas operativos (Android, ChromeOS, iOS, macOS, Ubuntu Linux y Windows), pero, lamentablemente, iOS y Android no pasaron la prueba. Mientras los navegadores continúan desarrollándose, por ahora la técnica se limita solo a navegadores de escritorio.

Visibilidad

Arrastrar y copiar y pegar son interacciones a nivel del sistema cuando se trabaja en una computadora de escritorio, con raíces en las primeras GUI de hace más de 40 años. Piensa cuántas veces usaste estas interacciones para organizar archivos. Esto no es muy común en la Web.

Deberás educar a los usuarios sobre esta nueva interacción y crear patrones de UX para que sea reconocible, especialmente para las personas cuya experiencia con computadoras hasta ahora se limita a dispositivos móviles.

Accesibilidad

La interacción arrastrar y soltar no es una interacción muy accesible, pero la API de DataTransfer también funciona con la función de copiar y pegar. Asegúrate de escuchar eventos de copiar y pegar. No requiere mucho trabajo adicional, y los usuarios te agradecerán por agregarla.

Seguridad y privacidad

Existen algunas consideraciones de seguridad y privacidad que debes tener en cuenta cuando uses esta técnica.

  • Los datos del portapapeles están disponibles para otras aplicaciones del dispositivo del usuario.
  • Las aplicaciones web que arrastras tienen acceso a las teclas de tipo, no a los datos. Los datos solo estarán disponibles cuando se los tenga o se peguen.
  • Los datos recibidos deben tratarse como cualquier otra entrada del usuario; desinfecta y valida antes de usarlos.

Comienza a usar la biblioteca de ayuda de Transmat

¿Te entusiasma usar la API de DataTransfer en tu aplicación? Consulta la biblioteca de Transmat en GitHub. Esta biblioteca de código abierto alinea las diferencias del navegador, proporciona utilidades de JSON-LD, contiene un observador que responde a los eventos de transferencia para destacar áreas de la función de soltar y te permite integrar las operaciones de transferencia de datos entre implementaciones existentes de arrastrar y soltar.

import { Transmat, TransmatObserver, addListeners } from 'transmat';

// Send data on drag/copy.
addListeners(myElement, 'transmit', (event) => {
  const transmat = new Transmat(event);
  transmat.setData({
    'text/plain': 'Foobar',
    'application/json': { foo: 'bar' },
  });
});

// Receive data on drop/paste.
addListeners(myElement, 'receive', (event) => {
  const transmat = new Transmat(event);
  if (transmat.hasType('application/json') && transmat.accept()) {
    const data = JSON.parse(transmat.getData('application/json'));
  }
});

// Observe transfer events and highlight drop areas.
const obs = new TransmatObserver((entries) => {
  for (const entry of entries) {
    const transmat = new Transmat(entry.event);
    if (transmat.hasMimeType('application/json')) {
      entry.target.classList.toggle('drag-over', entry.isTarget);
      entry.target.classList.toggle('drag-active', entry.isActive);
    }
  }
});
obs.observe(myElement);

Agradecimientos

Hero image de Luba Ertel en Unsplash.