Quebra de barreiras com a API DataTransfer

Permite que o usuário compartilhe dados além 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 da área de transferência. Ele pode ser usado para transferir dados entre origens e destinos de recebimento.

Browser Support

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

Source

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

Tanto o recurso de arrastar e soltar integrado ao navegador quanto as interações de copiar e colar podem se comunicar com outros aplicativos, da Web ou não, e não estão vinculados a nenhuma origem. A API é compatível com várias entradas de dados com comportamentos diferentes com base em onde os dados são transferidos. Seu aplicativo da Web pode enviar e receber os dados transferidos ao detectar eventos recebidos.

Essa capacidade pode mudar a forma como pensamos sobre compartilhamento e interoperabilidade em aplicativos da Web para computador. A transferência de dados entre aplicativos não precisa mais depender de integrações fortemente 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, implemente 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 Drag and Drop, há um ótimo artigo explicando o recurso de arrastar e soltar do HTML5, que explica todos os detalhes.

Ao fornecer dados com chaves MIME-type, você pode interagir livremente com aplicativos externos. A maioria dos editores WYSIWYG, editores de texto e navegadores responde 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 ver, esse objeto às vezes é retornado por propriedades com outros nomes.

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

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 aceitos em 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 de elementos de entrada, conteúdo de editores de código e o fallback de text/html.
  • text/uri-list:navega até 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 um canvas) para editores como o Google Docs.

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 copiar e colar

O uso da API DataTransfer com interações de copiar e colar é mostrado abaixo. 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, você pode 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);
  }
});

Conectando 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, precisará de um formato de dados universal.

O padrão JSON-LD (Linked Data) é 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 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 do 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 altamente específicos, como MedicalEntity, se necessário. Ao usar o TypeScript, é possível usar as definições de interface das definições de tipo schema-dts.

Ao transmitir e receber dados JSON-LD, você vai apoiar uma Web mais conectada e aberta. Com aplicativos falando a mesma linguagem, é possível criar integrações avançadas com aplicativos externos. Não há necessidade de integrações complicadas de API. Todas as informações necessárias estão incluídas nos dados transferidos.

Pense em todas as possibilidades de transferir 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, não é? Tudo começa com você! 🙌

Problemas

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

Compatibilidade com navegadores

Os navegadores para computadores têm suporte excelente para a técnica descrita acima, mas os dispositivos móveis não. A técnica foi testada em todos os principais navegadores (Chrome, Edge, Firefox e 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 se desenvolvendo, 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 desktop, com raízes 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.

É preciso orientar os usuários sobre essa nova interação e criar padrões de UX para torná-la reconhecível, principalmente para pessoas cuja experiência com computadores até agora se limitou a dispositivos móveis.

Acessibilidade

Arrastar e soltar não é uma interação muito acessível, mas a API DataTransfer também funciona com copiar e colar. Verifique se você está detectando eventos de copiar e colar. Não é preciso muito trabalho extra, e seus usuários vão agradecer por isso.

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 ficam 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 quando você solta ou cola.
  • Os dados recebidos devem ser tratados como qualquer outra entrada do usuário: limpe e valide antes de usar.

Primeiros passos com a biblioteca auxiliar do Transmat

Você está animado para usar a API DataTransfer no seu aplicativo? Confira a biblioteca Transmat no GitHub. Essa biblioteca de código aberto alinha as diferenças entre navegadores, 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 as implementações de arrastar e soltar atuais.

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.