鸿蒙6.0应用开发——使用Web组件的拖拽功能与网页交互
ArkWeb的拖拽功能使应用能够在网页中实现元素的拖放,用户可以长按可拖拽的元素,将其拖至可放置的元素上,然后松手完成放置。ArkWeb在网页内容中的拖拽功能满足H5标准。
·
鸿蒙6.0应用开发——使用Web组件的拖拽功能与网页交互
ArkWeb的拖拽功能使应用能够在网页中实现元素的拖放,用户可以长按可拖拽的元素,将其拖至可放置的元素上,然后松手完成放置。ArkWeb在网页内容中的拖拽功能满足H5标准。
将网页内容拖拽至其他应用
ArkWeb目前支持以下四种数据格式。应用按照 H5 标准设置这些格式的拖拽数据,即可将内容拖拽到其他应用中。
| 数据格式 | 说明 |
|---|---|
| text/plain | 文本 |
| text/uri-list | 链接 |
| text/html | HTML格式 |
| Files | 文件 |
拖拽事件通知
ArkWeb拖拽不同于ArkUI的组件级拖拽,主要针对网页内容的拖拽,因此仅支持部分拖拽事件的监听方法。
| 监听方法 | 说明 |
|---|---|
| onDragStart | 不建议使用此方法,否则会影响Web组件的拖拽行为,造成拖拽逻辑不符合预期,如无法触发H5拖拽事件监听,预览图无法创建或预览图错误,拖拽数据无法预置等。 |
| onDragEnter | 拖拽的元素进入Web区域。 |
| onDragMove | 拖拽的元素在Web区域移动。 |
| onDragLeave | 拖拽的元素离开Web区域。 |
| onDrop | 拖拽的元素落入Web区域。 |
| onDragEnd | 由Web发起的拖拽元素结束拖拽。 |
在ArkTS侧实现拖拽相关逻辑
在多数情况下,应用在H5端实现的拖拽功能能够满足需求。如有需要,请参考以下案例,实现在ArkTS端进行拖拽数据读取等操作。
- 建立应用侧与前端页面数据通道。
- 在onDrop方法中,做简单逻辑,例如暂存一些关键数据。
- 在ArkTS侧接收消息的方法中,添加应用处理逻辑,可以进行耗时任务。
由于ArkTS侧的onDrop方法会早于H5中放置事件的处理方法(H5示例中的droppable.addEventListener(‘drop’))执行,若在onDrop方法中进行页面跳转等操作,将导致H5中的drop方法无法正确执行,产生不符合预期的结果。因此,应建立双向通信机制,在H5中的drop方法执行完毕后,通知ArkTS侧执行相应的业务逻辑,以确保业务逻辑的预期执行。
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) => {
// 注册通信端口
this.ports = this.controller.createWebMessagePorts();
this.ports[1].onMessageEvent((result: webview.WebMessage) => {
// ArkTS收到html传来的数据后的处理,可以先打日志确认下消息,双端的消息格式可以自己约定,能唯一识别就行
console.info('ETS receive Message: typeof (result) = ' + typeof (result) + ';' + result);
// 这里添加result中消息接收到后的处理,可进行耗时任务
});
console.info('ETS postMessage set h5port ');
// 完成通信端口注册后,向前端发送注册完成消息,完成双向的端口绑定
this.controller.postMessage('__init_port__', [this.ports[0]], '*');
})// onDrop 可做简单逻辑,例如暂存一些关键数据
.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;
}
// 可以遍历records取数据暂存,或者以其他方式暂存数据
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
})
}
}
}
代码逻辑走读:
- 导入模块:
- 从
@kit.ArkWeb导入webview模块,用于Webview相关操作。 - 从
@kit.ArkData导入unifiedDataChannel和uniformTypeDescriptor模块,用于处理统一数据通道和数据类型描述。
- 从
- 组件定义:
- 使用
@Entry和@Component装饰器定义一个名为DragDrop的组件。
- 使用
- 状态变量初始化:
- 初始化
controller为webview.WebviewController实例,用于控制Webview。 - 初始化
ports为一个空数组,用于存储通信端口。 - 初始化
dragData为一个空数组,用于存储拖放数据。
- 初始化
- 构建UI:
- 使用
Column布局组件构建UI。 - 在
Column中嵌入一个Web组件,设置其源文件为drag.html,并绑定controller。
- 使用
- 页面加载事件处理:
- 在
onPageEnd事件中,注册通信端口,并通过this.ports[1].onMessageEvent监听前端发送的数据。 - 向前端发送注册完成消息,完成双向端口绑定。
- 在
- 拖放事件处理:
- 在
onDrop事件中,处理拖放事件,获取拖放数据并进行处理。 - 如果拖放数据包含纯文本,则提取并打印文本内容。
- 在
H5示例:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>H5 拖拽 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>H5 拖拽 Demo</h2>
<div id="draggable" class="draggable" draggable="true">可拖拽元素</div>
<div id="droppable" class="droppable">请将方块拖到这里</div>
<script>
const draggable = document.getElementById('draggable');
const droppable = document.getElementById('droppable');
// 拖拽开始事件
draggable.addEventListener('dragstart', function (e) {
e.dataTransfer.setData('text/plain', this.id);
this.style.opacity = '0.4';
});
// 拖拽结束事件
draggable.addEventListener('dragend', function (e) {
this.style.opacity = '1';
});
// 拖入目标区域时触发
droppable.addEventListener('dragover', function (e) {
e.preventDefault(); // 必须调用,否则无法触发 drop 事件
});
// 放置事件
droppable.addEventListener('drop', function (e) {
e.preventDefault();
const data = e.dataTransfer.getData('text/plain');
// 传入ArkTS
PostMsgToArkTS(data);
const draggableEl = document.getElementById(data);
this.appendChild(draggableEl);
this.classList.add('success');
this.textContent = "放置成功!";
});
// scriptproxy端口在js侧设置
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];
}
}
});
// 通过scriptproxy方式,发送数据到ArkTS侧的实现
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>

日志打印:

如何禁用web组件拖拽能力
在未进行特殊配置的情况下,web组件默认支持拖拽功能。如果不需要拖拽功能,可以参考以下示例禁用拖拽。
禁用拖拽方式主要分为两类:
- 网页侧通过W3C CSS、JS进行拦截/禁用。
- 应用侧通过Web组件runJavaScriptExt接口注入JS进行拦截/禁用。
H5示例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>w3c通用属性/方法禁用拖拽</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>w3c通用属性/方法禁用拖拽</h2>
<!--一,通过显式设置draggable样式为false来禁用该元素的拖拽-->
<!--仅对img或div这种整个元素节点的拖拽行为生效,对节点中选中的文字不生效-->
<div>draggable设置禁用拖拽</div>
<img class="normal" draggable="false" src="./any-pic.png"><br>
<!--二,通过引用一个样式class,class中设置-webkit-user-drag为none来禁用拖拽-->
<!--生效范围同方式一-->
<div>-webkit-user-drag设置禁用拖拽</div>
<img class="undraggable" src="./any-pic.png"><br>
<!--三,通过对设置ondragstart事件监听并preventDefault来禁用拖拽-->
<!--对任意内容的拖拽行为都生效-->
<!--可通过扩大监听器监听的范围来禁用更大区域内的拖拽,比如监听在window上可实现整个web组件的拖拽禁用-->
<!--由于生效节点较靠后,拖拽事实上已进行部分,会对菜单功能产生影响-->
<div>ondragstart设置禁用拖拽</div>
<div ondragstart="dragstartHandler(event)">
<img class="normal" src="./any-pic.png">
<p>
此段文本用于验证ondragstart脚本对选中文本的禁用拖拽效果
</p>
</div>
<script>
function dragstartHandler(event) {
console.info('forbid drag when drag start');
event.preventDefault();
}
</script>
</body>
</html>

html示例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>runJavascriptExt注入js禁用拖拽</title>
</head>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.normal {
width: 100px;
height: 100px;
margin-bottom: 40px;
}
</style>
<body>
<h2>runJavascriptExt注入js禁用拖拽</h2>
<div>
<img class="normal" src="./any-pic.png">
<p>
此段文本用于验证runJavascriptExt注入js对选中文本的禁止拖拽效果
</p>
</div>
</body>
</html>

ArkTS示例:
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 {
// 使用runJavaScriptExt执行脚本添加dragstart事件监听器去禁用拖拽
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%')
}
}
代码逻辑走读:
- 导入模块:代码首先从
@kit.ArkWeb库中导入了webview模块,这是用于处理Webview控件的模块。 - 组件定义:使用
@Entry和@Component装饰器定义了一个名为Index的组件,这是应用的入口组件。 - Webview控制器初始化:在组件内部,初始化了一个
WebviewController实例,用于控制Webview的行为。 - UI构建:在
build方法中,使用Column布局容器来垂直排列UI元素。 - 按钮定义:定义了三个按钮,每个按钮都有一个点击事件处理函数:
w3cDemoPage按钮点击后,加载名为w3c-forbid.html的HTML文件。runJsDemoPage按钮点击后,加载名为runJs-forbid.html的HTML文件。runJsForbidDrag按钮点击后,尝试通过JavaScript禁用当前页面上的拖拽功能。
- Webview配置:在
Column布局中嵌入了一个Web组件,用于显示Web内容。配置了src属性为本地的w3c-forbid.html文件,并通过controller属性绑定到之前初始化的WebviewController实例。 - Webview属性设置:设置了
domStorageAccess、javaScriptAccess和fileAccess属性为true,允许Webview访问DOM存储、执行JavaScript代码和访问本地文件系统。
forbid.html`的HTML文件。runJsDemoPage按钮点击后,加载名为runJs-forbid.html的HTML文件。runJsForbidDrag按钮点击后,尝试通过JavaScript禁用当前页面上的拖拽功能。
更多推荐



所有评论(0)