ড্র্যাগ করা যায় এমন এলিমেন্ট তৈরি করা

প্যানেল, প্যালেট, টুলবার, ইন্সপেক্টর বা ডায়ালগের মতো আগে থেকে থাকা কোনও ফ্লোটিং এলিমেন্টে ড্র্যাগিং যোগ করুন। এলিমেন্ট কীভাবে দেখানো, বাতিল, স্ট্যাক বা ফোকাস করা হবে তা টেনে আনার মাধ্যমে নির্ধারণ করা হয় না।

একটি ডেডিকেটেড হ্যান্ডেল ব্যবহার করুন

এলিমেন্টে বেছে নেওয়ার মতো টেক্সট, বোতাম, ইনপুট বা লিঙ্ক থাকলে, নির্দিষ্ট হ্যান্ডেল থেকে ড্র্যাগ করা শুরু করুন। এটি এর সাধারণ কন্টেন্ট ব্যবহারযোগ্য রাখে। কীবোর্ড মুভমেন্টের জন্য হ্যান্ডেল ফোকাসযোগ্য করুন এবং এটিকে একটি অ্যাক্সেসযোগ্য নাম দিন যা তীরচিহ্ন-কী ইন্টার‍্যাকশন উল্লেখ করে।

হ্যান্ডেলে role="application" প্রয়োগ করবেন না। এটি সহায়ক-প্রযুক্তি ইন্টার‍্যাকশনকে অপ্রয়োজনীয়ভাবে পরিবর্তন করে।

হ্যান্ডেলে cursor: grab/grabbing, touch-action: none এবং user-select: none ও -webkit-user-select: none উভয়ই প্রয়োগ করুন—পুরো ড্র্যাগেবল এলিমেন্টে নয়—যাতে এর বাইরের টেক্সট বেছে নেওয়া যায় এবং টাচ জেসচার স্বাভাবিকভাবে কাজ করে:

.drag-handle {
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.drag-handle.dragging {
  cursor: grabbing;
}

পয়েন্টারের অবস্থান ও রিচেবিলিটি বজায় রাখুন

ভিউ-পোর্টে মুভমেন্ট ক্ল্যাম্প করার সময় ভিউ-পোর্ট-রিলেটিভ পজিশনিং মডেল ব্যবহার করুন, যেমন position: fixed।

  • শুধুমাত্র প্রাইমারি পয়েন্টার বোতামের (event.button === 0) জন্য টেনে আনার প্রক্রিয়া শুরু করুন।
  • pointerdown-এ পয়েন্টার অফসেট রেকর্ড করুন এবং প্রতিটি মুভে এটি ব্যবহার করুন যাতে এলিমেন্টটি তার কোণ থেকে দূরে সরানোর সময় লাফিয়ে না যায়।
  • পয়েন্টার হ্যান্ডেল থেকে সরে যাওয়ার পরেও টেনে আনার অ্যাক্টিভিটি চালু রাখুন। setPointerCapture()-এ ডিফল্ট করুন এবং lostpointercapture-এ টেনে আনার স্ট্যাটাস মুছে দিন। মিড-ড্র্যাগ করার সময় হ্যান্ডেল সরিয়ে দেওয়া বা আবার রেন্ডার করা হলে, তার পরিবর্তে ডকুমেন্ট-লেভেল pointermove, pointerup ও pointercancel লিসনার ব্যবহার করুন, কারণ সেই ক্ষেত্রে ক্যাপচার হারিয়ে যায়।
  • বিকল্প হিসেবে, ড্র্যাগিং স্টেট শুরু হওয়ার আগে প্রায় ৪ পিক্সেল মুভমেন্টের জন্য অপেক্ষা করুন, যাতে হ্যান্ডেলে ক্লিক করলে ড্র্যাগ স্টাইলিং ট্রিগার না হয়।

পয়েন্টার ক্যাপচারের মাধ্যমে, মুভমেন্ট লিসেনার হ্যান্ডেলে থাকতে পারে:

handle.addEventListener('pointerdown', (event) => {
  if (event.button !== 0 || event.target.closest('button, input, select, textarea, a')) return;

  const rect = draggable.getBoundingClientRect();
  offsetX = event.clientX - rect.left;
  offsetY = event.clientY - rect.top;
  handle.setPointerCapture(event.pointerId);
});

handle.addEventListener('pointermove', (event) => {
  if (!handle.hasPointerCapture(event.pointerId)) return;
  handle.classList.add('dragging');
  moveTo(event.clientX - offsetX, event.clientY - offsetY);
});

handle.addEventListener('lostpointercapture', () => {
  handle.classList.remove('dragging');
});

function moveTo(left, top) {
  const maxLeft = Math.max(0, window.innerWidth - draggable.offsetWidth);
  const maxTop = Math.max(0, window.innerHeight - draggable.offsetHeight);

  draggable.style.left = `${Math.max(0, Math.min(left, maxLeft))}px`;
  draggable.style.top = `${Math.max(0, Math.min(top, maxTop))}px`;
}

একই হেল্পারের মাধ্যমে প্রতিটি পয়েন্টার ও কীবোর্ড পজিশন ক্ল্যাম্প করুন যাতে এলিমেন্টটি অ্যাক্সেসযোগ্য থাকে। ভিউ-পোর্ট বা এলিমেন্টের সাইজ পরিবর্তন করার পরে বর্তমান পজিশনে আবার ক্ল্যাম্প করুন। লেআউট-হেভি এলিমেন্টের জন্য, প্রতি-ফ্রেম লেআউট এড়াতে ড্র্যাগ করার সময় অনুবাদ করার কথা বিবেচনা করুন, তারপর রিলিজ করার সময় চূড়ান্ত পজিশন left এবং top-এ কমিট করুন।

কীবোর্ড মুভমেন্টের সুবিধা দিন

হ্যান্ডেল ফোকাসে থাকলে, তীরচিহ্নযুক্ত কী ব্যবহার করে এলিমেন্ট সরান এবং সেইসব কী-এর জন্য preventDefault() কল করুন। পয়েন্টার মুভমেন্টের মতো একই ক্ল্যাম্পড পজিশনিং হেল্পার ব্যবহার করুন। ১৫ পিক্সেল ইনক্রিমেন্ট হল একটি যুক্তিসঙ্গত শুরুর পয়েন্ট; কম্পোনেন্টের সাইজ এবং নির্ভুলতার প্রয়োজন অনুযায়ী এটি মানিয়ে নিন।

ডায়ালগ-নির্দিষ্ট নোট

<dialog> বা [popover] এই মেকানিজম ব্যবহার করতে পারে, যার হ্যান্ডেল হিসেবে হেডার ব্যবহার করা হয়। left এবং top অ্যাসাইন করার আগে margin: 0 সেট করুন, কারণ ব্রাউজারের ডিফল্ট মার্জিন ম্যানুয়াল পজিশনিংয়ে বাধা সৃষ্টি করে। ডায়ালগ ও পপওভার খোলা, বন্ধ করা, ফোকাস ও বাতিল করার আচরণ সম্পর্কে জানতে, ডিক্লারেটিভ ডায়ালগ ও পপওভার কন্ট্রোল এবং প্ল্যাটফর্ম কন্ট্রোল ডিসমিস ডায়ালগ দেখুন।

ফলব্যাক স্ট্র্যাটেজি

টেনে আনার মাধ্যমে কোনও কম্পোনেন্ট শুধুমাত্র নতুন জায়গায় নিয়ে যাওয়া হলে, সেটিকে প্রোগ্রেসিভ এনহ্যান্সমেন্ট হিসেবে বিবেচনা করুন: জাভাস্ক্রিপ্ট ছাড়া, এলিমেন্ট, তার কন্টেন্ট ও কন্ট্রোল, ডিফল্ট পজিশনে দৃশ্যমান, পাঠযোগ্য ও কার্যকর রাখুন। কাজ করে না এমন ড্র্যাগ হ্যান্ডেল দেখাবেন না: হ্যান্ডেলটি যদি একটি স্বতন্ত্র গ্রিপ কন্ট্রোল হয়, তাহলে ডিফল্ট হিসেবে এটি লুকান এবং জাভাস্ক্রিপ্ট শুরু হলে এটি দেখান; যদি একটি হেডার হ্যান্ডেল হিসেবে দ্বিগুণ হয়, তাহলে জাভাস্ক্রিপ্ট শুরু হলে এর ড্র্যাগ অ্যাফর্ডেন্স অ্যাটাচ করুন।

user-select: none-এর আগে -webkit-user-select: none অন্তর্ভুক্ত করুন যাতে Safari-তে টেনে আনার সময় টেক্সট বেছে নেওয়ার সুবিধা বন্ধ থাকে।