Мышь: Drag’n’Drop
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/mouse-drag-and-drop.
Drag’n’Drop – это возможность захватить мышью элемент и перенести его. В своё время это было замечательным открытием в области интерфейсов, которое позволило упростить большое количество операций.
Перенос мышкой может заменить целую последовательность кликов. И, самое главное, он упрощает внешний вид интерфейса: функции, реализуемые через Drag’n’Drop, в ином случае потребовали бы дополнительных полей, виджетов и т.п.
Отличия от HTML5 Drag’n’Drop
В современном стандарте HTML5 есть поддержка Drag’n’Drop при помощи специальных событий.
Эти события поддерживаются всеми современными браузерами, и у них есть свои интересные особенности, например, можно перетащить файл в браузер, так что JS получит доступ к его содержимому. Они заслуживают отдельного рассмотрения.
Но в плане именно Drag’n’Drop у них есть существенные ограничения. Например, нельзя организовать перенос «только по горизонтали» или «только по вертикали». Также нельзя ограничить перенос внутри заданной зоны. Есть и другие интерфейсные задачи, которые такими встроенными событиями нереализуемы.
Поэтому здесь мы будем рассматривать Drag’n’Drop при помощи событий мыши.
Рассматриваемые приёмы, вообще говоря, применяются не только в Drag’n’Drop, но и для любых интерфейсных взаимодействий вида «захватить – потянуть – отпустить».
Алгоритм Drag’n’Drop
Основной алгоритм Drag’n’Drop выглядит так:
- Отслеживаем нажатие кнопки мыши на переносимом элементе при помощи события mousedown .
- При нажатии – подготовить элемент к перемещению.
- Далее отслеживаем движение мыши через mousemove и передвигаем переносимый элемент на новые координаты путём смены left/top и position:absolute .
- При отпускании кнопки мыши, то есть наступлении события mouseup – остановить перенос элемента и произвести все действия, связанные с окончанием Drag’n’Drop.
В следующем примере эти шаги реализованы для переноса мяча:
var ball = document.getElementById('ball'); ball.onmousedown = function(e) < // 1. отследить нажатие // подготовить к перемещению // 2. разместить на том же месте, но в абсолютных координатах ball.style.position = 'absolute'; moveAt(e); // переместим в body, чтобы мяч был точно не внутри position:relative document.body.appendChild(ball); ball.style.zIndex = 1000; // показывать мяч над другими элементами // передвинуть мяч под координаты курсора // и сдвинуть на половину ширины/высоты для центрирования function moveAt(e) < ball.style.left = e.pageX - ball.offsetWidth / 2 + 'px'; ball.style.top = e.pageY - ball.offsetHeight / 2 + 'px'; >// 3, перемещать по экрану document.onmousemove = function(e) < moveAt(e); >// 4. отследить окончание переноса ball.onmouseup = function() < document.onmousemove = null; ball.onmouseup = null; >>
Если запустить этот код, то мы заметим нечто странное. При начале переноса мяч «раздваивается» и переносится не сам мяч, а его «клон».
Это можно увидеть в действии внутри ифрейма:
Попробуйте перенести мяч мышкой и вы увидите описанное, довольно-таки странное, поведение.
Это потому, что браузер имеет свой собственный Drag’n’Drop, который автоматически запускается и вступает в конфликт с нашим. Это происходит именно для картинок и некоторых других элементов.
Его нужно отключить:
ball.ondragstart = function() < return false; >;
Теперь всё будет в порядке.
В действии (внутри ифрейма):
Ещё одна особенность правильного Drag’n’Drop – событие mousemove отслеживается на document , а не на ball .
С первого взгляда кажется, что мышь всегда над мячом и обработчик mousemove можно повесить на сам мяч, а не на документ.
Однако, на самом деле мышь во время переноса не всегда над мячом.
Вспомним, событие mousemove возникает хоть и часто, но не для каждого пикселя. Быстрое движение курсора вызовет mousemove уже не над мячом, а, например, в дальнем конце страницы.
Вот почему мы должны отслеживать mousemove на всём document .
Правильное позиционирование
В примерах выше мяч позиционируется в центре под курсором мыши:
self.style.left = e.pageX - ball.offsetWidth / 2 + 'px'; self.style.top = e.pageY - ball.offsetHeight / 2 + 'px';
Если поставить left/top ровно в pageX/pageY , то мячик прилипнет верхним-левым углом к курсору мыши. Будет некрасиво. Поэтому мы сдвигаем его на половину высоты/ширины, чтобы был центром под мышью. Уже лучше.
Но не идеально. В частности, в самом начале переноса, особенно если мячик «взят» за край – он резко «прыгает» центром под курсор мыши.
Для правильного переноса необходимо, чтобы изначальный сдвиг курсора относительно элемента сохранялся.
Где захватили, за ту «часть элемента» и переносим:

-
Когда человек нажимает на мячик mousedown – курсор сдвинут относительно левого-верхнего угла мяча на расстояние, которое мы обозначим shiftX/shiftY . И нужно при переносе сохранить этот сдвиг. Получить значения shiftX/shiftY легко: достаточно вычесть из координат курсора pageX/pageY левую-верхнюю границу мячика, полученную при помощи функции getCoords. При Drag’n’Drop мы везде используем координаты относительно документа, так как они подходят в большем количестве ситуаций. Конечно же, не проблема перейти к координатам относительно окна, если это понадобится. Достаточно использовать position:fixed , elem.getBoundingClientRect() для определения координат и e.clientX/Y .
// onmousedown shiftX = e.pageX - getCoords(ball).left; shiftY = e.pageY - getCoords(ball).top;
// onmousemove ball.style.left = e.pageX - shiftX + 'px'; ball.style.top = e.pageY - shiftY + 'px';
Итоговый код с правильным позиционированием:
var ball = document.getElementById('ball'); ball.onmousedown = function(e) < var coords = getCoords(ball); var shiftX = e.pageX - coords.left; var shiftY = e.pageY - coords.top; ball.style.position = 'absolute'; document.body.appendChild(ball); moveAt(e); ball.style.zIndex = 1000; // над другими элементами function moveAt(e) < ball.style.left = e.pageX - shiftX + 'px'; ball.style.top = e.pageY - shiftY + 'px'; >document.onmousemove = function(e) < moveAt(e); >; ball.onmouseup = function() < document.onmousemove = null; ball.onmouseup = null; >; > ball.ondragstart = function() < return false; >; function getCoords(elem) < // кроме IE8- var box = elem.getBoundingClientRect(); return < top: box.top + pageYOffset, left: box.left + pageXOffset >; >
В действии (внутри ифрейма):
Различие особенно заметно, если захватить мяч за правый-нижний угол. В предыдущем примере мячик «прыгнет» серединой под курсор, в этом – будет плавно переноситься с текущей позиции.
Итого
Мы рассмотрели «минимальный каркас» Drag’n’Drop .
- События ball.mousedown → document.mousemove → ball.mouseup .
- Передвижение с учётом изначального сдвига shiftX/shiftY .
- Отмена действия браузера по событию dragstart .
На этой основе можно сделать очень многое.
- При mouseup можно обработать окончание переноса, произвести изменения в данных, если они нужны.
- Во время самого переноса можно подсвечивать элементы, над которыми проходит элемент.
- При обработке событий mousedown и mouseup можно использовать делегирование, так что одного обработчика достаточно для управления переносом в зоне с сотнями элементов.
Это и многое другое мы рассмотрим в статье про Drag’n’Drop объектов.
16 posts tagged with «react.js»
Zustand (читается как «цуштанд», что переводится с немецкого как «состояние») — это, на мой взгляд, один из лучших на сегодняшний день инструментов для управления состоянием приложений, написанных на React.
В этой статье я немного расскажу о том, как он работает.
Давайте начнем с примера использования zustand для реализации функционала отображения/скрытия модального окна.
- react.js
- reactjs
- react
- zustand
- reverse engineering
- state management
- управление состоянием
- обратная разработка
Как React 18 улучшает производительность приложения
July 24, 2023 · 13 min read
JavaScript Developer
React 18 представил конкурентные (concurrent) возможности, которые радикально меняют способ рендеринга приложений. В этой статье мы рассмотрим, как эти возможности улучшают производительность приложения.
Начнем с повторения основ «долгих (долго выполняющихся) задач» (long tasks) и соответствующих метрик производительности.
RSC с нуля. Часть 1. Серверные компоненты
June 14, 2023 · 35 min read
JavaScript Developer
В этом техническом «глубоком погружении» (deep dive) мы с нуля реализуем очень простую версию серверных компонентов React.
Данный туториал будет состоять из трех частей (написана пока только эта).
Интересная схема работы с формами в React
March 21, 2023 · 13 min read
JavaScript Developer
В этой статье я хочу рассказать о схеме (назовем ее так) работы с формами в React, которая на сегодняшний день кажется мне наиболее эффективной. Эта схема предполагает использование React Hook Form для обработки форм и Zod для валидации пользовательских данных. Применение данной схемы имеет несколько существенных преимуществ по сравнению с использованием других решений или реализацией необходимого функционала вручную. Главными преимуществами являются минимизация количества шаблонного кода и автоматическое выведение типов (type inference).
Для тех, кого интересует только код, вот ссылка на соответствующий репозиторий.
Интересно? Тогда прошу под кат.
Разрабатываем расширение для браузера
March 6, 2023 · 7 min read
JavaScript Developer
В этом небольшом туториале мы с вами разработаем простое, но полезное расширение для браузера с помощью Plasmo.
Наше расширение будет представлять собой вызываемый сочетанием клавиш попап с инпутом для поиска информации на MDN с выводом 5 лучших результатов в виде списка. Кроме основного функционала, мы добавим страницу настроек для кастомизации цветов и отображения хлебных крошек. Мы будем разрабатывать расширения для Chrome, которое также будет работать в Firefox.
Вот как это будет выглядеть:

Для тех, кого интересует только код, вот ссылка на соответствующий репозиторий.
Искусственный интеллект как React-разработчик
January 26, 2023 · 8 min read
JavaScript Developer
Представляю вашему вниманию перевод этой замечательной статьи, в которой автор просит ChatGPT разработать компонент модального окна на React.
ChatGPT — это чат-бот с искусственным интеллектом, разработанный компанией OpenAI и способный работать в диалоговом режиме, поддерживающий запросы на естественных языках. ChatGPT — большая языковая модель, для тренировки которой использовались методы обучения с учителем и обучения с подкреплением. Чат-бот основывается на другой языковой модели от OpenAI GPT-3.5 — улучшенной версии модели GPT-3.
ChatGPT был запущен 30 ноября 2022 года и привлек внимание своими широкими возможностями: написание кода, создание текстов, возможности перевода, получения точных ответов и использование контекста диалога для ответов, хотя его фактическая точность подверглась критике (источник — Википедия).
Работа с 3D-графикой в React
December 7, 2022 · 9 min read
JavaScript Developer
В данном туториале я покажу вам самый простой и быстрый, хотя и не очень оптимальный с точки зрения размера сборки, способ рендеринга 3D-объектов и моделей в React.
Мы решим 3 интересные задачи:
- рендеринг самописного 3D-объекта;
- рендеринг готовой 3D-модели;
- совместный рендеринг объекта и модели.
Знание вами основ работы с трехмерной графикой в браузере является опциональным.
Источником вдохновения для меня послужила эта замечательная статья.
Разработка приложения с помощью React Query
December 1, 2022 · 21 min read
JavaScript Developer
Представляю вашему вниманию перевод этой замечательной статьи, в которой рассказывается о разработке приложения с помощью React Query.
Прим. пер.: автор рассказывает лишь о ключевых особенностях приложения, поэтому я рекомендую клонировать репозиторий, установить зависимости и запустить сервер для разработки, чтобы иметь возможность выполнять необходимые операции при чтении статьи. Обратите внимание: если у вас возникнут проблемы при запуске сервера для разработки с помощью команды npm start , перенесите переменные, определенные в этой команде в файле package.json , в файл .env :
SKIP_PREFLIGHT_CHECK=true TSC_COMPILE_ON_ERROR=true ESLINT_NO_DEV_ERRORS=true
И отредактируйте команду start следующим образом:
"start": "react-scripts start"
Как изменить height объекта путем перетаскивания мыши?
В общем есть div, надо что бы при его перетаскивании, не менялись его координаты, а менялось значения стиля height, то есть что бы можно было мышкой растягивать элемент по вертикали. jQuery нельзя использовать. Если не сложно, дайте код для этого jsfiddle.net/Aliance/Z4NPu/4/.
- Вопрос задан более трёх лет назад
- 5019 просмотров
4 комментария
Оценить 4 комментария
мб. всетаки drag and drop?
Описание вопроса не соответствует его названию
Вопросы с меткой [drag-n-drop]
Это позволяет «схватить» объект с помощью мыши и перетащить его в другое место. Перетаскивание (Drag and Drop ) можно выполнять в приложении на основе браузера (веб) или в собственных приложениях с графическим интерфейсом, таких как Android или desktop.
231 вопрос
Конкурсные
Неотвеченные
- Конкурсные 0
- Неотвеченные
- Цитируемые
- Рейтинг
- Неотвеченные (мои метки)
в библиотеке dnd kit для drag&drop при нажатии на вложенный компонент не срабатывает событие клика
У меня два компонента, родительский и дочерний, брал пример из документации, в дочернем компоненте у меня реализована логика по событию onClick раскрывать список элементов внутри, а в родительском .
задан 26 дек 2023 в 14:26
48 показов
Как сделать рабочую область C# Winforms
Делаю программу по учебе и мне требуется сделать конструктор php файла. Пытаюсь сделать программу на C# Winforms и хочу реализовать drag-n-drop интерфейс, позволяющий перетаскивать модули на рабочую .
задан 18 дек 2023 в 13:23
Смещения зоны загрузки drag-and-drop
Зона загрузки drag-and-drop назначена на модальное окно. Когда заносишь файлы на зону, то зона сместилась в сторону. Почему?! Необходимо над модальным окном. function inputFile() < const .
задан 11 дек 2023 в 9:07
48 показов
Инициировать событие Drag при создании объекта
У меня есть кнопка, при зажатии на которой клавиши (Input.GeyKey()), создаёт в точке другой объект из префаба. Создаваемый объект использует EventSystem для DragAndDrop. Но при создании, событие Drag .
задан 1 дек 2023 в 9:03
111 показов
Как модифицировать почти работающий скрипт, реализующий альтернативный скроллбар на странице через drag-n-drop? javascript
Вкратце: хочу реализовать скроллбар альтернативный системному. Если запустить код в FF, то наглядно видно, что всё вроде как работает, но скроллинг путем перетаскивания слайдера (функция DragElement) .
задан 9 окт 2023 в 21:30
30 показов
Проблемы с Drag and Drop в unity c# при изменении разрешения
При стандартном разрешении во время перетаскивания объекта все идет хорошо. Но при изменении разрешения в ondrag объект начинает двигаться быстрее курсора, чем выше разрешение тем он быстрее. Вот .
задан 26 сен 2023 в 14:42
Как в batch файле обработать нескольких сброшенных на него файлов?
Один файл без проблем: ffmpeg -i «%~1» «%~n1.gif» Как обработать группу из нескольких сброшенных (drag and drop) файлов? Пробовал: for %%a in (%*) do ( ffmpeg «%%a~1» .