Supera los obstáculos con la API de DataTransfer

Permite que el usuario comparta datos más allá de la ventana del navegador.

Es posible que hayas escuchado sobre la API de DataTransfer, que forma parte de la API de arrastrar y soltar de HTML5 y los eventos del portapapeles. Se puede usar para transferir datos entre destinos de origen y de destino.

Navegadores compatibles

  • Chrome: 3.
  • Edge: 12.
  • Firefox: 3.5.
  • Safari: 4.

Origen

Las interacciones de arrastrar y soltar y copiar y pegar suelen usarse para interacciones dentro de una página para 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 la función de arrastrar y soltar integrada en el navegador como las interacciones de copiar y pegar pueden comunicarse con otras aplicaciones, ya sean web o de otro tipo, 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 función puede cambiar la forma en que pensamos en el uso compartido y la interoperabilidad en las aplicaciones web para computadoras de escritorio. La transferencia de datos entre aplicaciones ya no depende de integraciones fuertemente vinculadas. En su lugar, puedes darles a los usuarios el control total para transferir datos a donde quieran.

Un ejemplo de las interacciones posibles con la API de DataTransfer. (El video no incluye sonido).

Cómo transferir 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 de copiar y pegar es similar. Si no estás familiarizado con la API de arrastrar y soltar, hay un excelente artículo que explica el arrastrar y soltar de HTML5, en el que se explican todos los detalles.

Cuando proporcionas datos con clave de tipo MIME, puedes interactuar libremente con aplicaciones externas. La mayoría de los editores WYSIWYG, los editores de texto y los navegadores responden a los tipos 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 muestran este objeto con otros nombres.

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 claves. Cuando se arrastra sobre un elemento, el navegador solo tiene acceso a las claves type de los datos. Solo se puede acceder a los datos después de una publicación.

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();
});

Tres tipos de MIME son ampliamente compatibles con todas las aplicaciones:

  • 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 se coloca en la barra de URL o en la página del navegador. Se creará un atajo de URL cuando se suelte en un directorio o en 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 elementos visuales (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 con 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 en 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);
  }
});

Cómo conectar la Web

Si bien los formatos personalizados son excelentes para la comunicación entre las aplicaciones que tienes bajo tu control, también limitan al usuario cuando transfiere 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 una excelente opción para esto. Es ligero y fácil de leer y escribir en JavaScript. Schema.org contiene muchos tipos predefinidos que se pueden usar, y las definiciones de esquemas personalizados 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 uses los tipos de Schema.org, puedes comenzar con el tipo genérico Thing 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 usas TypeScript, puedes usar las definiciones de interfaz de las definiciones de tipos de schema-dts.

Si transmites y recibes datos JSON-LD, admitirás una Web más conectada y abierta. Con aplicaciones que hablan el mismo idioma, puedes crear integraciones profundas con aplicaciones externas. No es necesario realizar integraciones de API complicadas, ya que 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 a tu app de tareas pendientes favorita, adjuntar archivos virtuales a correos electrónicos, compartir contactos. Eso sería genial, ¿no? Todo comienza contigo. 🙌

Problemas

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

Compatibilidad del navegador

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

Visibilidad

El arrastrar y soltar y el copiar y pegar son interacciones a nivel del sistema cuando se trabaja en una computadora de escritorio, con orígenes que se remontan a las primeras GUI hace más de 40 años. Piensa en cuántas veces has gebruikt estas interacciones para organizar archivos. Esto aún 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, en especial para las personas cuya experiencia con las computadoras hasta ahora se limitó a los dispositivos móviles.

Accesibilidad

El arrastrar y soltar no es una interacción muy accesible, pero la API de DataTransfer también funciona con copiar y pegar. Asegúrate de escuchar los eventos de copiar y pegar. No requiere mucho trabajo adicional, y tus usuarios te agradecerán que lo hayas agregado.

Seguridad y privacidad

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

  • Los datos del portapapeles están disponibles para otras aplicaciones en el dispositivo del usuario.
  • Las aplicaciones web a las que arrastras el elemento tienen acceso a las claves de tipo, no a los datos. Los datos solo estarán disponibles cuando los sueltes o pegues.
  • Los datos recibidos deben tratarse como cualquier otra entrada del usuario. Límpialos y validalos antes de usarlos.

Cómo comenzar 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 para responder a eventos de transferencia para destacar áreas de caída y te permite integrar las operaciones de transferencia de datos entre implementaciones de arrastrar y soltar existentes.

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

Imagen hero de Luba Ertel en Unsplash.