Drag event

Types of drag events

Drag refers to the user holding down the mouse button on an object, dragging it to another location, and then releasing the mouse button to drop the object there.

There are several types of dragged objects, including element nodes, pictures, links, selected text, and so on. In a web page, except for element nodes that cannot be dragged by default, other (images, links, selected text) can be dragged directly. In order to make the element node draggable, the draggable property of the node can be set to true.

<div draggable="true">This area can be dragged</div>

The div block of the above code can be directly dragged with the mouse on the web page. When you release the mouse button, the drag effect will disappear and the block remains at its original position.

The draggable attribute can be used for any element node, but pictures (<img>) and links (<a>) can be dragged without this attribute. For them, when using this attribute, it is often set to false to prevent dragging of these two elements.

Note that once the draggable attribute of an element node is set to true, you can no longer use the mouse to select the text or child nodes inside the node.

When the element node or selected text is dragged, it will continue to trigger drag events, including the following events.

-drag: During the drag process, continue to trigger on the dragged node (a few hundred milliseconds apart). -dragstart: When the user starts dragging, it will be triggered on the dragged node. The target attribute of this event is the dragged node. Usually, you should specify the dragged data in the listener function of this event. -dragend: When the drag is finished (release the mouse button or press the ESC key), it is triggered on the node being dragged. The target attribute of this event is the node being dragged. It is triggered on the same node as the dragstart event. The dragend event will always be triggered regardless of whether the drag crosses the window or is cancelled in the middle. -dragenter: When dragging into the current node, it will be triggered once on the current node. The target attribute of this event is the current node. Generally, you should specify whether to allow the dragged data to be dropped on the current node in the listener function of this event. If the current node does not have a listener function for the event, or the listener function does not perform any operation, it means that data is not allowed to be placed on the current node. The visual display of drag and drop into the current node is also set in the listener function of this event. -dragover: When dragged above the current node, it will continue to trigger on the current node (a few hundred milliseconds apart). The target attribute of this event is the current node. The difference between this event and the dragenter event is that the dragenter event is triggered when it enters the node, and then as long as it does not leave the node, the dragover event will continue to be triggered. -dragleave: When the drag operation leaves the current node range, it will be triggered on the current node. The target attribute of this event is the current node. If you want to visually display the current node of the drag-and-drop operation, set it in the listener function of this event. -drop: When the dragged node or selected text is released to the target node, it will be triggered on the target node. Note that if the current node does not allow drop, the event will not be triggered even if the mouse button is released above the node. If the user presses the ESC key to cancel this operation, the event will not be triggered. The monitoring function of this event is responsible for fetching the drag data and performing related processing.

The following example shows how to dynamically change the background color of the dragged node.

div.addEventListener(
  "dragstart",
  function (e) {
    this.style.backgroundColor = "red";
  },
  false
);

div.addEventListener(
  "dragend",
  function (e) {
    this.style.backgroundColor = "green";
  },
  false
);

In the above code, when the div node is dragged, the background color will change to red, and when the dragging ends, it will turn back to green.

The following is an example to show how to drag a node from the current parent node to another parent node.

/* HTML code is as follows
 <div class="dropzone">
   <div id="draggable" draggable="true">
     The node can be dragged
   </div>
 </div>
 <div class="dropzone"></div>
 <div class="dropzone"></div>
 <div class="dropzone"></div>
*/

// dragged node
var dragged;

document.addEventListener(
  "dragstart",
  function (event) {
    // Save the dragged node
    dragged = event.target;
    // The background color of the dragged node becomes transparent
    event.target.style.opacity = 0.5;
  },
  false
);

document.addEventListener(
  "dragend",
  function (event) {
    // The background color of the dragged node returns to normal
    event.target.style.opacity = "";
  },
  false
);

document.addEventListener(
  "dragover",
  function (event) {
    // Prevent the dragging effect from being reset, allowing the dragged node to be placed into the target node
    event.preventDefault();
  },
  false
);

document.addEventListener(
  "dragenter",
  function (event) {
    // The background color of the target node changes to purple
    // Since the event will bubble, the nodes need to be filtered
    if (event.target.className === "dropzone") {
      event.target.style.background = "purple";
    }
  },
  false
);

document.addEventListener(
  "dragleave",
  function (event) {
    // The background color of the target node is restored
    if (event.target.className === "dropzone") {
      event.target.style.background = "";
    }
  },
  false
);

document.addEventListener(
  "drop",
  function (event) {
    // Prevent the default behavior of the event (for example, links can be opened on some element nodes),
    event.preventDefault();
    if (event.target.className === "dropzone") {
      // Restore the background color of the target node
      event.target.style.background = "";
      // Insert the dragged node into the target node
      dragged.parentNode.removeChild(dragged);
      event.target.appendChild(dragged);
    }
  },
  false
);

Regarding the drag incident, there are the following points to note.

-The drag process only triggers the above drag events. Although the mouse is moving, the mouse events will not be triggered. -Dragging and dropping files from the operating system into the browser will not trigger the dragstart and dragend events. -Monitoring functions for dragenter and dragover events, used to retrieve the dragged data (that is, allow the dragged element to be dropped). Since most areas of the web page are not suitable as the target node for dropping dragged elements, the default setting of these two events is that the current node does not allow the dragged element to be accepted. If you want to drop data on the target node, you must first prevent the default behavior of these two events.

<div ondragover="return false">
  <div ondragover="event.preventDefault()"></div>
</div>

In the above code, if you do not cancel the drag event or prevent the default behavior, you cannot drop the dragged node on the div node.

DragEvent interface

Drag events inherit the DragEvent interface, which in turn inherits the MouseEvent interface and the Event interface.

The browser natively provides a DragEvent() constructor to generate instance objects of drag events.

new DragEvent(type, options);

The constructor of DragEvent() accepts two parameters. The first parameter is a string, which indicates the type of the event. This parameter is required; the second parameter is the configuration object of the event, which is used to set the properties of the event. . In addition to accepting the configuration properties of the MouseEvent interface and the Event interface, the configuration object can also set the dataTransfer property to be either null or an instance of the DataTransfer interface.

The instance object of DataTransfer is used to read and write the data transferred in the drag event. For details, please refer to the section "DataTransfer Interface" below.

DataTransfer interface overview

All drag event instances have a DragEvent.dataTransfer property, which is used to read and write the data that needs to be transferred. The value of this property is an instance of the DataTransfer interface.

The browser natively provides a DataTransfer() constructor to generate DataTransfer instance objects.

var dataTrans = new DataTransfer();

The DataTransfer() constructor does not accept parameters.

The dragged data is divided into two aspects: the type of data (also known as the format) and the value of the data. The type of data is a MIME string (such as text/plain, image/jpeg), and the value of the data is a string. Generally speaking, if you drag a piece of text, the data defaults to that text; if you drag a link, the data defaults to the URL of the link.

When the drag event starts, the developer can provide the data type and data value. During the dragging process, the developer checks the data type through the listener functions of the dragenter and dragover events to determine whether the dragged object is allowed to be dropped. For example, in an area where only links are allowed to drop, check whether the drag data type is text/uri-list.

When the drop event occurs, the listener function takes out the dragged data and processes it.

Instance properties of DataTransfer

DataTransfer.dropEffect

The DataTransfer.dropEffect property is used to set the effect of dropping the dragged node, which will affect the shape of the mouse when dragging through the relevant area. It may take the following values.

-copy: copy the dragged node -move: move the dragged node -link: create a link to the node being dragged -none: The dragged node cannot be dropped

In addition to the above values, setting other values ​​is invalid.

target.addEventListener("dragover", function (e) {
  e.preventDefault();
  e.stopPropagation();
  e.dataTransfer.dropEffect = "copy";
});

In the above code, once the dragged element is drop, the accepted area will copy the node.

The dropEffect attribute is generally set in the listener function of the dragenter and dragover events. For the three events of dragstart, drag, and dragleave, this attribute has no effect. Because this attribute is only valid for the area that accepts the dragged node, it is invalid for the dragged node itself. After entering the target area, the drag behavior will be initialized to the set effect.

DataTransfer.effectAllowed

The DataTransfer.effectAllowed property sets the effect allowed in this drag. It may take the following values.

-copy: copy the dragged node -move: move the dragged node -link: create a link to the node being dragged -copyLink: Allow copy or link -copyMove: Allow copy or move -linkMove: Allow link or move -all: Allow all effects -none: The dragged node cannot be dropped -uninitialized: the default value, equivalent to all

If a certain effect is not allowed, the user cannot achieve this effect in the target node.

This property and the dropEffect property are two aspects of the same thing. The former sets the effect allowed by the dragged node, and the latter sets the effect of the dragged area. They are often used in conjunction.

The listener function of the dragstart event can be used to set this property. Setting this property in the listener function of other events is invalid.

source.addEventListener("dragstart", function (e) {
  e.dataTransfer.effectAllowed = "move";
});

target.addEventListener("dragover", function (e) {
  e.dataTransfer.dropEffect = "move";
});

As long as one of the dropEffect property and the effectAllowed property is none, the drop operation cannot be completed on the target node.

DataTransfer.files

The DataTransfer.files property is a FileList object that contains a set of local files, which can be used to transfer in a drag-and-drop operation. If no files are involved in this drag, this attribute is an empty FileList object.

The following is an example of receiving dragged files.

// HTML code is as follows
// <div id="output" style="min-height: 200px;border: 1px solid black;">
// Drag and drop the file here
// </div>

var div = document.getElementById("output");

div.addEventListener(
  "dragenter",
  function (event) {
    div.textContent = "";
    event.stopPropagation();
    event.preventDefault();
  },
  false
);

div.addEventListener(
  "dragover",
  function (event) {
    event.stopPropagation();
    event.preventDefault();
  },
  false
);

div.addEventListener(
  "drop",
  function (event) {
    event.stopPropagation();
    event.preventDefault();
    var files = event.dataTransfer.files;
    for (var i = 0; i < files.length; i++) {
      div.textContent += files[i].name + "" + files[i].size + "byte\n";
    }
  },
  false
);

In the above code, the information of the dragged file is read through the dataTransfer.files property. If you want to read the contents of a file, you must use the FileReader object.

div.addEventListener("drop", function (e) {
  e.preventDefault();
  e.stopPropagation();

  var fileList = e.dataTransfer.files;
  if (fileList.length > 0) {
    var file = fileList[0];
    var reader = new FileReader();
    reader.onloadend = function (e) {
      if (e.target.readyState === FileReader.DONE) {
        var content = reader.result;
        div.innerHTML = "File: " + file.name + "\n\n" + content;
      }
    };
    reader.readAsBinaryString(file);
  }
});

DataTransfer.types

The DataTransfer.types property is a read-only array, each member is a string, inside is the drag data format (usually MIME value). For example, if the drag is text, the corresponding member is text/plain.

The following is an example. By checking the type of the dataTransfer attribute, it is determined whether to allow the execution of the drop operation on the current node.

function contains(list, value) {
  for (var i = 0; i < list.length; ++i) {
    if (list[i] === value) return true;
  }
  return false;
}

function doDragOver(event) {
  var isLink = contains(event.dataTransfer.types, "text/uri-list");
  if (isLink) event.preventDefault();
}

In the above code, only when one of the dragged nodes is a link, can it be dropped on the current node.

DataTransfer.items

The DataTransfer.items property returns an array-like read-only object (DataTransferItemList instance), and each member is an object (DataTransferItem instance) dragged this time. If this drag does not contain an object, an empty object is returned.

The DataTransferItemList instance has the following properties and methods.

-length: returns the number of members -add(data, type): add a string with specified content and type (such as text/html and text/plain) as a member -add(file): Another usage of add method, adding a file as a member -remove(index): remove the member at the specified position -clear(): remove all members

The DataTransferItem instance has the following properties and methods.

-kind: Returns the kind of member (string or file). -type: Returns the type of the member (usually a MIME value). -getAsFile(): If the dragged file is a file, return the file, otherwise return null. -getAsString(callback): If the dragged string is a string, pass the character to the specified callback function for processing. This method is asynchronous, so you need to pass in a callback function.

Below is an example.

div.addEventListener("drop", function (e) {
  e.preventDefault();
  if (e.dataTransfer.items != null) {
    for (var i = 0; i < e.dataTransfer.items.length; i++) {
      console.log(
        e.dataTransfer.items[i].kind + ": " + e.dataTransfer.items[i].type
      );
    }
  }
});

Instance method of DataTransfer

DataTransfer.setData()

The DataTransfer.setData() method is used to set the data carried by the drag event. This method has no return value.

event.dataTransfer.setData("text/plain", "Text to drag");

The above code adds plain text data to the current drag event.

This method accepts two parameters, both of which are strings. The first parameter indicates the data type (such as text/plain), and the second parameter is the specific data. If the data of the specified type does not exist in the dataTransfer attribute, then these data will be added, otherwise the original data will be replaced by the new data.

If you drag the text box or drag the selected text, the corresponding text data will be added to the dataTransfer attribute by default, and there is no need to specify it manually.

<div draggable="true">aaa</div>

In the above code, dragging this <div> element will automatically bring the text data aaa.

Use the setData method to replace the original data.

<div
  draggable="true"
  ondragstart="event.dataTransfer.setData('text/plain','bbb')"
>
  aaa
</div>

In the above code, the drag data is actually bbb, not aaa.

The following is to add other types of data. Since text/plain is the most commonly supported format, in order to ensure compatibility, it is recommended to always save a copy of the data in plain text format at the end.

var dt = event.dataTransfer;

// Add a link
dt.setData("text/uri-list", "http://www.example.com");
dt.setData("text/plain", "http://www.example.com");

// Add HTML code
dt.setData("text/html", "Hello there, <strong>stranger</strong>");
dt.setData("text/plain", "Hello there, <strong>stranger</strong>");

// Add the URL of the image
dt.setData("text/uri-list", imageurl);
dt.setData("text/plain", imageurl);

Data in multiple formats can be provided at once.

var dt = event.dataTransfer;
dt.setData("application/x-bookmark", bookmarkString);
dt.setData("text/uri-list", "http://www.example.com");
dt.setData("text/plain", "http://www.example.com");

In the above code, by storing three types of data on the same event, the drag event can be on different objects with different values ​​of drop. Note that the first format is a custom format and cannot be read by browsers by default. This means that only a node with a specific code deployed can drop this data.

DataTransfer.getData()

The DataTransfer.getData() method accepts a string (representing the data type) as a parameter, and returns the specified type of data carried by the event (usually the data added by the setData method). If the data of the specified type does not exist, an empty string is returned. Usually only after the drop event is triggered, data can be retrieved.

The following is a listener function for the drop event to retrieve data of a specified type.

function onDrop(event) {
  var data = event.dataTransfer.getData("text/plain");
  event.target.textContent = data;
  event.preventDefault();
}

The above code takes out the text data of the drag event and replaces it with the text content of the current node. Note that you must also cancel the default behavior of the browser at this time, because if the user drags a link, the browser will open the link in the current window by default.

The getData method returns a string. If it contains multiple data, it must be parsed manually.

function doDrop(event) {
  var lines = event.dataTransfer.getData("text/uri-list").split("\n");
  for (let line of lines) {
    let link = document.createElement("a");
    link.href = line;
    link.textContent = line;
    event.target.appendChild(link);
  }
  event.preventDefault();
}

In the above code, the getData method returns a set of links, which must be resolved by itself.

The type value is specified as URL, and the first valid link can be retrieved.

var link = event.dataTransfer.getData("URL");

The following example is to fetch data from multiple types of data.

function doDrop(event) {
  var types = event.dataTransfer.types;
  var supportedTypes = ["text/uri-list", "text/plain"];
  types = supportedTypes.filter(function (value) {
    types.includes(value);
  });
  if (types.length) {
    var data = event.dataTransfer.getData(types[0]);
  }
  event.preventDefault();
}

DataTransfer.clearData()

The DataTransfer.clearData() method accepts a string (representing the data type) as a parameter, and deletes the specified type of data carried by the event. If no type is specified, all data will be deleted. If the specified type does not exist, calling this method will have no effect.

event.dataTransfer.clearData("text/uri-list");

The above code clears the data of type text/uri-list carried by the event.

This method will not remove the dragged files, so after calling this method, the DataTransfer.types property may still return the Files type (provided that there is a file drag).

Note that this method can only be used in the listener function of the dragstart event, because this is the only time when the data of the drag operation can be written.

DataTransfer.setDragImage()

During the drag process (after the dragstart event is triggered), the browser will display a picture that moves with the mouse, indicating the node being dragged. This picture is created automatically, usually displayed as the appearance of the dragged node, no need to set it yourself.

The DataTransfer.setDragImage() method can customize this image. It accepts three parameters. The first is <img> node or <canvas> node, if omitted or null, the appearance of the dragged node is used; the second and third parameters are the mouse relative to the image The abscissa and ordinate of the upper left corner.

Below is an example.

/* HTML code is as follows
 <div id="drag-with-image" class="dragdemo" draggable="true">
   drag me
 </div>
*/

var div = document.getElementById("drag-with-image");
div.addEventListener(
  "dragstart",
  function (e) {
    var img = document.createElement("img");
    img.src = "http://path/to/img";
    e.dataTransfer.setDragImage(img, 0, 0);
  },
  false
);