تجاوز العوائق باستخدام واجهة برمجة التطبيقات DataTransfer API

السماح للمستخدم بمشاركة البيانات خارج نافذة المتصفّح

ربما سمعت عن DataTransfer API، وهي جزء من HTML5 Drag and Drop API وأحداث الحافظة. ويمكن استخدامها لنقل البيانات بين المصدر والأهداف المستلِمة.

Browser Support

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

Source

يتم غالبًا استخدام التفاعلات التي تتضمّن السحب والإفلات والنسخ واللصق للتفاعلات داخل الصفحة لنقل نص بسيط من الموضع "أ" إلى الموضع "ب". لكنّ ما يتم تجاهله في كثير من الأحيان هو إمكانية استخدام هذه التفاعلات نفسها لتجاوز نافذة المتصفّح.

يمكن أن تتواصل كلّ من ميزة السحب والإفلات المضمّنة في المتصفّح وعمليات النسخ واللصق مع التطبيقات الأخرى، سواء كانت على الويب أو غير ذلك، ولا ترتبط بأي مصدر. تتيح واجهة برمجة التطبيقات إدخالات بيانات متعددة ذات سلوكيات مختلفة استنادًا إلى المكان الذي يتم نقل البيانات إليه. يمكن لتطبيق الويب إرسال البيانات المنقولة وتلقّيها عند الاستماع إلى الأحداث الواردة.

يمكن أن تغيّر هذه الإمكانية طريقة تفكيرنا في المشاركة وإمكانية التشغيل التفاعلي في تطبيقات الويب على أجهزة الكمبيوتر. لم يعُد نقل البيانات بين التطبيقات يتطلّب عمليات دمج وثيقة الصلة. بدلاً من ذلك، يمكنك منح المستخدمين إمكانية التحكّم الكامل في نقل البيانات إلى أي مكان يريدونه.

مثال على التفاعلات الممكنة باستخدام DataTransfer API (لا يتضمّن الفيديو أي صوت).

نقل البيانات

للبدء، عليك تنفيذ عملية السحب والإفلات أو النسخ واللصق. تعرض الأمثلة أدناه تفاعلات السحب والإفلات، ولكن عملية النسخ واللصق مشابهة. إذا لم تكن على دراية بواجهة برمجة التطبيقات Drag and Drop، يمكنك الاطّلاع على مقالة رائعة تشرح ميزة السحب والإفلات في HTML5، وتوضّح جميع التفاصيل.

من خلال توفير بيانات مفتاحية من نوع MIME، يمكنك التفاعل بحرية مع التطبيقات الخارجية. تستجيب معظم محرّرات WYSIWYG ومحرّرات النصوص والمتصفحات لأنواع mime "الأولية" المستخدَمة في المثال أدناه.

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

لاحظ السمة event.dataTransfer. يؤدي ذلك إلى عرض نسخة من DataTransfer. كما تلاحظ، يتم عرض هذا العنصر أحيانًا من خلال سمات بأسماء أخرى.

تتّبع عملية تلقّي البيانات الخطوات نفسها تقريبًا التي تتّبعها عملية تقديمها. استمع إلى أحداث الاستلام (drop أو paste) واقرأ المفاتيح. عند سحب مؤشر الماوس فوق عنصر، لا يمكن للمتصفّح الوصول إلا إلى مفاتيح type الخاصة بالبيانات. لا يمكن الوصول إلى البيانات نفسها إلا بعد الحصول على جائزة.

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

تتوفّر ثلاثة أنواع MIME على نطاق واسع في جميع التطبيقات:

  • text/html: يعرض حمولة HTML في عناصر contentEditable وأدوات تحرير النصوص المنسّقة (WYSIWYG) مثل &quot;مستندات Google&quot; وMicrosoft Word وغيرها.
  • text/plain: تضبط هذه السمة قيمة عناصر الإدخال ومحتوى أدوات تعديل الرموز والعنصر الاحتياطي من text/html.
  • text/uri-list: ينتقل إلى عنوان URL عند إسقاطه في شريط العناوين أو صفحة المتصفّح. سيتم إنشاء اختصار لعنوان URL عند إسقاطه على مجلد أو على سطح المكتب.

إنّ الاستخدام الواسع النطاق لـ text/html من قِبل محرّرات WYSIWYG يجعلها مفيدة جدًا. كما هو الحال في مستندات HTML، يمكنك تضمين الموارد باستخدام عناوين URL للبيانات أو عناوين URL يمكن الوصول إليها بشكل علني. وتعمل هذه الطريقة بشكل جيد مع تصدير المرئيات (على سبيل المثال، من لوحة العرض) إلى أدوات التحرير مثل "مستندات Google".

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

النقل باستخدام النسخ واللصق

في ما يلي مثال على استخدام DataTransfer API مع تفاعلات النسخ واللصق. يُرجى العِلم بأنّه يتم عرض الكائن DataTransfer من خلال سمة تُسمى clipboardData لأحداث الحافظة.

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

تنسيقات البيانات المخصّصة

لا يقتصر الأمر على أنواع MIME الأساسية، بل يمكنك استخدام أي مفتاح لتحديد البيانات المنقولة. يمكن أن يكون ذلك مفيدًا للتفاعلات بين المتصفّحات داخل تطبيقك. كما هو موضّح أدناه، يمكنك نقل بيانات أكثر تعقيدًا باستخدام الدالتَين JSON.stringify() و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);
  }
});

ربط الويب

في حين أنّ التنسيقات المخصّصة رائعة للتواصل بين التطبيقات التي تتحكّم فيها، فإنّها تفرض أيضًا قيودًا على المستخدم عند نقل البيانات إلى التطبيقات التي لا تستخدم تنسيقك. إذا أردت الربط بتطبيقات تابعة لجهات خارجية على الويب، عليك استخدام تنسيق بيانات عالمي.

يُعدّ معيار JSON-LD (البيانات المرتبطة) خيارًا ممتازًا لذلك. وهي خفيفة الوزن وسهلة القراءة والكتابة باستخدام JavaScript. يحتوي Schema.org على العديد من الأنواع المحدّدة مسبقًا التي يمكن استخدامها، كما أنّ تعريفات المخطط المخصّصة هي خيار متاح أيضًا.

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

عند استخدام أنواع Schema.org، يمكنك البدء بنوع Thing العام، أو استخدام نوع أقرب إلى حالة الاستخدام، مثل Event أو Person أو MediaObject أو Place، أو حتى أنواع محددة جدًا مثل MedicalEntity إذا لزم الأمر. عند استخدام TypeScript، يمكنك استخدام تعريفات الواجهات من تعريفات النوع schema-dts.

من خلال إرسال بيانات JSON-LD وتلقّيها، ستساهم في توفير ويب أكثر اتصالاً وانفتاحًا. من خلال التطبيقات التي تستخدم اللغة نفسها، يمكنك إنشاء عمليات تكامل وثيق مع تطبيقات خارجية. لا حاجة إلى عمليات دمج معقّدة لواجهة برمجة التطبيقات، فجميع المعلومات المطلوبة مضمّنة في البيانات المنقولة.

فكر في جميع إمكانيات نقل البيانات بين أي تطبيق (على الويب) بدون أي قيود، مثل مشاركة الأحداث من تقويم إلى تطبيق المهام المفضّل لديك، أو إرفاق ملفات افتراضية برسائل إلكترونية، أو مشاركة جهات الاتصال. سيكون ذلك رائعًا، أليس كذلك؟ يبدأ هذا التغيير منك. 🙌

المخاوف والمشاكل الطبية

مع أنّ واجهة DataTransfer API متاحة اليوم، هناك بعض النقاط التي يجب معرفتها قبل دمجها.

توافُق المتصفح

تتوافق جميع متصفّحات الكمبيوتر المكتبي بشكل كبير مع الأسلوب الموضّح أعلاه، بينما لا تتوافق الأجهزة الجوّالة معه. تم اختبار هذه التقنية على جميع المتصفحات الرئيسية (Chrome وEdge وFirefox وSafari) وأنظمة التشغيل (Android وChromeOS وiOS وmacOS وUbuntu Linux وWindows)، ولكن لم يجتَز نظاما التشغيل Android وiOS الاختبار. في الوقت الحالي، تقتصر هذه التقنية على متصفّحات أجهزة الكمبيوتر فقط، ولكن سيتم توسيع نطاقها مع استمرار تطوير المتصفّحات.

سهولة العثور على المحتوى

تُعدّ عمليات السحب والإفلات والنسخ واللصق تفاعلات على مستوى النظام عند العمل على جهاز كمبيوتر مكتبي، ويعود تاريخها إلى أول واجهات مستخدم رسومية منذ أكثر من 40 عامًا. فكِّر في عدد المرات التي استخدمت فيها هذه التفاعلات لتنظيم الملفات. هذا النوع من التنسيق ليس شائعًا جدًا على الويب حتى الآن.

عليك توعية المستخدمين بشأن هذا التفاعل الجديد، وتقديم أنماط تجربة مستخدم تجعل هذا التفاعل مألوفًا، خاصةً للأشخاص الذين اقتصرت خبرتهم في استخدام أجهزة الكمبيوتر حتى الآن على الأجهزة الجوّالة.

تسهيل الاستخدام

لا يتيح السحب والإفلات تفاعلاً يسهل الوصول إليه، ولكن تعمل DataTransfer API مع النسخ واللصق أيضًا. تأكَّد من الاستماع إلى أحداث النسخ واللصق. لن يتطلّب ذلك مجهودًا إضافيًا كبيرًا، وسيشكرك المستخدمون على توفير هذه الميزة.

الأمان والخصوصية

هناك بعض الاعتبارات المتعلّقة بالأمان والخصوصية التي يجب أن تكون على دراية بها عند استخدام هذه التقنية.

  • تكون بيانات الحافظة متاحة للتطبيقات الأخرى على جهاز المستخدم.
  • يمكن لتطبيقات الويب التي تسحبها الوصول إلى مفاتيح النوع، وليس إلى البيانات. لا تتوفّر البيانات إلا عند إسقاطها أو لصقها.
  • يجب التعامل مع البيانات المستلَمة مثل أي إدخال آخر من المستخدم، أي تنظيفها والتحقّق من صحتها قبل استخدامها.

بدء استخدام مكتبة Transmat المساعدة

هل أنت متحمّس لاستخدام DataTransfer API في تطبيقك؟ ننصحك بالاطّلاع على مكتبة Transmat على GitHub. تتوافق مكتبة المصدر المفتوح هذه مع اختلافات المتصفحات، وتوفّر أدوات JSON-LD، وتحتوي على أداة مراقبة للاستجابة لأحداث النقل من أجل تمييز مناطق الإفلات، وتتيح لك دمج عمليات نقل البيانات بين عمليات السحب والإفلات الحالية.

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

الإقرارات

الصورة الرئيسية من لوبا إرتيل على Unsplash