Skip to content

Drag'n'Drop with mouse events #322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Mar 26, 2022
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
As we can see from HTML/CSS, the slider is a `<div>` with a colored background, that contains a runner -- another `<div>` with `position:relative`.
Як можна бачити з `HTML/CSS`, слайдер -- це `<div>`, з кольровим фоном, всередині якого знаходиться інший `<div>`, оформлений як бігунок, з `position: relative`.

To position the runner we use `position:relative`, to provide the coordinates relative to its parent, here it's more convenient here than `position:absolute`.
Використаємо для його позиціювання `position: relative`, тобто координати встановлюються не абсолютні (`position: absolute`), а відносно батьківського елементу, бо це зручніше.

Then we implement horizontal-only Drag'n'Drop with limitation by width.
І далі реалізуємо Drag'n'Drop тільки по горизонталі, з обмеженням по ширині.
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@
let thumb = slider.querySelector('.thumb');

thumb.onmousedown = function(event) {
event.preventDefault(); // prevent selection start (browser action)
event.preventDefault(); // запобігає запуску виділення (типова дія браузера)

let shiftX = event.clientX - thumb.getBoundingClientRect().left;
// shiftY not needed, the thumb moves only horizontally
// shiftY не потрібен, слайдер рухається тільки по горизонталі

document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);

function onMouseMove(event) {
let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left;

// the pointer is out of slider => lock the thumb within the bounaries
// курсор вийшов за межі слайдера => зафіксуємо бігунок в межах слайдера.
if (newLeft < 0) {
newLeft = 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</div>

<script>
// ...your code...
// ...ваш код...
</script>

</body>
Expand Down
12 changes: 6 additions & 6 deletions 2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ importance: 5

---

# Slider
# Слайдер

Create a slider:
Створіть слайдер:

[iframe src="solution" height=60 border=1]

Drag the blue thumb with the mouse and move it.
Наведіть курсор миші на бігунок слайдеру, затисніть кнопку миші і рухайте бігунок переміщаючи курсор.

Important details:
Важливі деталі:

- When the mouse button is pressed, during the dragging the mouse may go over or below the slider. The slider will still work (convenient for the user).
- If the mouse moves very fast to the left or to the right, the thumb should stop exactly at the edge.
- При натиснутій кнопці миші, курсор може виходити за межі слайдера, але слайдер все одно має працювати (це зручно для користувача).
- Слайдер повинен нормально працювати при різкому русі миші ливоруч або праворуч за межі слайдера. При цьому бігунок повинен зупинятися чітко біля його краю.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
To drag the element we can use `position:fixed`, it makes coordinates easier to manage. At the end we should switch it back to `position:absolute` to lay the element into the document.
Щоб перетягнути елемент, ми можемо використовувати `position: fixed`, це робить управління координатами простішим. Після завершення слід переключитися назад на `position: absolute`, щоб елемент залишився частиною сторінки.

When coordinates are at window top/bottom, we use `window.scrollTo` to scroll it.
Коли координати знаходяться у верхній/нижній частині вікна, ми використовуємо `window.scrollTo` для прокрутки.

More details in the code, in comments.
Деталі рішення розписані в коментарях у коді.
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@

<body>

<h2>Place superheroes around the soccer field.</h2>

<p>Superheroes and the ball are elements with the class "draggable". Make them really draggable.</p>

<p>Important: limit dragging by the window. If a draggable reaches window top or bottom, then the page should scroll to let us drag it further.</p>

<p>If your screen is big enough to fit the whole document -- make the window smaller to get vertical scrolling, so that you could test it.</p>

<p>In this task it's enough to handle vertical scrolling. There's no horizontal scrolling usually, and it's handled the similar way if needed.</p>

<p>And one more thing: heroes may never leave the page. If they reach the edge of the document, no dragging outside of it.</p>

<h2>Розставте супергероїв полем.</h2>

<p>Супергерої і м’яч -- це елементи з класом "draggable". Зробіть так, щоб їх можна було переносити.</p>
<p>Важливо: обмежити перетягування межами вікна. Якщо супергероя підносять до верхньої або нижньої межі вікна, сторінка повинна автоматично прокручуватися.</p>
<p>Якщо сторінка міститься на екрані повністю і не має вертикальної прокрутки -- зробіть вікно браузера менше, щоб протестувати цю можливість.</p>
<p>У цьому завданні достатньо впоратися з вертикальною прокруткою. Зазвичай горизонтальна прокрутка відсутня, вона обробляється аналогічно, якщо це необхідно.</p>
<p>Так, і ще: супергерої ні за яких умов не повинні потрапити за край екрану.</p>
<div id="field">

</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ html, body {
float: left;
}

/* heroes and the ball (dragables) */
/* герої і м’яч (dragables) */

.hero {
background: url(https://js.cx/drag-heroes/heroes.png);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ document.addEventListener('mousedown', function(event) {
moveAt(event.clientX, event.clientY);
}

// on drag start:
// remember the initial shift
// move the element position:fixed and a direct child of body
// на початку переміщення елемента:
// зберегаємо місце кліку по елементу
// перемикаємо позиціонування елемента (position: fixed) і рухаємо елемент
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// перемикаємо позиціонування елемента (position: fixed) і рухаємо елемент
// перемикаємо позиціонування елемента (position: fixed) і рухаємо елемент

function startDrag(element, clientX, clientY) {
if(isDragging) {
return;
Expand All @@ -45,7 +45,7 @@ document.addEventListener('mousedown', function(event) {
moveAt(clientX, clientY);
};

// switch to absolute coordinates at the end, to fix the element in the document
// перемикаємося назад на абсолютні координати щоб закріпити елемент відносно документа
function finishDrag() {
if(!isDragging) {
return;
Expand All @@ -61,49 +61,49 @@ document.addEventListener('mousedown', function(event) {
}

function moveAt(clientX, clientY) {
// new window-relative coordinates
// обчислюємо нові координати (відносно вікна)
let newX = clientX - shiftX;
let newY = clientY - shiftY;

// check if the new coordinates are below the bottom window edge
let newBottom = newY + dragElement.offsetHeight; // new bottom
// перевіряємо, чи не виходять нові координати за нижній край вікна:
let newBottom = newY + dragElement.offsetHeight; // нові координати

// below the window? let's scroll the page
// виходять за межі вікна? прокручуємо сторінку
if (newBottom > document.documentElement.clientHeight) {
// window-relative coordinate of document end
// координата нижнього краю документа щодо вікна
let docBottom = document.documentElement.getBoundingClientRect().bottom;

// scroll the document down by 10px has a problem
// it can scroll beyond the end of the document
// Math.min(how much left to the end, 10)
// прокрутка документа на 10px вниз має проблему --
// він може прокрутити документ за його межі,
// тому використовуємо Math.min (відстань до кінця, 10)
let scrollY = Math.min(docBottom - newBottom, 10);

// calculations are imprecise, there may be rounding errors that lead to scrolling up
// that should be impossible, fix that here
// обчислення можуть бути не зовсім точні -- трапляються помилки при округленні,
// які призводять до негативного значенням прокрутки. Відфільтруємо їх:
if (scrollY < 0) scrollY = 0;

window.scrollBy(0, scrollY);

// a swift mouse move make put the cursor beyond the document end
// if that happens -
// limit the new Y by the maximally possible (right at the bottom of the document)
// швидке переміщення миші може помістити курсор за межі документа вниз
// якщо це сталося -
// обмежуємо нове значення Y максимально можливим, виходячи з розміру документа:
newY = Math.min(newY, document.documentElement.clientHeight - dragElement.offsetHeight);
}

// check if the new coordinates are above the top window edge (similar logic)
// перевіряємо, чи не переходять нові координати за верхній край вікна (за схожим алгоритмом)
if (newY < 0) {
// scroll up
let scrollY = Math.min(-newY, 10);
if (scrollY < 0) scrollY = 0; // check precision errors
if (scrollY < 0) scrollY = 0; // перевіряємо помилки точності

window.scrollBy(0, -scrollY);
// a swift mouse move can put the cursor beyond the document start
newY = Math.max(newY, 0); // newY may not be below 0
// швидке переміщення миші може помістити курсор за межі документа зверху
newY = Math.max(newY, 0); // newY не може бути менше нуля
}


// limit the new X within the window boundaries
// there's no scroll here so it's simple
// обмежимо newX розмірами вікна
// згідно з умовою, горизонтальна прокрутка відсутня, тому це не складно:
if (newX < 0) newX = 0;
if (newX > document.documentElement.clientWidth - dragElement.offsetWidth) {
newX = document.documentElement.clientWidth - dragElement.offsetWidth;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@

<body>

<h2>Place superheroes around the soccer field.</h2>

<p>Superheroes and the ball are elements with the class "draggable". Make them really draggable.</p>

<p>Important: limit dragging by the window. If a draggable reaches window top or bottom, then the page should scroll to let us drag it further.</p>

<p>If your screen is big enough to fit the whole document -- make the window smaller to get vertical scrolling, so that you could test it.</p>

<p>In this task it's enough to handle vertical scrolling. There's no horizontal scrolling usually, and it's handled the similar way if needed.</p>

<p>And one more thing: heroes may never leave the page. If they reach the edge of the document, no dragging outside of it.</p>

<h2>Розставте супергероїв полем.</h2>

<p>Супергерої і м’яч - це елементи з класом "draggable". Зробіть так, щоб їх можна було переносити.</p>
<p>Важливо: обмежити перетягування межами вікна. Якщо супергероя підносять до верхньої або нижньої межі сторінки, вона повинна автоматично прокручуватися.</p>
<p>Якщо сторінка поміщається на вашому екрані цілком і не має вертикальної прокрутки - зробіть вікно браузера менше, щоб протестувати цю можливість.</p>
<p>У цьому завданні достатньо впоратися з вертикальною прокруткою. Зазвичай немає горизонтальної прокрутки, і вона обробляється аналогічним чином, якщо це необхідно.</p>
<p>Так, і ще: супергерої ні за яких умов не повинні потрапити за край екрану.</p>
<div id="field">

</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ html, body {
float: left;
}

/* heroes and the ball (dragables) */
/* герої і м’яч (dragables) */

.hero {
background: url(https://js.cx/drag-heroes/heroes.png);
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
// Your code
// Ваш код
18 changes: 9 additions & 9 deletions 2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ importance: 5

---

# Drag superheroes around the field
# Розставте супергероїв полем

This task can help you to check understanding of several aspects of Drag'n'Drop and DOM.
У цьому завданні ви можете перевірити своє розуміння відразу декількох аспектів Drag'n'Drop і DOM.

Make all elements with class `draggable` -- draggable. Like a ball in the chapter.
Зробіть так, щоб елементи з класом `draggable` -- можна було переносити мишкою. Як м’яч в цьому розділі.

Requirements:
Вимоги до реалізації:

- Use event delegation to track drag start: a single event handler on `document` for `mousedown`.
- If elements are dragged to top/bottom window edges -- the page scrolls up/down to allow further dragging.
- There is no horizontal scroll (this makes the task a bit simpler, adding it is easy).
- Draggable elements or their parts should never leave the window, even after swift mouse moves.
- Використовуйте делегування подій для відстеження початку перетягування: тільки один обробник подій `mousedown` на `document`.
- Якщо елементи підносять до верхньої/нижньої межі вікна -- вікно повинне прокручуватися вгору/вниз, щоб дозволити подальше перетягування.
- Горизонтальна прокрутка відсутня (трохи спрощує завдання, її просто додати).
- Елемент при перенесенні, навіть при різких рухах мишкою, не повинен навіть частково потрапити поза вікно.

The demo is too big to fit it here, so here's the link.
Демо занадто велике для розміщення тут, перейдіть за посиланням нижче.

[demo src="solution"]
Loading