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.
  }
}

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

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