Операции перетаскивания
В данном разделе описываются шаги, выполняемые во время операции перетаскивания и перекладывания.
Операции перетаскивания, описанные в этом документе, используют интерфейс DataTransfer. В этом документе не используется интерфейс DataTransferItem, а также интерфейс DataTransferItemList.
Атрибут draggable
В веб-странице существуют случаи, когда используется поведение перетаскивания по умолчанию. К ним относятся выделение текста, изображений и ссылок. При перетаскивании изображения или ссылки URL изображения или ссылки устанавливается в качестве данных перетаскивания, и начинается операция перетаскивания. Для других элементов они должны быть частью выделения, чтобы произошло перетаскивание по умолчанию. Чтобы увидеть это в действии, выделите область веб-страницы, а затем нажмите и удерживайте кнопку мыши и перетащите выделение. Появится специфичное для ОС отображение выделения, которое будет следовать за указателем мыши во время перетаскивания. Однако это поведение является только поведением перетаскивания по умолчанию, если слушатели не изменяют данные, которые будут перетаскиваться.
В HTML, помимо поведения по умолчанию для изображений, ссылок и выделений, другие элементы по умолчанию не перетаскиваются.
Чтобы сделать другие HTML-элементы перетаскиваемыми, необходимо выполнить три действия:
- Установите атрибут
draggableв значение"true"для элемента, который вы хотите сделать перетаскиваемым. - Добавьте обработчик события
dragstart. - Установите данные перетаскивания в вышеупомянутом обработчике.
Вот пример, который позволяет перетаскивать часть контента.
<p draggable="true">This text <strong>may</strong> be dragged.</p>
const draggableElement = document.querySelector('p[draggable="true"]');
draggableElement.addEventListener("dragstart", (event) =>
event.dataTransfer.setData("text/plain", "This text may be dragged"),
);
Атрибут draggable установлен в значение "true", поэтому этот элемент становится перетаскиваемым. Если этот атрибут отсутствует или установлен в значение "false", элемент не будет перетаскиваться, а вместо этого будет выбран текст.
Атрибут draggable может быть использован для любого элемента, включая изображения и ссылки. Однако для этих двух последних элементов значение по умолчанию равно true, поэтому вы будете использовать атрибут draggable со значением false, чтобы отключить перетаскивание этих элементов.
Примечание: Когда элемент становится перетаскиваемым, текст или другие элементы внутри него больше нельзя выделять обычным способом, нажав и перетащив мышью. Вместо этого пользователь должен удерживать клавишу Alt, чтобы выделить текст мышью, или использовать клавиатуру.
Начало операции перетаскивания
В этом примере мы добавляем обработчик события dragstart с помощью метода addEventListener().
<p draggable="true">This text <strong>may</strong> be dragged.</p>
const draggableElement = document.querySelector('p[draggable="true"]');
draggableElement.addEventListener("dragstart", (event) =>
event.dataTransfer.setData("text/plain", "This text may be dragged"),
);
Когда пользователь начинает перетаскивание, срабатывает событие dragstart.
В этом примере обработчик события dragstart добавлен к самому перетаскиваемому элементу. Однако вы можете прослушивать более высокий предковый элемент, так как события перетаскивания, как и большинство других событий, распространяются вверх по дереву.
Внутри события dragstart вы можете указать данные перетаскивания, изображение обратной связи и эффекты перетаскивания, все из которых описаны ниже. Однако данные перетаскивания необходимы. (В большинстве ситуаций подходят значения по умолчанию для изображения и эффектов перетаскивания.)
Данные перетаскивания
Все объекты DragEvent имеют свойство dataTransfer, которое хранит данные перетаскивания (dataTransfer — объект DataTransfer).
При перетаскивании данные должны быть связаны с перетаскиванием, чтобы указать, что перетаскивается. Например, при перетаскивании выделенного текста в текстовом поле данные, связанные с элементом данных перетаскивания, — это сам текст. Аналогично, при перетаскивании ссылки на веб-странице элементом данных перетаскивания является URL-адрес ссылки.
Объект DataTransfer содержит две части информации: тип (или формат) данных и значение данных. Формат — это строка типа (например, text/plain для текстовых данных), а значение — строка текста. При начале перетаскивания вы добавляете данные, предоставляя тип и значение. Во время перетаскивания в обработчике события dragenter и dragover вы используете типы данных перетаскиваемых данных, чтобы проверить, разрешено ли перекладывание. Например, целевой элемент перекладывания, который принимает ссылки, проверяет тип text/uri-list. Во время события перекладывания обработчик извлекает перетаскиваемые данные и вставляет их в место перекладывания.
Свойство types объекта DataTransfer возвращает список строк типа MIME, таких как text/plain или image/jpeg. Вы также можете создавать свои собственные типы. Наиболее часто используемые типы перечислены в статье Рекомендуемые типы перетаскивания.
Перетаскивание может включать элементы данных нескольких типов. Это позволяет предоставлять данные в более специфических типах, часто пользовательских типах, но при этом обеспечивать резервные данные для целей перекладывания, которые не поддерживают более специфические типы. Обычно наименее специфическим типом являются обычные текстовые данные с типом text/plain.
Чтобы установить элемент данных перетаскивания в dataTransfer, используйте метод setData(). Он принимает два аргумента: тип данных и значение данных. Например:
event.dataTransfer.setData("text/plain", "Text to drag");
В этом случае значение данных — "Текст для перетаскивания", и формат — text/plain.
Вы можете предоставить данные в нескольких форматах. Для этого вызовите метод setData() несколько раз с разными форматами. Вы должны вызывать его с форматами в порядке от наиболее специфических к наименее специфическим.
const dt = event.dataTransfer;
dt.setData("application/x.bookmark", bookmarkString);
dt.setData("text/uri-list", "https://www.mozilla.org");
dt.setData("text/plain", "https://www.mozilla.org");
Здесь данные добавляются в трех различных типах. Первый тип, application/x.bookmark, — это пользовательский тип. Другие приложения не будут поддерживать этот тип, но вы можете использовать пользовательский тип для перетаскивания между областями одного сайта или приложения.
Предоставляя данные и в других типах, мы также можем поддерживать перетаскивание в другие приложения в менее специфических формах. Тип application/x.bookmark может предоставить данные с большей детализацией для использования внутри приложения, тогда как другие типы могут включать только одну ссылку или текстовое представление.
Обратите внимание, что как text/uri-list, так и text/plain содержат те же данные в этом примере. Это часто бывает верно, но не обязательно.
Если вы попытаетесь добавить данные дважды с одним и тем же форматом, новые данные заменят старые данные, но в том же положении в списке типов, что и старые данные.
Вы можете очистить данные с помощью метода clearData(), который принимает один аргумент: тип данных для удаления.
event.dataTransfer.clearData("text/uri-list");
Аргумент type для метода clearData() необязателен. Если type не указан, удаляются данные, связанные со всеми типами. Если перетаскивание не содержит элементов данных перетаскивания или все элементы были затем очищены, перетаскивание не произойдет.
Установка изображения обратной связи перетаскивания
При перетаскивании генерируется полупрозрачное изображение из целевого элемента перетаскивания (элемента, на котором срабатывает событие dragstart), которое следует за указателем мыши во время перетаскивания. Это изображение создается автоматически, поэтому вам не нужно создавать его самостоятельно. Однако вы можете использовать setDragImage(), чтобы указать пользовательское изображение обратной связи перетаскивания.
event.dataTransfer.setDragImage(image, xOffset, yOffset);
Необходимо три аргумента. Первый — ссылка на изображение. Эта ссылка, как правило, будет ссылаться на элемент <img>, но она также может ссылаться на элемент <canvas> или любой другой элемент. Изображение обратной связи будет генерироваться на основе того, как изображение выглядит на экране, хотя для изображений они будут нарисованы в их исходном размере. Вторым и третьим аргументами метода setDragImage() являются смещения, где должно появиться изображение относительно указателя мыши.
Также можно использовать изображения и холсты, которые не находятся в документе. Этот метод полезен при рисовании пользовательских изображений перетаскивания с помощью элемента холста, как показано в следующем примере:
function dragWithCustomImage(event) {
const canvas = document.createElement("canvas");
canvas.width = canvas.height = 50;
const ctx = canvas.getContext("2d");
ctx.lineWidth = 4;
ctx.moveTo(0, 0);
ctx.lineTo(50, 50);
ctx.moveTo(0, 50);
ctx.lineTo(50, 0);
ctx.stroke();
const dt = event.dataTransfer;
dt.setData("text/plain", "Data to Drag");
dt.setDragImage(canvas, 25, 25);
}
В этом примере мы делаем один холст изображением перетаскивания. Так как холст имеет размер 50×50 пикселей, мы используем смещения, равные половине этого размера (25), чтобы изображение отображалось по центру указателя мыши.
Эффекты перетаскивания
При перетаскивании могут выполняться несколько операций. Операция copy используется для указания того, что перетаскиваемые данные будут скопированы из текущего места в место назначения. Операция move используется для указания того, что перетаскиваемые данные будут перемещены, а операция link используется для указания создания какой-либо связи между исходным и целевым местоположениями.
Вы можете указать, какие из трёх операций разрешены для источника перетаскивания, задав свойство effectAllowed внутри обработчика события dragstart.
event.dataTransfer.effectAllowed = "copy";
В этом примере разрешено только копирование.
Вы можете комбинировать значения различными способами:
none-
разрешена никакая операция
copy-
только
copy move-
только
move link-
только
link copyMove-
только
copyилиmove copyLink-
только
copyилиlink linkMove-
только
linkилиmove all-
copy,move, илиlink uninitialized-
значение по умолчанию, когда эффект не задан, эквивалентно
all
Обратите внимание, что эти значения должны быть использованы точно так, как указано выше. Например, установка свойства effectAllowed на copyMove разрешает операции копирования или перемещения, но запрещает пользователю выполнять операцию создания ссылки. Если вы не изменяете свойство effectAllowed, то разрешается любая операция, так же как и со значением all. Поэтому вам не нужно изменять это свойство, если вы не хотите исключить определённые типы.
Во время операции перетаскивания обработчик событий dragenter или dragover может проверить свойство effectAllowed, чтобы узнать, какие операции разрешены. Свойство, связанное с этим свойством, dropEffect, должно быть установлено в одном из этих событий, чтобы указать, какая отдельная операция должна быть выполнена. Действительные значения для dropEffect это none, copy, move, или link . Комбинированные значения для этого свойства не используются.
При событии dragenter и dragover свойство dropEffect инициализируется эффектом, который пользователь запрашивает. Пользователь может изменить желаемый эффект, нажав клавиши-модификаторы. Хотя точные используемые клавиши зависят от платформы, обычно для переключения между копированием, перемещением и созданием ссылки используются клавиши Shift и Control. Указатель мыши изменится, чтобы указать желаемую операцию. Например, для copy, курсор может отображаться с плюсом рядом с ним.
Вы можете изменить свойство dropEffect во время событий dragenter или dragover, если, например, определённая целевая область приема падения поддерживает только определённые операции. Вы можете изменить свойство dropEffect, чтобы переопределить пользовательский эффект и принудительно выполнить конкретную операцию падения. Обратите внимание, что этот эффект должен быть указан в свойстве effectAllowed. В противном случае он будет установлен на альтернативное разрешенное значение.
event.dataTransfer.dropEffect = "copy";
В этом примере эффект, который выполняется, — копирование.
Вы можете использовать значение none для указания того, что падение не разрешено в данном месте, хотя в этом случае предпочтительно не отменять событие.
В событиях drop и dragend вы можете проверить свойство dropEffect, чтобы определить, какой эффект был в конечном счёте выбран. Если выбранный эффект был move, то исходные данные должны быть удалены из источника перетаскивания в событии dragend.
Определение целевых областей приема падения
Обработчик событий dragenter и dragover используется для указания допустимых целевых областей приема падения, то есть мест, куда можно поместить перетаскиваемые элементы. Большинство областей веб-страницы или приложения не являются допустимыми местами для помещения данных. Таким образом, обработка этих событий по умолчанию — не разрешать падение.
Если вы хотите разрешить падение, вы должны предотвратить стандартное поведение, отменив оба события dragenter и dragover . Вы можете сделать это, вызвав их методы preventDefault():
<div id="drop-target">You can drag and then drop a draggable item here</div>
const dropElement = document.getElementById("drop-target");
dropElement.addEventListener("dragenter", (event) => {
event.preventDefault();
});
dropElement.addEventListener("dragover", (event) => {
event.preventDefault();
});
Вызов метода preventDefault() во время событий dragenter и dragover укажет, что падение разрешено в данном месте. Однако, вы часто захотите вызвать метод preventDefault() только в определённых ситуациях (например, только если перетаскивается ссылка).
Для этого вызовите функцию, которая проверяет условие и отменяет событие только когда условие выполняется. Если условие не выполняется, не отменяйте событие, и падение не произойдёт, если пользователь отпустит кнопку мыши.
Чаще всего падение принимается или отклоняется на основе типа перетаскиваемых данных в передаче данных — например, разрешение изображений или ссылок, или обоих. Для этого вы можете проверить свойство types свойства dataTransfer события. Свойство types возвращает массив строковых типов, добавленных при начале перетаскивания, в порядке от наиболее значимого к наименее значимому.
function doDragOver(event) {
const isLink = event.dataTransfer.types.includes("text/uri-list");
if (isLink) {
event.preventDefault();
}
}
В этом примере мы используем метод includes для проверки, присутствует ли тип text/uri-list в списке типов. Если это так, мы отменяем событие, чтобы разрешить падение. Если перетаскиваемые данные не содержат ссылки, событие не отменяется, и падение не может произойти в этом месте.
Вы также можете установить свойства effectAllowed, dropEffect или оба одновременно, если вы хотите быть более точными относительно типа операции, которая будет выполнена. Естественно, изменение любого из свойств не окажет никакого влияния, если вы также не отменяете событие.
Обратная связь при падении
Существует несколько способов сообщить пользователю, что падение разрешено в определённом месте. Указатель мыши будет обновляться по мере необходимости в зависимости от значения свойства dropEffect.
Хотя точный вид зависит от платформы пользователя, обычно для copy появляется значок плюса, а значок «нельзя поместить сюда» появляется, когда падение не разрешено. Эта обратная связь с указателем мыши в большинстве случаев достаточна.
Для более сложных визуальных эффектов вы можете выполнить другие операции во время события dragenter. Например, вставив элемент в место, где произойдёт падение. Это может быть маркер вставки или элемент, представляющий перетаскиваемый элемент в его новом месте. Для этого вы можете создать элемент <img> и вставить его в документ во время события dragenter.
Событие dragover будет срабатывать на элементе, на который указывает курсор мыши. Естественно, вам может потребоваться переместить маркер вставки во время события dragover также. Вы можете использовать свойства события clientX и clientY, как и с другими событиями мыши, чтобы определить положение указателя мыши.
Наконец, событие dragleave будет срабатывать на элементе, когда перетаскивание покинет элемент. В это время вы должны удалить любые маркеры вставки или выделения. Вам не нужно отменять это событие. Событие dragleave всегда будет срабатывать, даже если перетаскивание отменено, поэтому вы можете всегда гарантировать, что очистка любой точки вставки может быть выполнена во время этого события.
Выполнение падения
Когда пользователь отпускает кнопку мыши, операция перетаскивания и помещения завершается.
Если мышь отпущена над элементом, который является допустимой целью помещения, то есть тем, который отменил последнее событие dragenter или dragover, то помещение будет успешным, и событие drop будет вызвано в целевом элементе. В противном случае операция перетаскивания отменяется, и событие drop не будет вызвано.
Во время события drop необходимо получить данные, которые были перетащены, и вставить их в место помещения. Можно использовать свойство dropEffect для определения желаемой операции перетаскивания.
Как и при всех событиях, связанных с перетаскиванием, свойство события dataTransfer будет содержать данные, которые перетаскиваются. Метод getData() может быть использован для повторного получения данных.
function onDrop(event) {
const data = event.dataTransfer.getData("text/plain");
event.target.textContent = data;
event.preventDefault();
}
Метод getData() принимает один аргумент — тип данных для извлечения. Он вернёт строковое значение, которое было установлено при вызове setData() в начале операции перетаскивания. Пустая строка будет возвращена, если данные такого типа не существуют. (Естественно, вы, вероятно, будете знать, что нужный тип данных доступен, так как это было проверено ранее во время события dragover.)
В данном примере, после получения данных, мы вставляем строку в качестве текстового содержимого целевого элемента. Это имеет эффект вставки перетащенного текста в то место, куда он был помещён, при условии, что целевой элемент является областью текста, например, элемент p или div.
На веб-странице следует вызвать метод preventDefault() события, если вы приняли помещение, чтобы предотвратить срабатывание браузерной обработки по умолчанию для перетащенных данных. Например, при перетаскивании ссылки на веб-страницу Firefox откроет ссылку. Отмена события предотвратит это поведение.
Также можно получить и другие типы данных. Если данные являются ссылкой, у них должен быть тип text/uri-list. Тогда можно вставить ссылку в содержимое.
function doDrop(event) {
const lines = event.dataTransfer.getData("text/uri-list").split("\n");
lines
.filter((line) => !line.startsWith("#"))
.forEach((line) => {
const link = document.createElement("a");
link.href = line;
link.textContent = line;
event.target.appendChild(link);
});
event.preventDefault();
}
Этот пример вставляет ссылку из перетаскиваемых данных. Как следует из названия, тип text/uri-list фактически может содержать список URL-адресов, каждый на отдельной строке. Приведённый код использует split для разделения строки на строки, затем итерируется по списку строк и вставляет каждую строку в документ в виде ссылки. (Обратите внимание, что ссылки, начинающиеся с символа решётки (#), пропускаются, так как это комментарии.)
В простых случаях можно использовать специальный тип URL для получения только первого действительного URL-адреса в списке. Например:
const link = event.dataTransfer.getData("URL");
Это избавляет от необходимости проверки комментариев или итерации по строкам вручную. Однако это ограничено только первым URL-адресом в списке.
Тип URL является специальным типом. Он используется только как сокращение и не отображается в списке типов, указанных в свойстве types.
Иногда вам может потребоваться поддержка нескольких форматов, и вы хотите получить данные, которые наиболее соответствуют поддерживаемым форматам. В следующем примере три формата поддерживаются целевым элементом для помещения.
Следующий пример возвращает данные, связанные с лучшим поддерживаемым форматом:
function doDrop(event) {
const supportedTypes = [
"application/x-moz-file",
"text/uri-list",
"text/plain",
];
const types = event.dataTransfer.types.filter((type) =>
supportedTypes.includes(type),
);
if (types.length) {
const data = event.dataTransfer.getData(types[0]);
// Use this type of data…
}
event.preventDefault();
}
Завершение перетаскивания
После завершения перетаскивания событие dragend вызывается в источнике перетаскивания (тот же элемент, который получил событие dragstart). Это событие будет вызвано, если перетаскивание прошло успешно или было отменено. Однако вы можете использовать свойство dropEffect для определения того, какая операция помещения произошла.
Если свойство dropEffect имеет значение none во время события dragend, то перетаскивание было отменено. В противном случае эффект указывает, какая операция была выполнена. Источник может использовать эту информацию после операции move для удаления перетаскиваемого элемента из старого расположения.
Помещение может произойти внутри того же окна или над другим приложением. Событие dragend всегда будет вызвано. Свойства события screenX и screenY будут установлены в экранные координаты, где произошло помещение.
После того, как событие dragend завершит свою обработку, операция перетаскивания и помещения считается завершённой.
См. также
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations