aardio 文档

web.view 自动化输入指南

在 aardio 编程时利用 web.view 打开网页, 可以用 waitEle(selectorOrXPath,javascript[,timeout]), waitEle2(selectorOrXPath,javascrip[,timeout]) 等方法等待指定的 HTML 元素,并自动回调指定的 JavaScript 代码以实现自动化操作。

waitEle 在当前页面等待,waitEle2 则支持跨页面等待 请参考 web.view 自动化

自动输入示例

import win.ui;
/*DSG{{*/
var winform = win.form(text="web.view 自动化输入";right=850;bottom=650)
/*}}*/

import web.view;
var wb = web.view(winform);

wb.html = /**
<input type="text" id="demo-input">
**/

winform.show();

/*
参数 @1 可指定 CSS 选择器或 XPath,
如果首字符为 / 或 ( 符号则 识别为 XPath。

参数 @2 如果指定 JavaScipt 代码,
则在指定元素被创建以后回调指定的 JavaScipt 代码。
在被回调的 JS 中 this 绑定当前找到的元素。
*/
wb.waitEle(`//input[@id="demo-input"]`, `
    this.value = 'Hello, aardio + XPath!';
`);

win.loopMessage();

但是现在有很多使用前端框架(例如 React)编写的页面直接用上面的 JS 代码 this.value = "Hello" 可能是无效的, 下面我们将分析原因并解决问题。

1. 背景与痛点:为什么 input.value = "xxx" 失效了?

在传统的网页中,直接修改 DOM 的 value 属性即可完成输入。但现代前端框架(尤其是 React)引入了 虚拟 DOM (Virtual DOM)状态管理 (State) 的概念:

为了让框架“感知”到我们的输入,我们必须绕过拦截,并伪造真实的用户输入事件

2. 核心解决方案分类与代码实现

以下是将您收集的方法进行整理优化,并补充完善后的几种主流解决方案:

方法一:调用原型链上的原生 Setter(🌟 最推荐,适用性最广)

原理:由于 React 重写了实例对象上的 value 属性,我们可以直接找到原生 DOM 原型(HTMLInputElement.prototypeHTMLTextAreaElement.prototype)上的 value 描述符,强行调用原生的 set 方法修改底层 DOM 值,最后触发 input 事件通知框架更新状态。

/*
 * 通用输入函数(推荐使用)
 * @param {HTMLElement} element - input 或 textarea 元素
 * @param {String} value - 要输入的值
 */
function setNativeValue(element, value) {

    // 判断是 input 还是 textarea,获取对应的原型
    const prototype = Object.getPrototypeOf(element);

    // 不要用 HTMLElement.prototype,因为 HTMLElement 没有 value 属性
    const nativeInputValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;

    if (nativeInputValueSetter) {
        nativeInputValueSetter.call(element, value);

        // 必须派发 input 事件,且 bubbles 必须为 true,让 React 在顶层能监听到
        const inputEvent = new Event('input', { bubbles: true });
        element.dispatchEvent(inputEvent);
    }
}

// 使用示例:
const inputEl = document.querySelector('input[type="text"]');
setNativeValue(inputEl, "测试文本");

要点

aardio 示例

import win.ui;
/*DSG{{*/
var winform = win.form(text="自动化输入")
/*}}*/

import web.view;
var wb = web.view(winform);
winform.show();

var js = /***

//添加 JS 全局函数
window.setNativeValue = function (element, value) {

    const prototype = Object.getPrototypeOf(element);
    const nativeInputValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;

    if (nativeInputValueSetter) {
        nativeInputValueSetter.call(element, value);
        const inputEvent = new Event('input', { bubbles: true });
        element.dispatchEvent(inputEvent);
    }
}
***/

wb.preloadScript(js);

wb.html = /**
<input type="text" id="demo-input">
**/

wb.waitEle(`//input[@id="demo-input"]`, `
    setNativeValue(this, "测试文本");
`);

win.loopMessage();

方法二:使用 document.execCommand(🛠 仿真度最高)

原理:这是浏览器提供的富文本编辑命令,它不仅会修改 DOM 值,还会由浏览器底层自动触发所有相关的原生键盘和输入事件,仿真度极高。

function simulateTyping(element, text) {
    element.focus();   // 必须先聚焦
    element.select();  // 选中当前所有内容(用于覆盖旧内容)

    // 执行插入文本命令
    document.execCommand('insertText', false, text);

    // 滚动到底部(针对 textarea)
    element.scrollTop = element.scrollHeight;
}

// 使用示例:
simulateTyping(document.querySelector('input'), "hello@example.com");

要点

aardio 示例

import win.ui;
/*DSG{{*/
var winform = win.form(text="自动化输入")
/*}}*/

import web.view;
var wb = web.view(winform);
winform.show();

var js = /***

//添加 JS 全局函数
window.simulateTyping = function (element, text) {
    element.focus(); 
    element.select(); 

    document.execCommand('insertText', false, text);
    element.scrollTop = element.scrollHeight;
}
***/

wb.preloadScript(js);

wb.html = /**
<input type="text" id="demo-input">
**/

wb.waitEle(`//input[@id="demo-input"]`, `
    simulateTyping(this, "测试文本");
`);

win.loopMessage();

方法三:破解 React 的 _valueTracker(💥 针对性强,但有失效风险)

原理:利用 React 内部实现机制的漏洞。React 为了防止重复触发事件,会在 DOM 节点上挂载一个 _valueTracker 对象。我们可以骗过这个追踪器,让它认为旧值和新值不同,从而放行事件。

function reactHackInput(element, newValue) {
    const previousValue = element.value;
    element.value = newValue; // 直接修改

    // 欺骗 React 的追踪器,把追踪器的值重置为修改前的值
    if (element._valueTracker) {
        element._valueTracker.setValue(previousValue);
    }

    // 触发 input 或 change 事件 (现代 React 主要监听 input)
    element.dispatchEvent(new Event('input', { bubbles: true }));
    element.dispatchEvent(new Event('change', { bubbles: true }));
}

要点

方法四:完整模拟键盘事件流

原理:某些表单不仅监听 input,还会监听 keydownkeyup 甚至 focusblur 来触发校验逻辑(例如:输入完成后按钮才亮起)。

function simulateFullInputEvents(element, value) {

    // 1. 聚焦触发 focus 事件
    element.focus();
    element.dispatchEvent(new Event('focus', { bubbles: true }));

    // 2. 核心:修改值并触发 input
    const nativeSetter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), 'value').set;
    nativeSetter.call(element, value);
    element.dispatchEvent(new Event('input', { bubbles: true }));

    // 3. 模拟键盘事件骗过特定的校验逻辑
    element.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter', code: 'Enter' }));
    element.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, key: 'Enter', code: 'Enter' }));

    // 4. 失焦触发校验
    element.blur();
    element.dispatchEvent(new Event('blur', { bubbles: true }));
}

3. 总结与选型建议

方法名称 仿真度 兼容性/可靠性 适用场景 缺点
原生原型 Setter法 (方法一) 极高 95%的 React/Vue 页面表单。首选方案 无法对付使用了 ContentEditable 的富文本编辑器。
execCommand 法 (方法二) 极高 较高 应对极度严格的输入校验规则、富文本编辑器。 API 官方已废弃,未来某天可能在某个版本突然失效。
_valueTracker 破解 (方法三) 较低 仅限特定的旧版 React 页面。 强依赖框架底层源码,版本升级即作废。
完整事件流模拟 (方法四) 针对输入后提交按钮不亮、失去焦点报红的严格表单。 代码繁琐,需要根据业务逻辑猜测缺了哪个事件。

4. 其他建议

  1. 异步渲染延迟:如果需要连续自动化输入多个框,注意前端框架重新 Render 需要时间。可以在两次输入之间调用 aardio 函数 thread.delay( milliseconds ) 或者 aardio 函数 setTimeout(func,milliseconds) 停顿 50~100ms。

  2. 使用 CDP 命令自动偎入

    使用 web.view 也可以直接调用的 CDP (Chrome DevTools Protocol) 命令模拟硬件级按键输入。 请参考范例 CDP 自动化

Markdown 格式