我有一个包含以下内容的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);
答案 0 :(得分:3)
该策略是在加载页面时加载qwebchannel.js,然后注入另一个脚本,该脚本使用Qt WebChannel与导出的对象建立连接
无需在C ++中创建QObject,可以使用QtObject。
main.cpp
#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()
}
}
}
}
答案 1 :(得分:2)
如果您希望javascript调用C ++函数,则需要使用Qt WebChannel
模块。
它将允许您将任何QObject
派生对象公开给javascript,从而允许您从javascript调用公开对象的任何slot函数。
在这种情况下,您可以在JavaScript中使用MutationObserver
并将C ++函数用作回调。
这是一个基本示例:
// C++
class MyObject: public QObject {
Q_OBJECT
public slots:
void mySlot();
}
int main() {
...
// Create the channel and expose the object.
// This part can also be done in QML
QWebChannel channel;
MyObject obj;
channel.registerObject(QStringLiteral("myobject"), &obj);
...
}
// js
myobject = channel.objects.myobject;
observer = new MutationObserver(() => { myobject.mySlot(); }); // Will call MyObject::mySlot() in C++
observer.observe(...);
如果需要更多详细信息,可以查看以下示例:https://doc.qt.io/qt-5/qtwebchannel-standalone-example.html
Qt文档中还有其他示例,但请记住,js部分Qt WebChannel可在QWebEngine或任何Web浏览器中使用。因此,您会发现一些示例可能与您需要做的事情有所不同。
此外,您不能在跨C ++ / js边界的函数调用中传递任何参数类型