HTML5 खींचें और छोड़ें एपीआई

इस पोस्ट में, 'खींचें और छोड़ें' सुविधा के बारे में बुनियादी जानकारी दी गई है.

ज़्यादातर ब्राउज़र में, चुने गए टेक्स्ट, इमेज, और लिंक को डिफ़ॉल्ट रूप से खींचा और छोड़ा जा सकता है. उदाहरण के लिए, अगर आप किसी वेब पेज पर कोई लिंक खींचते हैं, तो आपको टाइटल और यूआरएल वाला एक छोटा बॉक्स दिखेगा. इसे शॉर्टकट बनाने या लिंक पर नेविगेट करने के लिए, पता बार या डेस्कटॉप पर छोड़ा जा सकता है. दूसरे तरह के कॉन्टेंट को खींचकर छोड़ने लायक बनाने के लिए, आपको HTML5 ड्रैग और ड्रॉप एपीआई का इस्तेमाल करना होगा.

किसी ऑब्जेक्ट को खींचने और छोड़ने लायक बनाने के लिए, उस एलिमेंट पर draggable=true सेट करें. किसी भी चीज़ को खींचकर चालू किया जा सकता है, जिसमें आपके पेज पर मौजूद इमेज, फ़ाइलें, लिंक, फ़ाइलें या किसी भी मार्कअप का इस्तेमाल करना शामिल है.

यहां दिए गए उदाहरण में, सीएसएस ग्रिड की मदद से व्यवस्थित किए गए कॉलम को फिर से व्यवस्थित करने के लिए इंटरफ़ेस बनाया गया है. कॉलम का बुनियादी मार्कअप कुछ ऐसा दिखता है. इसमें हर कॉलम के लिए draggable एट्रिब्यूट की वैल्यू true पर सेट होती है:

<div class="container">
  <div draggable="true" class="box">A</div>
  <div draggable="true" class="box">B</div>
  <div draggable="true" class="box">C</div>
</div>

कंटेनर और बॉक्स एलिमेंट का सीएसएस यहां दिया गया है. खींचने और छोड़ने की सुविधा से जुड़ी सिर्फ़ एक सीएसएस, cursor: move प्रॉपर्टी है. बाकी के कोड से, कंटेनर और बॉक्स एलिमेंट के लेआउट और स्टाइल को कंट्रोल किया जाता है.

.container {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
}

.box {
  border: 3px solid #666;
  background-color: #ddd;
  border-radius: .5em;
  padding: 10px;
  cursor: move;
}

इस दौरान, आइटम को खींचा और छोड़ा जा सकता है, लेकिन कुछ नहीं होता. व्यवहार जोड़ने के लिए, आपको JavaScript API का इस्तेमाल करना होगा.

खींचकर छोड़ने वाले इवेंट को सुनें

खींचकर छोड़ने की प्रोसेस पर नज़र रखने के लिए, इनमें से किसी भी इवेंट को सुना जा सकता है:

खींचने और छोड़ने की सुविधा को मैनेज करने के लिए, आपको किसी तरह का सोर्स एलिमेंट (जहां खींचने की प्रोसेस शुरू होती है), डेटा पेलोड (खींची जा रही चीज़), और टारगेट (ड्रॉप को पकड़ने के लिए कोई जगह) की ज़रूरत होती है. सोर्स एलिमेंट, किसी भी तरह का हो सकता है. टारगेट, ड्रॉप ज़ोन या ड्रॉप ज़ोन का सेट होता है. इसमें उस डेटा को स्वीकार किया जाता है जिसे उपयोगकर्ता ड्रॉप करना चाहता है. सभी एलिमेंट को टारगेट नहीं किया जा सकता. उदाहरण के लिए, आपका टारगेट कोई इमेज नहीं हो सकती.

ड्रैग क्रम शुरू और खत्म करना

अपने कॉन्टेंट पर draggable="true" एट्रिब्यूट तय करने के बाद, हर कॉलम के लिए खींचने और छोड़ने का क्रम शुरू करने के लिए, dragstart इवेंट हैंडलर अटैच करें.

जब उपयोगकर्ता इसे खींचना शुरू करता है, तो यह कोड कॉलम की ओपैसिटी को 40% पर सेट करता है. इसके बाद, खींचकर छोड़ने वाला इवेंट खत्म होने पर, इसे 100% पर वापस कर देता है.

function handleDragStart(e) {
  this.style.opacity = '0.4';
}

function handleDragEnd(e) {
  this.style.opacity = '1';
}

let items = document.querySelectorAll('.container .box');
items.forEach(function (item) {
  item.addEventListener('dragstart', handleDragStart);
  item.addEventListener('dragend', handleDragEnd);
});

इसका नतीजा, Glitch के इस डेमो में देखा जा सकता है. कोई आइटम खींचें और छोड़ें और उसकी अपारदर्शिता बदल जाएगी. सोर्स एलिमेंट में dragstart इवेंट है. इसलिए, this.style.opacity को 40% पर सेट करने से उपयोगकर्ता को विज़ुअल फ़ीडबैक मिलता है कि वह एलिमेंट ही मौजूदा एलिमेंट है. आइटम को छोड़ने पर, सोर्स एलिमेंट 100% ओपैसिटी पर वापस आ जाता है. भले ही, आपने अभी तक वैल्यू में गिरावट न की हो.

ज़्यादा विज़ुअल संकेत जोड़ें

उपयोगकर्ता को आपके इंटरफ़ेस के साथ इंटरैक्ट करने का तरीका समझाने के लिए, dragenter, dragover, और dragleave इवेंट हैंडलर का इस्तेमाल करें. इस उदाहरण में, कॉलम को खींचकर छोड़ा जा सकता है. साथ ही, ये कॉलम ड्रॉप टारगेट भी हैं. इसे समझने में उपयोगकर्ता की मदद करें. इसके लिए, जब वे खींचकर छोड़े गए किसी आइटम को कॉलम पर पकड़ें, तब बॉर्डर पर डैश लगाएं. उदाहरण के लिए, अपनी सीएसएस में, ड्रॉप टारगेट एलिमेंट के लिए over क्लास बनाई जा सकती है:

.box.over {
  border: 3px dotted #666;
}

इसके बाद, अपने JavaScript में इवेंट हैंडलर सेट अप करें. कॉलम को खींचकर छोड़ने पर over क्लास जोड़ें. साथ ही, ड्रैग एलिमेंट छोड़ने पर इसे हटा दें. dragend हैंडलर में, हम यह भी पक्का करते हैं कि खींचने और छोड़ने के आखिर में क्लास हटा दी जाएं.

document.addEventListener('DOMContentLoaded', (event) => {

  function handleDragStart(e) {
    this.style.opacity = '0.4';
  }

  function handleDragEnd(e) {
    this.style.opacity = '1';

    items.forEach(function (item) {
      item.classList.remove('over');
    });
  }

  function handleDragOver(e) {
    e.preventDefault();
    return false;
  }

  function handleDragEnter(e) {
    this.classList.add('over');
  }

  function handleDragLeave(e) {
    this.classList.remove('over');
  }

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });
});

इस कोड में कुछ बातें ध्यान देने वाली हैं:

  • dragover इवेंट के लिए, डिफ़ॉल्ट कार्रवाई का इस्तेमाल करके, dataTransfer.dropEffect प्रॉपर्टी को "none" पर सेट किया जाता है. dropEffect प्रॉपर्टी के बारे में इस पेज पर आगे बताया गया है. फ़िलहाल, सिर्फ़ इतना जानें कि यह drop इवेंट को ट्रिगर होने से रोकता है. इस व्यवहार को बदलने के लिए, e.preventDefault() पर कॉल करें. एक और अच्छा तरीका यह है कि उसी हैंडलर में false को वापस लाया जाए.

  • dragenter इवेंट हैंडलर का इस्तेमाल, dragover के बजाय over क्लास को टॉगल करने के लिए किया जाता है. अगर dragover का इस्तेमाल किया जाता है, तो इवेंट बार-बार तब ट्रिगर होता है, जब उपयोगकर्ता खींचे गए आइटम को कॉलम पर रखता है. इसकी वजह से, सीएसएस क्लास बार-बार टॉगल करती है. इससे ब्राउज़र को रेंडर करने के लिए बहुत ज़्यादा काम करना पड़ता है, जिससे उपयोगकर्ता अनुभव पर असर पड़ सकता है. हमारा सुझाव है कि आप फिर से ड्रॉ करने की संख्या कम करें. अगर आपको dragover का इस्तेमाल करना है, तो अपने इवेंट लिसनर को थ्रॉटल करें या डीबाउंस करें.

ड्रॉप पूरा करना

लॉन्च की प्रोसेस पूरी करने के लिए, drop इवेंट के लिए इवेंट लिसनर जोड़ें. drop हैंडलर में, आपको ड्रॉप के लिए ब्राउज़र के डिफ़ॉल्ट व्यवहार को रोकना होगा. आम तौर पर, यह किसी तरह का परेशान करने वाला रीडायरेक्ट होता है. ऐसा करने के लिए, e.stopPropagation() को कॉल करें.

function handleDrop(e) {
  e.stopPropagation(); // stops the browser from redirecting.
  return false;
}

अन्य हैंडलर के साथ नए हैंडलर को रजिस्टर करना न भूलें:

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });

अगर इस समय कोड चलाया जाता है, तो आइटम नई जगह पर नहीं जाता. ऐसा करने के लिए, DataTransfer ऑब्जेक्ट का इस्तेमाल करें.

खींचकर छोड़ने की कार्रवाई में भेजा गया डेटा dataTransfer प्रॉपर्टी में सेव होता है. dataTransfer को dragstart इवेंट में सेट किया जाता है और ड्रॉप इवेंट में पढ़ा या मैनेज किया जाता है. e.dataTransfer.setData(mimeType, dataPayload) को कॉल करके, ऑब्जेक्ट का MIME टाइप और डेटा पेलोड सेट किया जा सकता है.

इस उदाहरण में, हम उपयोगकर्ताओं को कॉलम का क्रम बदलने की सुविधा देंगे. ऐसा करने के लिए, सबसे पहले आपको सोर्स एलिमेंट का एचटीएमएल तब सेव करना होगा, जब खींचना शुरू हो जाए:

function handleDragStart(e) {
  this.style.opacity = '0.4';

  dragSrcEl = this;

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.innerHTML);
}

drop इवेंट में, सोर्स कॉलम के एचटीएमएल को उस टारगेट कॉलम के एचटीएमएल पर सेट करके कॉलम ड्रॉप को प्रोसेस किया जाता है जिस पर आपने डेटा छोड़ा था. इसमें यह जांचना भी शामिल है कि उपयोगकर्ता उसी कॉलम पर वापस तो नहीं आ रहा है, जिससे उसने खींची थी.

function handleDrop(e) {
  e.stopPropagation();

  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = e.dataTransfer.getData('text/html');
  }

  return false;
}

नीचे दिए गए डेमो में आप नतीजे देख सकते हैं. यह सुविधा काम करे, इसके लिए आपको डेस्कटॉप ब्राउज़र की ज़रूरत होगी. 'खींचें और छोड़ें' एपीआई मोबाइल पर काम नहीं करता. A कॉलम को खींचकर, B कॉलम के ऊपर छोड़ें और देखें कि वे कैसे अपनी जगह बदलते हैं:

खींचकर छोड़ने की ज़्यादा प्रॉपर्टी

dataTransfer ऑब्जेक्ट, प्रॉपर्टी को दिखाता है, ताकि उपयोगकर्ता को खींचने और छोड़ने की प्रोसेस के दौरान विज़ुअल फ़ीडबैक दिया जा सके. साथ ही, यह कंट्रोल करता है कि हर ड्रॉप टारगेट, किसी खास डेटा टाइप पर कैसे प्रतिक्रिया देता है.

  • dataTransfer.effectAllowed इससे यह तय होता है कि उपयोगकर्ता, एलिमेंट पर किस तरह का 'खींचें और छोड़ें' कार्रवाई कर सकता है. इसका इस्तेमाल, dragenter और dragover इवेंट के दौरान dropEffect को शुरू करने के लिए, खींचें और छोड़ें वाले प्रोसेसिंग मॉडल में किया जाता है. प्रॉपर्टी में ये वैल्यू हो सकती हैं: none, copy, copyLink, copyMove, link, linkMove, move, all, और uninitialized.
  • dataTransfer.dropEffect, dragenter और dragover इवेंट के दौरान उपयोगकर्ता को मिलने वाले सुझाव, शिकायत या राय को कंट्रोल करता है. जब उपयोगकर्ता अपने पॉइंटर को किसी टारगेट एलिमेंट के ऊपर रखता है, तो ब्राउज़र का कर्सर बताता है कि किस तरह की कार्रवाई होने वाली है. जैसे, कॉपी या मूव करना. इफ़ेक्ट के लिए, इनमें से कोई एक वैल्यू दी जा सकती है: none, copy, link, move.
  • e.dataTransfer.setDragImage(imgElement, x, y) का मतलब है कि ब्राउज़र के डिफ़ॉल्ट 'घोस्ट इमेज' फ़ीडबैक का इस्तेमाल करने के बजाय, आपके पास ड्रैग आइकॉन सेट करने का विकल्प है.

फ़ाइल अपलोड करें

इस आसान उदाहरण में, कॉलम को ड्रैग सोर्स और ड्रैग टारगेट, दोनों के तौर पर इस्तेमाल किया गया है. ऐसा उस यूज़र इंटरफ़ेस (यूआई) में हो सकता है जिसमें उपयोगकर्ता से आइटम को फिर से क्रम में लगाने के लिए कहा जाता है. कुछ मामलों में, खींचें और छोड़ें टारगेट और सोर्स, अलग-अलग तरह के एलिमेंट हो सकते हैं. जैसे, किसी इंटरफ़ेस में, जहां उपयोगकर्ता को किसी प्रॉडक्ट के लिए मुख्य इमेज के तौर पर एक इमेज चुननी होती है. इसके लिए, उसे चुनी गई इमेज को टारगेट पर खींचकर छोड़ना होता है.

'खींचें और छोड़ें' सुविधा का अक्सर इस्तेमाल किया जाता है, ताकि उपयोगकर्ता अपने डेस्कटॉप से आइटम को किसी ऐप्लिकेशन में खींचकर छोड़ सकें. मुख्य अंतर आपके drop हैंडलर में है. फ़ाइलों को ऐक्सेस करने के लिए dataTransfer.getData() का इस्तेमाल करने के बजाय, उनका डेटा dataTransfer.files प्रॉपर्टी में शामिल होता है:

function handleDrop(e) {
  e.stopPropagation(); // Stops some browsers from redirecting.
  e.preventDefault();

  var files = e.dataTransfer.files;
  for (var i = 0, f; (f = files[i]); i++) {
    // Read the File objects in this FileList.
  }
}

इस बारे में ज़्यादा जानकारी पाने के लिए, कस्टम ड्रैग-एंड-ड्रॉप लेख पढ़ें.

ज़्यादा रिसॉर्स