Implementing the Drag Functionality
ArkWeb provides the functionality of dragging elements on web pages. Users can place an element by holding down the element and dragging it to another element. This functionality meets the HTML5 standard.
Dragging Web Page Content to Other Applications
The following table lists the data formats supported by ArkWeb. You set data in these formats based on the HTML5 standard to drag content to other applications.
| Data Format | Description |
|---|---|
| text/plain | Text |
| text/uri-list | Link |
| text/html | HTML |
| Files | File |
Listening for Drag Events
The drag functionality of ArkWeb is different from that of ArkUI. ArkWeb is mainly used to drag web page content. Therefore, only some drag events can be listened for.
| Method | Description |
|---|---|
| onDragStart | This method is not recommended. Calling it will affect the dragging behavior of Web components, resulting in unexpected dragging logic. For example, HTML dragging event listening cannot be triggered, preview images cannot be created, preview images are incorrect, or dragging data cannot be preset. |
| onDragEnter | Called when an element is dragged to the web area. |
| onDragMove | Called when an element is moved in the web area. |
| onDragLeave | Called when the dragged element leaves the web area. |
| onDragEnd | Called when the dragging of an element ends. |
Implementing the Drag Logic on ArkTS
In most cases, the drag functionality implemented in HTML5 can meet the requirements of an application. If necessary, refer to the following examples to read drag data on ArkTS.
- Establishing a data channel between the application and the frontend Page.
- In the onDrop method, implement simple logic, for example, temporarily storing some key data.
- To implement time-consuming tasks, add the application processing logic to the message receiving method on ArkTS.
The onDrop method on ArkTS is executed earlier than the event processing method (droppable.addEventListener('drop') in the HTML example) in HTML5. If page redirection is performed in the onDrop method, the drop method in HTML5 cannot be executed correctly, and the unexpected result is generated. Therefore, a bidirectional communication mechanism must be established to notify ArkTS to execute the corresponding service logic after the drop method in HTML5 is executed.
import { webview } from '@kit.ArkWeb'
import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData';
@Entry
@Component
struct DragDrop {
private controller: webview.WebviewController = new webview.WebviewController()
@State ports: Array<webview.WebMessagePort> = []
@State dragData: Array<unifiedDataChannel.UnifiedRecord> = []
build() {
Column() {
Web({
src: $rawfile('drag.html'),
controller: this.controller,
}).onPageEnd((event) => {
//Register the message port.
this.ports = this.controller.createWebMessagePorts();
this.ports[1].onMessageEvent((result: webview.WebMessage) => {
//Process the data received from HTML. You can record logs to confirm the message. The message format can be customized as long as it can be uniquely identified.
console.info('ETS receive Message: typeof (result) = ' + typeof (result) + ';' + result);
// Process the message after the message is received in result. You can perform time-consuming tasks.
});
console.info('ETS postMessage set h5port ');
//After the message port is registered, the front end sends a registration completion message to complete bidirectional port binding.
this.controller.postMessage('__init_port__', [this.ports[0]], '*');
})// Implement simple logic in onDrop, for example, temporarily storing some key data.
.onDrop((dragEvent: DragEvent) => {
console.info('ETS onDrop!')
let data: UnifiedData = dragEvent.getData();
if(!data) {
return false;
}
let uriArr: unifiedDataChannel.UnifiedRecord[] = data.getRecords();
if (!uriArr || uriArr.length <= 0) {
return false;
}
// Traverse records to obtain data for temporary storage or use other methods to temporarily store data.
for (let i = 0; i < uriArr.length; ++i) {
if (uriArr[i].getType() === uniformTypeDescriptor.UniformDataType.PLAIN_TEXT) {
let plainText = uriArr[i] as unifiedDataChannel.PlainText;
if (plainText.textContent) {
console.info('plainText.textContent: ', plainText.textContent);
}
}
}
return true
})
}
}
}
HTML example:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>HTML5 Dragging Demo</title>
</head>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.draggable {
width: 100px;
height: 100px;
background-color: #4CAF50;
color: white;
text-align: center;
line-height: 100px;
margin-bottom: 20px;
cursor: grab;
}
.droppable {
width: 300px;
height: 150px;
border: 2px dashed #999;
background-color: #f0f0f0;
text-align: center;
line-height: 150px;
font-size: 16px;
}
.success {
background-color: #4CAF50;
color: white;
}
</style>
<body>
<h2>HTML5 Dragging Demo</h2>
<div id="draggable" class="draggable" draggable="true">Draggable element</div>
<div id="droppable" class="droppable">Please drag the element here</div>
<script>
const draggable = document.getElementById('draggable');
const droppable = document.getElementById('droppable');
// Listen for the drag start event.
draggable.addEventListener('dragstart', function (e) {
e.dataTransfer.setData('text/plain', this.id);
this.style.opacity = '0.4';
});
// Listen for the drag end event.
draggable.addEventListener('dragend', function (e) {
this.style.opacity = '1';
});
// Listen for the event triggered when the element is dragged to the target area.
droppable.addEventListener('dragover', function (e) {
e.preventDefault(); // This method must be invoked. Otherwise, the drop event cannot be triggered.
});
// Listen for the drop event.
droppable.addEventListener('drop', function (e) {
e.preventDefault();
const data = e.dataTransfer.getData('text/plain');
// Transfer the element to ArkTS.
PostMsgToArkTS(data);
const draggableEl = document.getElementById(data);
this.appendChild(draggableEl);
this.classList.add('success');
this.textContent = "Dropped successfully!";
});
// Set the scriptproxy port on JavaScript.
var h5Port;
window.addEventListener('message', function (event) {
console.info("H5 receive settingPort message");
if (event.data == '__init_port__') {
if (event.ports[0] != null) {
console.info("H5 set h5Port " + event.ports[0]);
h5Port = event.ports[0];
}
}
});
// Send data to ArkTS using scriptproxy.
function PostMsgToArkTS(data) {
console.info("H5 PostMsgToArkTS, h5Port " + h5Port);
if (h5Port) {
h5Port.postMessage(data);
} else {
console.error("h5Port is null, Please initialize first");
}
}
</script>
</body>
</html>

Log output:

FAQs
Why is the drag event set in HTML5 not triggered?
Check whether the CSS resources are properly set. Some web pages set the CSS style only for devices with specific UAs. You can set a custom UA in the Web component to solve this problem. For example:
import { webview } from '@kit.ArkWeb'
@Entry
@Component
struct Index {
private webController: webview.WebviewController = new webview.WebviewController()
build(){
Column() {
Web({
src: 'example.com',
controller: this.webController,
}).onControllerAttached(() => {
// Set a custom UA.
let customUA = 'android'
this.webController.setCustomUserAgent(this.webController.getUserAgent() + customUA)
})
}
}
}
How do I disable the Web component drag functionality?
By default, the Web component supports the drag functionality. If the drag functionality is not required, you can disable it by referring to the following example.
The drag functionality can be disabled in any of the following ways:
- On the web page side, use W3C, CSS, and JS for interception or disabling.
- On the application side, use the runJavascriptExt API of the Web component to inject JS for interception or disabling.
HTML example 1:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Using the W3C Common Attributes or Methods</title>
</head>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.normal {
width: 100px;
height: 100px;
margin-bottom: 40px;
}
.undraggable {
width: 100px;
height: 100px;
margin-bottom: 40px;
-webkit-user-drag: none;
}
</style>
<body>
<h2>Use the W3C common attributes or methods to disable the drag functionality.</h2>
<!--1. Explicitly set the draggable attribute to false to disable the drag functionality of the element.-->
<!--This setting takes effect only for the entire element node, such as img or div, and does not take effect for the selected text in the node.-->
<div>Set the draggable attribute to disable the drag functionality.</div>
<img class="normal" draggable="false" src="./any-pic.png"><br>
<!--2. Reference a style class and set -webkit-user-drag to none to disable the drag functionality.-->
<!--The effective scope is the same as that of method 1.-->
<div>Set -webkit-user-drag to disable the drag functionality.</div>
<img class="undraggable" src="./any-pic.png"><br>
<!--3. Listen for the ondragstart event and set preventDefault to disable the drag functionality.-->
<!--This setting takes effect for all content.-->
<!--You can expand the listening range of the listener to disable drag in a larger area. For example, implementing listening on the window can disable drag of the entire Web component.-->
<!--Because the setting takes effect subsequently, the drag operation is partially executed, impacting the menu function.-->
<div>Set ondragstart to disable the drag functionality.</div>
<div ondragstart="dragstartHandler(event)">
<img class="normal" src="./any-pic.png">
<p>
This text is used to verify the drag disabling effect of the ondragstart script on the selected text.
</p>
</div>
<script>
function dragstartHandler(event) {
console.info('forbid drag when drag start');
event.preventDefault();
}
</script>
</body>
</html>

HTML example 2:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Using runJavascriptExt to Inject JS</title>
</head>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.normal {
width: 100px;
height: 100px;
margin-bottom: 40px;
}
</style>
<body>
<h2>Use runJavascriptExt to inject JS for disabling the drag functionality.</h2>
<div>
<img class="normal" src="./any-pic.png">
<p>
This text is used to verify the drag disabling effect of the JS injected by runJavascriptExt on the selected text.
</p>
</div>
</body>
</html>

ETS example:
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct Index {
webViewController: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
Button('w3cDemoPage')
.onClick(() => {
this.webViewController.loadUrl($rawfile('w3c-forbid.html'));
})
Button('runJsDemoPage')
.onClick(() => {
this.webViewController.loadUrl($rawfile('runJs-forbid.html'));
})
Button('runJsForbidDrag')
.onClick(() => {
try {
// Use runJavaScriptExt to execute the script, which adds a dragstart event listener to disable the drag functionality.
this.webViewController.runJavaScriptExt(
'window.addEventListener(\'dragstart\', (ev) => {\n' +
'ev.preventDefault();\n' +
'});',
(error, result) => {
if (error) {
console.error(`run JavaScript error, ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`)
return;
}
});
} catch (resError) {
console.error(`ErrorCode: ${(resError as BusinessError).code}, Message: ${(resError as BusinessError).message}`);
}
})
Web({
src: $rawfile('w3c-forbid.html'),
controller: this.webViewController
})
.domStorageAccess(true)
.javaScriptAccess(true)
.fileAccess(true)
}
.height('100%')
.width('100%')
}
}