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

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

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

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

इस बारे में ज़्यादा जानकारी पाने के लिए, पसंद के मुताबिक खींचें और छोड़ें सेक्शन में जाएं.

कई और संसाधन