Quebra de barreiras com a API DataTransfer

Permitir que o usuário compartilhe dados fora da janela do navegador.

Talvez você já tenha ouvido falar da API DataTransfer, que faz parte da API HTML5 Drag and Drop e dos Eventos de área de transferência. Ele pode ser usado para transferir dados entre a origem e os destinos de recebimento.

Compatibilidade com navegadores

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

Origem

As interações de arrastar e soltar e de copiar e colar são usadas com frequência em uma página para transferir texto simples de A para B. Mas o que muitas vezes é ignorado é a capacidade de usar essas mesmas interações para ir além da janela do navegador.

As interações de arrastar e soltar e copiar e colar integradas do navegador podem se comunicar com outros aplicativos, da Web ou de outra forma, e não estão vinculadas a nenhuma origem. A API oferece suporte a várias entradas de dados com comportamentos diferentes com base no local para onde os dados são transferidos. Seu aplicativo da Web pode enviar e receber os dados transferidos ao detectar eventos recebidos.

Esse recurso pode mudar a forma como pensamos sobre compartilhamento e interoperabilidade em aplicativos da Web no computador. A transferência de dados entre aplicativos não precisa mais depender de integrações acopladas. Em vez disso, você pode dar aos usuários controle total para transferir dados para onde quiserem.

Um exemplo de interações possíveis com a API DataTransfer. (O vídeo não inclui som.)

Transferência de dados

Para começar, você precisa implementar o recurso de arrastar e soltar ou copiar e colar. Os exemplos abaixo mostram interações de arrastar e soltar, mas o processo de copiar e colar é semelhante. Se você não conhece a API de arrastar e soltar, há um ótimo artigo que explica o HTML5 de arrastar e soltar.

Ao fornecer dados com chave de tipo MIME, você pode interagir livremente com aplicativos externos. A maioria dos editores WYSIWYG, editores de texto e navegadores respondem aos tipos MIME "primitivos" usados no exemplo abaixo.

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

Observe a propriedade event.dataTransfer. Isso retorna uma instância de DataTransfer. Como você vai notar, esse objeto às vezes é retornado por propriedades com outros nomes.

Receber a transferência de dados funciona quase da mesma forma que fornecê-la. Ouça os eventos de recebimento (drop ou paste) e leia as chaves. Ao arrastar sobre um elemento, o navegador só tem acesso às chaves type dos dados. Os dados só podem ser acessados após uma queda.

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

Três tipos MIME têm suporte em vários aplicativos:

  • text/html:renderiza o payload HTML em elementos contentEditable e editores de texto rico (WYSIWYG), como o Documentos Google, o Microsoft Word e outros.
  • text/plain: Define o valor dos elementos de entrada, do conteúdo dos editores de código e o substituto de text/html.
  • text/uri-list:navega até o URL quando é colocado na barra de URL ou na página do navegador. Um atalho de URL será criado ao soltar em um diretório ou na área de trabalho.

A adoção generalizada de text/html por editores WYSIWYG torna essa tecnologia muito útil. Assim como nos documentos HTML, é possível incorporar recursos usando URLs de dados ou URLs acessíveis publicamente. Isso funciona bem com a exportação de recursos visuais (por exemplo, de uma tela) para editores como o Documentos Google.

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

Transferir usando o recurso copiar e colar

Confira abaixo como usar a API DataTransfer com interações de copiar e colar. Observe que o objeto DataTransfer é retornado por uma propriedade chamada clipboardData para eventos da área de transferência.

// 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 dados personalizados

Você não precisa usar apenas os tipos MIME primitivos, mas pode usar qualquer chave para identificar os dados transferidos. Isso pode ser útil para interações entre navegadores no seu aplicativo. Como mostrado abaixo, é possível transferir dados mais complexos usando as funções JSON.stringify() e 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);
  }
});

Conectar a Web

Embora os formatos personalizados sejam ótimos para a comunicação entre aplicativos que você controla, eles também limitam o usuário ao transferir dados para aplicativos que não usam seu formato. Se você quiser se conectar a aplicativos de terceiros na Web, vai precisar de um formato de dados universal.

O padrão JSON-LD (dados vinculados) é uma ótima opção para isso. Ele é leve e fácil de ler e gravar em JavaScript. O Schema.org contém muitos tipos predefinidos que podem ser usados, e as definições de esquema personalizadas também são uma opção.

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

Ao usar os tipos de Schema.org, você pode começar com o tipo genérico Thing ou usar algo mais próximo do seu caso de uso, como Event, Person, MediaObject, Place ou até mesmo tipos muito específicos, como MedicalEntity, se necessário. Ao usar o TypeScript, você pode usar as definições de interface das definições de tipo schema-dts.

Ao transmitir e receber dados JSON-LD, você vai contribuir para uma Web mais conectada e aberta. Com aplicativos que falam a mesma linguagem, é possível criar integrações profundas com aplicativos externos. Não é preciso fazer integrações complicadas de API. Todas as informações necessárias são incluídas nos dados transferidos.

Pense em todas as possibilidades de transferência de dados entre qualquer aplicativo (da Web) sem restrições: compartilhar eventos de uma agenda com seu app de tarefas favorito, anexar arquivos virtuais a e-mails, compartilhar contatos. Seria ótimo, certo? Tudo começa com você. 🙌

Problemas

Embora a API DataTransfer esteja disponível hoje, há algumas coisas que você precisa saber antes da integração.

Compatibilidade com navegadores

Todos os navegadores de computador têm suporte para a técnica descrita acima, mas os dispositivos móveis não têm. A técnica foi testada em todos os principais navegadores (Chrome, Edge, Firefox, Safari) e sistemas operacionais (Android, ChromeOS, iOS, macOS, Ubuntu Linux e Windows), mas, infelizmente, o Android e o iOS não passaram no teste. Enquanto os navegadores continuam a ser desenvolvidos, por enquanto a técnica é limitada apenas a navegadores para computador.

Facilidade de ser descoberto

Arrastar e soltar e copiar e colar são interações no nível do sistema ao trabalhar em um computador de mesa, com origem nas primeiras GUIs há mais de 40 anos. Pense em quantas vezes você usou essas interações para organizar arquivos. Isso ainda não é muito comum na Web.

Você vai precisar educar os usuários sobre essa nova interação e criar padrões de UX para que ela seja reconhecível, especialmente para pessoas que têm experiência com computadores limitada a dispositivos móveis.

Acessibilidade

Arrastar e soltar não é uma interação muito acessível, mas a API DataTransfer também funciona com cópia e colagem. Detecte eventos de copiar e colar. Não é preciso muito trabalho extra, e os usuários vão agradecer por você ter adicionado.

Segurança e privacidade

Há algumas considerações de segurança e privacidade que você precisa conhecer ao usar a técnica.

  • Os dados da área de transferência estão disponíveis para outros aplicativos no dispositivo do usuário.
  • Os aplicativos da Web que você está arrastando têm acesso às chaves de tipo, não aos dados. Os dados só ficam disponíveis ao soltar ou colar.
  • Os dados recebidos devem ser tratados como qualquer outra entrada do usuário: limpe e valide antes de usar.

Introdução à biblioteca auxiliar do Transmat

Você está animado para usar a API DataTransfer no seu app? Confira a biblioteca Transmat no GitHub. Essa biblioteca de código aberto alinha as diferenças do navegador, oferece utilitários JSON-LD, contém um observador para responder a eventos de transferência para destacar áreas de soltar e permite integrar as operações de transferência de dados entre implementações de arrastar e 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);

Agradecimentos

Imagem principal de Luba Ertel no Unsplash.