Permita 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 do Clipboard. Ele pode ser usado para transferir dados entre os destinos de origem e de recebimento.
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 é negligenciado é a capacidade de usar essas mesmas interações para ir além da janela do navegador.
Tanto a interação de arrastar e soltar integrada do navegador quanto as interações de copiar e colar podem se comunicar com outros aplicativos, da Web ou de outra forma, e não estão vinculadas a nenhuma origem. A API é compatível com várias entradas de dados com diferentes comportamentos 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 maneira 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 fortemente acopladas. Mas você pode dar aos usuários controle total para transferir os dados para onde quiserem.
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 Drag and Drop, há um ótimo artigo explicando o recurso HTML5 com o recurso de arrastar e soltar, que mostra todos os detalhes.
Ao fornecer dados com chave do 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ê verá, esse objeto às vezes é 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 chaves type
dos dados. Os dados só podem ser acessados depois de 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 têm suporte em vários aplicativos:
text/html
:renderiza o payload HTML em elementoscontentEditable
e em 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 detext/html
.text/uri-list
:navega para o URL ao soltar 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 de acesso público. 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
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. 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ê 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. As definições de esquema personalizado 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, você pode criar integrações profundas com aplicativos externos. Não é necessário fazer integrações de API complicadas. Todas as informações necessárias estã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, não é? 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 nos sistemas operacionais (Android, ChromeOS, iOS, macOS, Ubuntu Linux e Windows), mas infelizmente o Android e o iOS não foram aprovados no teste. Embora os navegadores continuem a ser desenvolvidos, por enquanto a técnica se limita 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 das 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ê 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 está restrita 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. Preste atenção aos 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 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 usar.
Primeiros passos com a biblioteca auxiliar Transmat
Você está animado em 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, 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 as 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 (links em inglês).