qt - 为 Qml WebEngineView 中的 Dom 更改创建侦听器(Mutation Observer)
问题描述
我有一个包含这个的 html 页面:
<div id="ajaxloader" style="display: none;">
<div>
hello world
</div>
</div>
我可以读取它的显示值,document.getElementById('ajaxloader').style.display
我想在它的显示变为block
或时得到通知none
。
目前我正在使用一个使用计时器的愚蠢解决方案:
Timer{
running : true
repeat : true
interval: 500
onTriggered: {
var js = "document.getElementById('ajaxloader').style.display"
webView.runJavaScript(js,x=>{
if(x=="block"){
// we catch the change but its really bad solution
}
})
}
}
我正在寻找一种方法来捕捉 DOM 中的这种变化,有一些东西叫做Mutation Observer
但我不知道如何将它实现到 QML 的 WebEngineView 中。
我所需要的只是一种方法来捕捉 WebEngineView 中发生的变化,或者捕捉引擎中正在进行的 CRUD 的事件,或者比这个计时器更好!
更新:
例如,我们有一个访问 google.com 的网络引擎,加载完成后它会将搜索文本更改为“hello world”,我们希望在不使用计时器的情况下捕捉该更改,在真实网站中,这种更改实际上是通过 CRUD 函数发生的(ajax 请求) 或其他方式:
WebChannel{
id:_channel
}
WebEngineView{
id:webEngine
height: parent.height
width: parent.width
webChannel: _channel
url : "www.google.com"
onNewViewRequested: {
request.openIn(webEngine)
}
objectName: "webView"
profile.httpCacheType: WebEngineProfile.NoCache
onLoadingChanged: {
if(loadRequest.status == WebEngineView.LoadSucceededStatus){
var js = "document.querySelector('input[role=combobox]').value = 'hello world'"
webEngine.runJavaScript(js,y=>{})
}
}
}
不要忘记在 c++ 中初始化引擎,如果没有这个,它将无法工作:QtWebEngine::initialize();
和其他导入的东西加上你现在需要将它添加到 pro 文件
QT += webengine webengine-private webenginecore webenginecore-private
,如果我使用我想把它放在一边的计时器方法它应该是这样的:
Timer{
running : true
repeat : true
interval : 500
onTriggered:{
var js = "document.querySelector('input[role=combobox]').value"
webEngine.runJavaScript(js,y=>{console.log(y)});
// now i have to check y to see if its equals to hello world or what ever which is really bad idea to use a timer here
}
}
例如,您可以像这样观察 google 输入的变化:
var targetNode = document.querySelector('input[role=combobox]')
targetNode.oninput = function(e){this.setAttribute('value',targetNode.value)}
var config = { attributes: true, childList: true, subtree: true };
var callback = function(mutationsList, observer) {
for(var mutation of mutationsList) {
if (mutation.type == 'childList') {
console.log('A child node has been added or removed.');
}
else if (mutation.type == 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
};
// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
解决方案
策略是在页面加载时加载 qwebchannel.js,然后注入另一个脚本,使用 Qt WebChannel 与导出的对象建立连接
无需在 C++ 中创建 QObject,您可以使用 QtObject。
主文件
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtWebEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QtWebEngine::initialize();
QString JsWebChannel;
QFile file(":///qtwebchannel/qwebchannel.js");
if(file.open(QIODevice::ReadOnly))
JsWebChannel = file.readAll();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("JsWebChannel", JsWebChannel);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtWebChannel 1.13
import QtWebEngine 1.1
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
QtObject{
id: listener
WebChannel.id: "listener"
property string text: ""
onTextChanged: console.log(text)
property string script: "
var listener;
new QWebChannel(qt.webChannelTransport, function (channel) {
listener = channel.objects.listener;
var targetNode = document.querySelector('input[role=combobox]')
targetNode.oninput = function(e){this.setAttribute('value',targetNode.value)}
var config = { attributes: true, childList: true, subtree: true };
var callback = function(mutationsList, observer) {
for(var mutation of mutationsList) {
if (mutation.type == 'childList') {
console.log('A child node has been added or removed.');
}
else if (mutation.type == 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
listener.text = targetNode.value; // update qproperty
}
}
};
// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
});
"
}
WebChannel{
id: channel
registeredObjects: [listener]
function inject(){
webEngine.runJavaScript(JsWebChannel);
webEngine.runJavaScript(listener.script);
}
}
WebEngineView {
id:webEngine
url : "https://www.google.com/"
profile.httpCacheType: WebEngineProfile.NoCache
webChannel: channel
anchors.fill: parent
onLoadingChanged: {
if(loadRequest.status == WebEngineView.LoadSucceededStatus){
console.log("Page has successfully loaded")
channel.inject()
}
}
}
}
推荐阅读
- python - 使用唯一的数据框,但行是一个列表,而不是“一维数组”
- android - 无法启动 Observatory HTTP 服务器 Flutter/Dart
- javascript - 控制台打印出函数,而不是值
- anylogic - 在anylogic中从double转换为int
- c++ - 您可以在开关中使用 OR 吗?
- django - 传单绘制不适用于 Django - 形状永远不会完成
- ruby - 如何绘制一个大小为 n 的方形网格到控制台?
- button - Python tkinter - 删除带有关联按钮的单个标签
- sql-server - SSIS 以 varchar(不是 nvarchar)格式将访问文本导入 SQL Server
- r - 从元素序列中减去