Quebra de barreiras com a API DataTransfer

Permitir que o usuário compartilhe dados além da janela do navegador.

Você já deve ter ouvido falar sobre a API DataTransfer, que faz parte da API HTML5 Drag and Drop e dos eventos da área de transferência. Ele pode ser usado para transferir dados entre destinos de origem e de recebimento.

Compatibilidade com navegadores

  • 3
  • 12
  • 3.5
  • 4

Origem

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

As interações de arrastar e soltar integradas do navegador e as interações de copiar e colar podem se comunicar com outros aplicativos, da Web ou não, e não estão vinculadas a nenhuma origem. A API oferece suporte a várias entradas de dados com comportamentos diferentes dependendo do 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 nossa perspectiva sobre o compartilhamento e a interoperabilidade em aplicativos da Web no computador. A transferência de dados entre aplicativos não precisa mais depender de integrações com acoplamento rígido. Em vez disso, você pode dar aos usuários controle total para transferir dados para onde quiserem.

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

Como transferir dados

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

Ao fornecer dados com chave do tipo MIME, você pode interagir livremente com aplicativos externos. A maioria dos editores, editores de texto e navegadores WYSIWYG respondem aos tipos MIME "primários" 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. Observe que, às vezes, esse objeto é retornado por propriedades com outros nomes.

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

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 são amplamente suportados em todos os aplicativos:

  • text/html:renderiza o payload HTML em elementos contentEditable e editores de rich text (WYSIWYG), como Documentos Google, Microsoft Word e outros.
  • text/plain: Define o valor dos elementos de entrada, o conteúdo dos editores de código e o substituto de text/html.
  • text/uri-list:navega para o URL quando solto 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 ampla adoção do text/html por editores WYSIWYG o torna muito útil. Assim como em 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 de copiar e colar

Confira abaixo como usar a API DataTransfer com interações de copiar e colar. 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 está limitado aos 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. Conforme 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ê tem sob seu controle, eles também limitam o usuário ao transferir dados para aplicativos que não estão usando seu formato. Para se conectar a aplicativos de terceiros na Web, é necessário usar um formato de dados universal.

O padrão JSON-LD (dados vinculados) é um ótimo candidato para isso. É leve e fácil de ler e gravar em JavaScript. O Schema.org contém muitos tipos predefinidos que podem ser usados, além de definições de esquemas personalizados.

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 Schema.org, é possível 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 altamente específicos, como MedicalEntity, se necessário. Quando você usa o TypeScript, pode usar as definições de interface das definições de tipo do schema-dts.

Ao transmitir e receber dados JSON-LD, você apoiará uma Web mais conectada e aberta. Com aplicativos que falam o mesmo idioma, é possível criar integrações profundas com aplicativos externos. Não é necessário fazer integrações de API complicadas. 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 (Web) sem restrições: compartilhar eventos de uma agenda com seu app de tarefas favorito, anexar arquivos virtuais a e-mails ou compartilhar contatos. Seria ótimo, certo? Isso começa com você! 🙌

Problemas

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

Compatibilidade com navegadores

Os navegadores para computador têm um ótimo suporte para a técnica descrita acima, mas os dispositivos móveis não. A técnica foi testada nos principais navegadores (Chrome, Edge, Firefox, Safari) e nos sistemas operacionais (Android, ChromeOS, iOS, macOS, Ubuntu Linux e Windows), mas, infelizmente, o Android e o iOS não foram aprovados no teste. Os navegadores continuam a ser desenvolvidos, mas, por enquanto, a técnica está limitada apenas a navegadores para computador.

Facilidade de descoberta

Arrastar e soltar e copiar e colar são interações no nível do sistema quando se trabalha em um computador desktop, com raízes nas primeiras GUIs mais de 40 anos atrás. 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 torná-la reconhecível, especialmente para pessoas cuja experiência com computadores até agora foi limitada a dispositivos móveis.

Acessibilidade

O recurso de arrastar e soltar não é uma interação muito acessível, mas a API DataTransfer também funciona com o recurso de copiar e colar. Você precisa ouvir os eventos de copiar e colar. Não exige muito trabalho extra, e seus usuários ficarão gratos a você por adicioná-lo.

Segurança e privacidade

Há algumas considerações de segurança e privacidade que você deve 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ê arrastar têm acesso às teclas 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 usá-los.

Começar a usar a biblioteca auxiliar Transmat

Você está entusiasmado para usar a API DataTransfer em seu aplicativo? Confira a biblioteca Transmat no GitHub. Essa biblioteca de código aberto alinha as diferenças do navegador, fornece 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.

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 (links em inglês).